Skip to main content

Opcode

Enum Opcode 

Source
pub enum Opcode {
Show 108 variants IConst, FConst, Splat, GlobalAddr, BlockAddr, Add, Sub, Mul, SDiv, UDiv, SRem, URem, And, Or, Xor, Shl, LShr, AShr, FAdd, FSub, FMul, FDiv, FRem, FNeg, Fma, ICmp, FCmp, Select, Trunc, SExt, ZExt, FPTrunc, FPExt, FPToSI, FPToUI, SIToFP, UIToFP, PtrToInt, IntToPtr, Bitcast, MemEntry, Alloca, Load, Store, PtrAdd, Memcpy, Memmove, Memset, AtomicLoad, AtomicStore, AtomicRmw, Cmpxchg, Fence, CapOf, CapLoad, CapStore, CapNull, CapNarrow, CapRecover, CheckBounds, CheckLive, CheckType, CheckInit, CheckDeriv, CheckRace, MetaBegin, MetaEnd, MetaType, MetaInit, MetaTransfer, SafeRegionBegin, SafeRegionEnd, Jump, BrIf, Switch, IndirectBr, Return, Unreachable, Call, CallIndirect, TailCall, Ctlz, Cttz, Ctpop, Bswap, Bitreverse, SAddOverflow, UAddOverflow, SSubOverflow, USubOverflow, SMulOverflow, UMulOverflow, Expect, UnreachableHint, Prefetch, FrameAddress, ReturnAddress, VaStart, VaArg, VaObject, VaEnd, VaCopy, StackSave, StackRestore, SetjmpMarker, LongjmpMarker, TargetIntrinsic, InlineAsm,
}
Expand description

One instruction of the IR.

The names are the textual form exactly, so Opcode::name and Opcode::from_name are what the printer and the parser use, and neither carries a table of its own that could drift from this one.

The enum is not non_exhaustive, deliberately. The set is closed, so a pass that matches on every opcode should stop compiling when one is added rather than fall into a wildcard arm that quietly does the wrong thing.

Variants§

§

IConst

An integer constant, iconst.i32 7.

§

FConst

A floating point constant, fconst.f64 0x1.8p+1.

§

Splat

A vector constant with every lane the same, splat.i8x16 0.

§

GlobalAddr

The address of a global or a function, global_addr @counter.

§

BlockAddr

The address of a block in this function, block_addr block3.

The one instruction that names a block without being a branch, which is what GNU’s &&label is. Where it goes is Opcode::IndirectBr, and the two are only useful together: an address on its own is a number that nothing can do anything with.

§

Add

Integer addition.

§

Sub

Integer subtraction.

§

Mul

Integer multiplication.

§

SDiv

Signed division.

§

UDiv

Unsigned division.

§

SRem

Signed remainder, with the sign of the dividend.

§

URem

Unsigned remainder.

§

And

Bitwise and.

§

Or

Bitwise or.

§

Xor

Bitwise exclusive or.

§

Shl

Shift left.

§

LShr

Logical shift right, shifting in zeroes.

§

AShr

Arithmetic shift right, shifting in the sign bit.

§

FAdd

Floating point addition.

§

FSub

Floating point subtraction.

§

FMul

Floating point multiplication.

§

FDiv

Floating point division.

§

FRem

Floating point remainder.

§

FNeg

Floating point negation, which flips the sign bit and is not 0 - x.

§

Fma

Fused multiply-add, rounded once.

§

ICmp

Integer comparison, producing i1 or a vector of i1.

§

FCmp

Floating point comparison, producing i1 or a vector of i1.

§

Select

One of two values, chosen by a bit. select c, a, b is a when c is one.

This is what control flow becomes when it stops being control flow. spec/optimizer/22-phiopt-and-if-conversion.md section 22.2 makes it the lowering target for a diamond whose two arms compute a value, and the reason it is an opcode rather than a pattern is that it is the form the rule set is written against: select(c, a, a) -> a and select(c, 1, 0) -> zext(c) are ordinary rules once the shape has a name.

Both arms are evaluated, which is the whole point and also the whole danger. Whatever produces one of these owes the argument that evaluating the arm that is not chosen is harmless, and section 22.6 is the list of ways that argument goes wrong.

§

Trunc

Narrows an integer, discarding the high bits.

§

SExt

Widens an integer, copying the sign bit.

§

ZExt

Widens an integer, filling with zeroes.

§

FPTrunc

Narrows a floating point value.

§

FPExt

Widens a floating point value.

§

FPToSI

Floating point to signed integer.

§

FPToUI

Floating point to unsigned integer.

§

SIToFP

Signed integer to floating point.

§

UIToFP

Unsigned integer to floating point.

§

PtrToInt

An address to an integer of the same width.

§

IntToPtr

An integer to an address.

§

Bitcast

A reinterpretation of the same bits at the same width.

§

MemEntry

Memory as the function found it, which is where a memory SSA chain starts.

It produces one mem and takes nothing, and it belongs at the top of the entry block. GCC calls the same thing the default definition of .MEM and LLVM calls it liveOnEntry. It exists as an instruction rather than as a parameter of the entry block because the entry block’s parameters are the function’s parameters and the verifier checks them against the signature, and memory is not an argument anybody passed.

§

Alloca

A stack slot. In the entry block, or marked dynamic for a variable length array.

§

Load

A read.

§

Store

A write, producing no value.

§

PtrAdd

Address arithmetic: an address and a byte offset.

§

Memcpy

A copy of a known size between addresses that do not overlap.

§

Memmove

A copy of a known size between addresses that may overlap.

§

Memset

A fill of a known size with one byte.

§

AtomicLoad

An atomic read.

§

AtomicStore

An atomic write.

§

AtomicRmw

An atomic read-modify-write, carrying which operation in RmwOp.

§

Cmpxchg

An atomic compare and exchange, producing the old value and whether it succeeded.

§

Fence

A memory barrier.

§

CapOf

The capability of a pointer value, taken from the pointer’s provenance.

§

CapLoad

The capability in the auxiliary slot beside a stored pointer, read back.

A pointer written to memory and read again has to bring its capability with it, and where the capability lives is document 05’s question rather than this one’s. What this says is that a capability comes back from an address, which is enough for every pass above.

§

CapStore

The other half of Opcode::CapLoad, writing one into the slot beside a pointer.

§

CapNull

The capability that permits nothing, which is what a null pointer has.

§

CapNarrow

A capability narrowed to a sub-object of what it covered.

Only under -fsafety-subobject. Narrowing is what catches an overflow from one member of a struct into the next, and it is separate because C code that walks off the end of a member on purpose exists and a project has to be able to say so.

§

CapRecover

The capability for an address that arrived from outside, recovered from the planes.

§

CheckBounds

An access is within its capability’s bounds, aligned, and permitted.

The size and the alignment are the access’s, and they are in the memory payload rather than in operands because they are what the front end knew and not what the program computed.

§

CheckLive

The capability’s provenance is still live.

§

CheckType

The access agrees with the type plane, which is the effective type rule of C 6.5.

§

CheckInit

The bytes the access reads have been written.

§

CheckDeriv

A pointer derived from another stays inside the capability the first one had.

Three operands, because the answer is about the new pointer and the question is about the old one’s capability.

§

CheckRace

The metadata this access is about to consult has not been changed under it.

§

MetaBegin

A storage instance begins here, over a range, with a class.

Judgement J4. This is the alloca for an automatic instance and the allocator’s report for an allocated one, and the range is a pointer and a length in registers rather than a payload, because the length of a variable length array is not known when the instruction is written down.

§

MetaEnd

A storage instance ends here, which is judgement J5.

Every capability for it fails from this point on and keeps failing after the address is handed out again, which is what makes the check a use after free check rather than a use after reallocation one.

§

MetaType

The effective type of a range is now this one.

§

MetaInit

The bytes of a range are now initialized.

§

MetaTransfer

A range leaves the monitor’s authority, or comes back, which is judgement J7.

§

SafeRegionBegin

A declared exemption starts here, with the reason it was declared.

Not an optimization hint. Everything between this and its safe_region_end is code the monitor is told not to judge, so the reason it carries is a trust set entry, and spec/safe-memory/10-boundaries.md section 10.2 counts them per build precisely so that a reviewer can read what a binary’s guarantee rests on.

§

SafeRegionEnd

The end of the region the last safe_region_begin opened.

§

Jump

An unconditional branch, jump block1(%a, %b).

§

BrIf

A two-way branch on an i1.

§

Switch

A multi-way branch on an integer, with a default.

§

IndirectBr

A branch to an address, indirect_br %0, block1, block2.

The targets are every block control can arrive at, which is what makes the edges of a computed goto ordinary edges: nothing else in the compiler has to know that the address decides which one it is. A target that is not listed is a branch that does not happen, so a frontend that leaves one out has made a promise on the program’s behalf.

§

Return

A return, with the values the signature says.

§

Unreachable

A place control cannot reach, which the frontend emits after a noreturn call.

§

Call

A call to a named function.

§

CallIndirect

A call through an address, carrying the signature it is called with.

§

TailCall

A call in tail position that reuses the frame, which is a terminator.

§

Ctlz

Count leading zeroes.

§

Cttz

Count trailing zeroes.

§

Ctpop

Count set bits.

§

Bswap

Reverse the bytes.

§

Bitreverse

Reverse the bits.

§

SAddOverflow

Signed addition, producing the result and whether it overflowed.

§

UAddOverflow

Unsigned addition, producing the result and whether it overflowed.

§

SSubOverflow

Signed subtraction, producing the result and whether it overflowed.

§

USubOverflow

Unsigned subtraction, producing the result and whether it overflowed.

§

SMulOverflow

Signed multiplication, producing the result and whether it overflowed.

§

UMulOverflow

Unsigned multiplication, producing the result and whether it overflowed.

§

Expect

__builtin_expect, which is the value with a hint attached.

§

UnreachableHint

__builtin_unreachable as a hint on a path, distinct from the terminator.

§

Prefetch

__builtin_prefetch.

§

FrameAddress

__builtin_frame_address.

§

ReturnAddress

__builtin_return_address.

§

VaStart

The start of a variable argument list.

§

VaArg

One argument off a variable argument list, which moves the list on as it reads it. Two of these on one list are two arguments and never one argument read twice, so whatever decides which instructions may be folded together has to leave these alone.

§

VaObject

One argument off a variable argument list, when that argument is an object rather than a value, which is what a struct or a union read out of one is.

It answers the address of the object rather than the object, because an aggregate is not a value and there is nothing for one result to be. Where the object arrives in registers there is no address until something makes one, so what this asks of a target is a place to put the registers and the address of that place, which is the copy every psABI’s own description of the algorithm makes. It moves the list on for the reason Opcode::VaArg does.

§

VaEnd

The end of a variable argument list.

§

VaCopy

A copy of a variable argument list.

§

StackSave

The stack pointer, saved before a variable length array.

§

StackRestore

The stack pointer, restored after one.

§

SetjmpMarker

The marker a setjmp leaves, which pins everything live across it.

§

LongjmpMarker

The marker a longjmp leaves.

§

TargetIntrinsic

A target-specific intrinsic, named rather than enumerated, for the vector builtins.

§

InlineAsm

Inline assembly. A terminator when it has labels, which is asm goto.

Implementations§

Source§

impl Opcode

Source

pub const fn name(self) -> &'static str

The textual form, which is also what the parser reads.

Source

pub fn all() -> impl Iterator<Item = Self>

Every opcode, in the order they are declared.

The parser walks this rather than holding a second table, because a second table is a table that can disagree with the first one.

Source

pub fn from_name(name: &str) -> Option<Self>

The opcode with that name, if there is one.

Source

pub const fn is_terminator(self) -> bool

Whether this ends a block.

Opcode::InlineAsm is not here and is the one instruction whose answer depends on the instruction rather than on the opcode: asm goto has successors and everything else does not. Ask the instruction, not the opcode.

Source

pub const fn is_commutative(self) -> bool

Whether the operands can be swapped without changing the result.

The floating point cases are commutative even under the strictest rounding, because swapping the operands of an addition does not change which of them is a NaN, and the sign of a NaN result is not something we promise anything about either way.

Source

pub const fn has_effects(self) -> bool

Whether this reads or writes memory, or has an effect the optimizer has to preserve.

An instruction that answers no can be deleted when nothing uses its result, moved across a call, and merged with another one computing the same thing. Everything else has to be argued about individually, so the conservative answer is the true one here and the list of exceptions is the part that is checked.

Source

pub const fn touches_memory(self) -> bool

Whether an instruction with this opcode touches memory.

This is what decides whether it takes a memory operand once memory SSA is built, per document 09 of spec/optimizer. It is written as the exceptions to touching memory rather than as a list of what does, for the reason document 08.6 gives about the escape analysis: an opcode added later has to end up on the conservative side by default, and a list of what touches memory would silently leave a new one out.

mem_entry answers no. It produces memory rather than touching it, which is the whole of what it is for.

Source

pub const fn writes_memory(self) -> bool

Whether an instruction with this opcode writes memory, and so produces a new version of it rather than only reading the version it was given.

Everything that touches memory writes it except the ones that plainly do not. A fence writes nothing and is still a write here, because document 09.5 says an atomic or a barrier is a definition nothing walks past, and giving it one is how that is expressed in a representation whose only ordering is the memory chain.

The checks read the planes and change nothing, which spec/safe-memory/06-instrumentation.md section 6.2.4 states as the word readonly. A check that trapped is a program that stopped and there is no version of memory after it for anything to observe, so the trap costs nothing here. What it does cost is that a check may not be moved across a plane write, and that is the memory chain saying so rather than this.

Source

pub const fn results(self) -> Option<u8>

How many values this produces, for the opcodes where the count is fixed.

None means the count comes from somewhere else: a call takes it from its signature, and inline assembly takes it from its output constraints. A tail call is not one of them, because whatever it returns goes straight out of the function and there is no instruction after it to use anything.

Source

pub const fn makes_capability(self) -> bool

Whether an instruction with this opcode produces a capability.

Five of the six cap instructions, cap_store being the one that consumes one instead. The reason this is a question about the opcode rather than about the result type is that the verifier asks it the other way round: it walks the results looking for a cap and needs to know whether the instruction under it was entitled to make one.

Source

pub const fn extra_kind(self) -> ExtraKind

Which payload an instruction with this opcode carries.

The printer reads the payload it finds and does not need this. The parser has only the opcode when it reaches the operands, so this is where the two of them agree on what comes after them. An instruction carrying a payload of some other kind prints as text the parser cannot read back, which is why the verifier checks it against Extra::kind rather than leaving it to be found later.

Trait Implementations§

Source§

impl Clone for Opcode

Source§

fn clone(&self) -> Opcode

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Copy for Opcode

Source§

impl Debug for Opcode

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for Opcode

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Eq for Opcode

Source§

impl Hash for Opcode

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Ord for Opcode

Source§

fn cmp(&self, other: &Opcode) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 (const: unstable) · Source§

fn max(self, other: Self) -> Self
where Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 (const: unstable) · Source§

fn min(self, other: Self) -> Self
where Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 (const: unstable) · Source§

fn clamp(self, min: Self, max: Self) -> Self
where Self: Sized,

Restrict a value to a certain interval. Read more
Source§

fn clamp_to<R>(self, range: R) -> Self
where Self: Sized, R: ClampBounds<Self>,

🔬This is a nightly-only experimental API. (clamp_to)
Restrict a value to a certain range. Read more
Source§

impl PartialEq for Opcode

Source§

fn eq(&self, other: &Opcode) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl PartialOrd for Opcode

Source§

fn partial_cmp(&self, other: &Opcode) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 (const: unstable) · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 (const: unstable) · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the <= operator. Read more
1.0.0 (const: unstable) · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > operator. Read more
1.0.0 (const: unstable) · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by the >= operator. Read more
Source§

impl StructuralPartialEq for Opcode

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.