cir.alloca
(cir::AllocaOp)Defines a scope-local variable
Syntax:
operation ::= `cir.alloca` $allocaType `,` `cir.ptr` type($addr) `,`
`[` $name
(`,` `init` $init^)?
`]`
(`ast` $ast^)? attr-dict
The cir.alloca
operation defines a scope-local variable.
The presence init
attribute indicates that the local variable represented by this alloca was originally initialized in C/C++ source code. In such cases, the first use contains the initialization (a cir.store, a cir.call to a ctor, etc).
The result type is a pointer to the input's type.
Example:
// int count = 3;
%0 = cir.alloca i32, !cir.ptr<i32>, ["count", init] {alignment = 4 : i64}
// int *ptr;
%1 = cir.alloca !cir.ptr<i32>, cir.ptr <!cir.ptr<i32>>, ["ptr"] {alignment = 8 : i64}
...
Attribute | MLIR Type | Description |
---|---|---|
allocaType | ::mlir::TypeAttr | any type attribute |
name | ::mlir::StringAttr | string attribute |
init | ::mlir::UnitAttr | unit attribute |
alignment | ::mlir::IntegerAttr | 64-bit signless integer attribute whose minimum value is 0 |
ast | ::mlir::cir::ASTVarDeclAttr | Wraps a ‘const clang::VarDecl *' AST node. |
Result | Description |
---|---|
addr | CIR pointer type |
cir.await
(cir::AwaitOp)Wraps C++ co_await implicit logic
Syntax:
operation ::= `cir.await` `(` $kind `,`
`ready` `:` $ready `,`
`suspend` `:` $suspend `,`
`resume` `:` $resume `,`
`)`
attr-dict
The under the hood effect of using C++ co_await expr
roughly translates to:
// co_await expr;
auto &&x = CommonExpr();
if (!x.await_ready()) {
...
x.await_suspend(...);
...
}
x.await_resume();
cir.await
represents this logic by using 3 regions:
Breaking this up in regions allow individual scrutiny of conditions which might lead to folding some of them out. Lowerings coming out of CIR, e.g. LLVM, should use the suspend
region to track more lower level codegen (e.g. intrinsic emission for coro.save/coro.suspend).
There are also 3 flavors of cir.await
available:
init
: compiler generated initial suspend via implicit co_await
.user
: also known as normal, representing user written co_await's.final
: compiler generated final suspend via implicit co_await
.From the C++ snippet we get:
cir.scope {
... // auto &&x = CommonExpr();
cir.await(user, ready : {
... // x.await_ready()
}, suspend : {
... // x.await_suspend()
}, resume : {
... // x.await_resume()
})
}
Note that resulution of the common expression is assumed to happen as part of the enclosing await scope.
Traits: NoRegionArguments, RecursivelySpeculatableImplTrait
Interfaces: ConditionallySpeculatable, RegionBranchOpInterface
Attribute | MLIR Type | Description |
---|---|---|
kind | ::mlir::cir::AwaitKindAttr | await kind |
cir.base_class_addr
(cir::BaseClassAddrOp)Get the base class address for a class/struct
Syntax:
operation ::= `cir.base_class_addr` `(`
$derived_addr `:` `cir.ptr` type($derived_addr)
`)` `->` `cir.ptr` type($base_addr) attr-dict
The cir.base_class_addr
operaration gets the address of a particular base class given a derived class pointer.
Example:
TBD
Operand | Description |
---|---|
derived_addr | CIR pointer type |
Result | Description |
---|---|
base_addr | CIR pointer type |
cir.binop
(cir::BinOp)Binary operations (arith and logic)
Syntax:
operation ::= `cir.binop` `(` $kind `,` $lhs `,` $rhs `)` `:` type($lhs) attr-dict
cir.binop performs the binary operation according to the specified opcode kind: [mul, div, rem, add, sub, and, xor, or].
It requires two input operands and has one result, all types should be the same.
%7 = cir.binop(add, %1, %2) : !s32i
%7 = cir.binop(mul, %1, %2) : !u8i
Traits: AlwaysSpeculatableImplTrait, SameOperandsAndResultType, SameTypeOperands
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
Attribute | MLIR Type | Description |
---|---|---|
kind | ::mlir::cir::BinOpKindAttr | binary operation (arith and logic) kind |
Operand | Description |
---|---|
lhs | any type |
rhs | any type |
Result | Description |
---|---|
result | any type |
cir.brcond
(cir::BrCondOp)Conditional branch
Syntax:
operation ::= `cir.brcond` $cond
$destTrue (`(` $destOperandsTrue^ `:` type($destOperandsTrue) `)`)?
`,`
$destFalse (`(` $destOperandsFalse^ `:` type($destOperandsFalse) `)`)?
attr-dict
The cir.brcond %cond, ^bb0, ^bb1
branches to ‘bb0' block in case %cond (which must be a !cir.bool type) evaluates to true, otherwise it branches to ‘bb1'.
Example:
...
cir.brcond %a, ^bb3, ^bb4
^bb3:
cir.return
^bb4:
cir.yield
Traits: AlwaysSpeculatableImplTrait, SameVariadicOperandSize, Terminator
Interfaces: BranchOpInterface, ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
Operand | Description |
---|---|
cond | CIR bool type |
destOperandsTrue | any type |
destOperandsFalse | any type |
Successor | Description |
---|---|
destTrue | any successor |
destFalse | any successor |
cir.br
(cir::BrOp)Unconditional branch
Syntax:
operation ::= `cir.br` $dest (`(` $destOperands^ `:` type($destOperands) `)`)? attr-dict
The cir.br
branches unconditionally to a block. Used to represent C/C++ goto's and general block branching.
Example:
...
cir.br ^bb3
^bb3:
cir.return
Traits: AlwaysSpeculatableImplTrait, Terminator
Interfaces: BranchOpInterface, ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
Operand | Description |
---|---|
destOperands | any type |
Successor | Description |
---|---|
dest | any successor |
cir.call
(cir::CallOp)Call operation
The call
operation represents a direct call to a function that is within the same symbol scope as the call. The operands and result types of the call must match the specified function type. The callee is encoded as a symbol reference attribute named "callee".
To walk the operands for this operation, use getNumArgOperands()
, getArgOperand()
, getArgOperands()
, arg_operand_begin()
and arg_operand_begin()
. Avoid using getNumOperands()
, getOperand()
, operand_begin()
, etc, direclty - might be misleading given on indirect calls the callee is encoded in the first operation operand. ``
Example:
// Direct call
%2 = cir.call @my_add(%0, %1) : (f32, f32) -> f32
...
// Indirect call
%20 = cir.call %18(%17)
Interfaces: CallOpInterface, SymbolUserOpInterface
Attribute | MLIR Type | Description |
---|---|---|
callee | ::mlir::FlatSymbolRefAttr | flat symbol reference attribute |
Operand | Description |
---|---|
operands | any type |
Result | Description |
---|---|
«unnamed» | any type |
cir.cast
(cir::CastOp)Conversion between values of different types
Syntax:
operation ::= `cir.cast` `(` $kind `,` $src `:` type($src) `)`
`,` type($result) attr-dict
Apply C/C++ usual conversions rules between values. Currently supported kinds:
int_to_bool
ptr_to_bool
array_to_ptrdecay
integral
bitcast
floating
float_to_int
This is effectively a subset of the rules from llvm-project/clang/include/clang/AST/OperationKinds.def
; but note that some of the conversions aren't implemented in terms of cir.cast
, lvalue-to-rvalue
for instance is modeled as a regular cir.load
.
%4 = cir.cast (int_to_bool, %3 : i32), !cir.bool
...
%x = cir.cast(array_to_ptrdecay, %0 : !cir.ptr<!cir.array<i32 x 10>>), !cir.ptr<i32>
Traits: AlwaysSpeculatableImplTrait
Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
Attribute | MLIR Type | Description |
---|---|---|
kind | ::mlir::cir::CastKindAttr | cast kind |
Operand | Description |
---|---|
src | any type |
Result | Description |
---|---|
result | any type |
cir.cmp
(cir::CmpOp)Compare values two values and produce a boolean result
Syntax:
operation ::= `cir.cmp` `(` $kind `,` $lhs `,` $rhs `)` `:` type($lhs) `,` type($result) attr-dict
cir.cmp
compares two input operands of the same type and produces a cir.bool
result. The kinds of comparison available are: [lt,gt,ge,eq,ne]
%7 = cir.cmp(gt, %1, %2) : i32, !cir.bool
Traits: AlwaysSpeculatableImplTrait, SameTypeOperands
Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
Attribute | MLIR Type | Description |
---|---|---|
kind | ::mlir::cir::CmpOpKindAttr | compare operation kind |
Operand | Description |
---|---|
lhs | any type |
rhs | any type |
Result | Description |
---|---|
result | any type |
cir.const
(cir::ConstantOp)Defines a CIR constant
Syntax:
operation ::= `cir.const` `(` custom<ConstantValue>($value) `)` attr-dict `:` type($res)
The cir.const
operation turns a literal into an SSA value. The data is attached to the operation as an attribute.
%0 = cir.const(42 : i32) : i32
%1 = cir.const(4.2 : f32) : f32
%2 = cir.const(nullptr : !cir.ptr<i32>) : !cir.ptr<i32>
Traits: AlwaysSpeculatableImplTrait, ConstantLike
Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
Attribute | MLIR Type | Description |
---|---|---|
value | ::mlir::TypedAttr | TypedAttr instance |
Result | Description |
---|---|
res | any type |
cir.func
(cir::FuncOp)Declare or define a function
Similar to mlir::FuncOp
built-in:
Operations within the function cannot implicitly capture values defined outside of the function, i.e. Functions are
IsolatedFromAbove
. All external references must use function arguments or attributes that establish a symbolic connection (e.g. symbols referenced by name via a string attribute like SymbolRefAttr). An external function declaration (used when referring to a function declared in some other module) has no body. While the MLIR textual form provides a nice inline syntax for function arguments, they are internally represented as "block arguments" to the first block in the region.Only dialect attribute names may be specified in the attribute dictionaries for function arguments, results, or the function itself.
The function linkage information is specified by linkage
, as defined by GlobalLinkageKind
attribute.
A compiler builtin function must be marked as builtin
for further processing when lowering from CIR.
The coroutine
keyword is used to mark coroutine function, which requires at least one cir.await
instruction to be used in its body.
The lambda
translates to a C++ operator()
that implements a lambda, this allow callsites to make certain assumptions about the real function nature when writing analysis. The verifier should, but do act on this keyword yet.
The no_proto
keyword is used to identify functions that were declared without a prototype and, consequently, may contain calls with invalid arguments and undefined behavior.
The extra_attrs
, which is an aggregate of function-specific attributes is required and mandatory to describle additional attributes that are not listed above. Though mandatory, the prining of the attribute can be omitted if it is empty.
Example:
// External function definitions.
cir.func @abort()
// A function with internal linkage.
cir.func internal @count(%x: i64) -> (i64)
return %x : i64
}
// Linkage information
cir.func linkonce_odr @some_method(...)
// Builtin function
cir.func builtin @__builtin_coro_end(!cir.ptr<i8>, !cir.bool) -> !cir.bool
// Coroutine
cir.func coroutine @_Z10silly_taskv() -> !CoroTask {
...
cir.await(...)
...
}
Traits: AutomaticAllocationScope, IsolatedFromAbove
Interfaces: CallableOpInterface, FunctionOpInterface, Symbol
Attribute | MLIR Type | Description |
---|---|---|
sym_name | ::mlir::StringAttr | string attribute |
function_type | ::mlir::TypeAttr | type attribute of CIR function type |
builtin | ::mlir::UnitAttr | unit attribute |
coroutine | ::mlir::UnitAttr | unit attribute |
lambda | ::mlir::UnitAttr | unit attribute |
no_proto | ::mlir::UnitAttr | unit attribute |
linkage | ::mlir::cir::GlobalLinkageKindAttr | Linkage type/kind |
extra_attrs | ::mlir::cir::ExtraFuncAttributesAttr | Represents aggregated attributes for a function |
sym_visibility | ::mlir::StringAttr | string attribute |
arg_attrs | ::mlir::ArrayAttr | Array of dictionary attributes |
res_attrs | ::mlir::ArrayAttr | Array of dictionary attributes |
aliasee | ::mlir::FlatSymbolRefAttr | flat symbol reference attribute |
ast | ::mlir::cir::ASTFunctionDeclAttr | Wraps a ‘const clang::FunctionDecl *' AST node. |
cir.get_global
(cir::GetGlobalOp)Get the address of a global variable
Syntax:
operation ::= `cir.get_global` $name `:` `cir.ptr` type($addr) attr-dict
The cir.get_global
operation retrieves the address pointing to a named global variable. If the global variable is marked constant, writing to the resulting address (such as through a cir.store
operation) is undefined. Resulting type must always be a !cir.ptr<...>
type.
Example:
%x = cir.get_global @foo : !cir.ptr<i32>
Traits: AlwaysSpeculatableImplTrait
Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface), SymbolUserOpInterface
Effects: MemoryEffects::Effect{}
Attribute | MLIR Type | Description |
---|---|---|
name | ::mlir::FlatSymbolRefAttr | flat symbol reference attribute |
Result | Description |
---|---|
addr | CIR pointer type |
cir.global
(cir::GlobalOp)Declares or defines a global variable
Syntax:
operation ::= `cir.global` ($sym_visibility^)?
(`constant` $constant^)?
$linkage
$sym_name
custom<GlobalOpTypeAndInitialValue>($sym_type, $initial_value, $ctorRegion)
attr-dict
The cir.global
operation declares or defines a named global variable.
The backing memory for the variable is allocated statically and is described by the type of the variable.
The operation is a declaration if no inital_value
is specified, else it is a definition.
The global variable can also be marked constant using the constant
unit attribute. Writing to such constant global variables is undefined.
The linkage
tracks C/C++ linkage types, currently very similar to LLVM's. Symbol visibility in sym_visibility
is defined in terms of MLIR's visibility and verified to be in accordance to linkage
.
Example:
// Public and constant variable with initial value.
cir.global public constant @c : i32 = 4;
Traits: NoRegionArguments
Interfaces: RegionBranchOpInterface, Symbol
Attribute | MLIR Type | Description |
---|---|---|
sym_name | ::mlir::StringAttr | string attribute |
sym_visibility | ::mlir::StringAttr | string attribute |
sym_type | ::mlir::TypeAttr | any type attribute |
linkage | ::mlir::cir::GlobalLinkageKindAttr | Linkage type/kind |
initial_value | ::mlir::Attribute | any attribute |
constant | ::mlir::UnitAttr | unit attribute |
alignment | ::mlir::IntegerAttr | 64-bit signless integer attribute |
cir.if
(cir::IfOp)The if-then-else operation
The cir.if
operation represents an if-then-else construct for conditionally executing two regions of code. The operand is a cir.bool
type.
Examples:
cir.if %b {
...
} else {
...
}
cir.if %c {
...
}
cir.if %c {
...
cir.br ^a
^a:
cir.yield
}
cir.if
defines no values and the ‘else' can be omitted. cir.yield
must explicitly terminate the region if it has more than one block.
Traits: AutomaticAllocationScope, NoRegionArguments, RecursivelySpeculatableImplTrait
Interfaces: ConditionallySpeculatable, RegionBranchOpInterface
Operand | Description |
---|---|
condition | CIR bool type |
cir.load
(cir::LoadOp)Load value from memory adddress
Syntax:
operation ::= `cir.load` (`deref` $isDeref^)? $addr `:` `cir.ptr` type($addr) `,`
type($result) attr-dict
cir.load
reads a value (lvalue to rvalue conversion) given an address backed up by a cir.ptr
type. A unit attribute deref
can be used to mark the resulting value as used by another operation to dereference a pointer.
Example:
// Read from local variable, address in %0.
%1 = cir.load %0 : !cir.ptr<i32>, i32
// Load address from memory at address %0. %3 is used by at least one
// operation that dereferences a pointer.
%3 = cir.load deref %0 : cir.ptr <!cir.ptr<i32>>
Interfaces: InferTypeOpInterface
Attribute | MLIR Type | Description |
---|---|---|
isDeref | ::mlir::UnitAttr | unit attribute |
Operand | Description |
---|---|
addr | CIR pointer type |
Result | Description |
---|---|
result | any type |
cir.loop
(cir::LoopOp)Loop
Syntax:
operation ::= `cir.loop` $kind
`(`
`cond` `:` $cond `,`
`step` `:` $step
`)`
$body
attr-dict
cir.loop
represents C/C++ loop forms. It defines 3 blocks:
cond
: region can contain multiple blocks, terminated by regular cir.yield
when control should yield back to the parent, and cir.yield continue
when execution continues to another region. The region destination depends on the loop form specified.step
: region with one block, containing code to compute the loop step, must be terminated with cir.yield
.body
: region for the loop's body, can contain an arbitrary number of blocks.The loop form: for
, while
and dowhile
must also be specified and each implies the loop regions execution order.
// while (true) {
// i = i + 1;
// }
cir.loop while(cond : {
cir.yield continue
}, step : {
cir.yield
}) {
%3 = cir.load %1 : cir.ptr <i32>, i32
%4 = cir.const(1 : i32) : i32
%5 = cir.binop(add, %3, %4) : i32
cir.store %5, %1 : i32, cir.ptr <i32>
cir.yield
}
Traits: NoRegionArguments, RecursivelySpeculatableImplTrait
Interfaces: ConditionallySpeculatable, LoopLikeOpInterface, RegionBranchOpInterface
Attribute | MLIR Type | Description |
---|---|---|
kind | ::mlir::cir::LoopOpKindAttr | Loop kind |
cir.objsize
(cir::ObjSizeOp)Conversion between values of different types
Syntax:
operation ::= `cir.objsize` `(`
$ptr `:` type($ptr) `,`
$kind
(`,` `dynamic` $dynamic^)?
`)`
`->` type($result) attr-dict
Traits: AlwaysSpeculatableImplTrait
Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
Attribute | MLIR Type | Description |
---|---|---|
kind | ::mlir::cir::SizeInfoTypeAttr | size info type |
dynamic | ::mlir::UnitAttr | unit attribute |
Operand | Description |
---|---|
ptr | CIR pointer type |
Result | Description |
---|---|
result | Integer type with arbitrary precision up to a fixed limit |
cir.ptr_diff
(cir::PtrDiffOp)Pointer subtraction arithmetic
Syntax:
operation ::= `cir.ptr_diff` `(` $lhs `,` $rhs `)` `:` type($lhs) `->` type($result) attr-dict
cir.ptr_diff
performs a subtraction between two pointer types with the same element type and produces a mlir::cir::IntType
result.
Note that the result considers the pointer size according to the ABI for the pointee sizes, e.g. the subtraction between two !cir.ptr<!u64i>
might yield 1, meaning 8 bytes, whereas for void
or function type pointees, yielding 8 means 8 bytes.
%7 = "cir.ptr_diff"(%0, %1) : !cir.ptr<!u64i> -> !u64i
Traits: AlwaysSpeculatableImplTrait, SameTypeOperands
Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
Operand | Description |
---|---|
lhs | any type |
rhs | any type |
Result | Description |
---|---|
result | Integer type with arbitrary precision up to a fixed limit |
cir.ptr_stride
(cir::PtrStrideOp)Pointer access with stride
Syntax:
operation ::= `cir.ptr_stride` `(` $base `:` type($base) `,` $stride `:` qualified(type($stride)) `)`
`,` type($result) attr-dict
Given a base pointer as first operand, provides a new pointer after applying a stride (second operand).
%3 = cir.const(0 : i32) : i32
%4 = cir.ptr_stride(%2 : !cir.ptr<i32>, %3 : i32), !cir.ptr<i32>
Traits: AlwaysSpeculatableImplTrait, SameFirstOperandAndResultType
Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
Operand | Description |
---|---|
base | any type |
stride | Integer type with arbitrary precision up to a fixed limit |
Result | Description |
---|---|
result | any type |
cir.return
(cir::ReturnOp)Return from function
Syntax:
operation ::= `cir.return` ($input^ `:` type($input))? attr-dict
The "return" operation represents a return operation within a function. The operation takes an optional operand and produces no results. The operand type must match the signature of the function that contains the operation.
func @foo() -> i32 {
...
cir.return %0 : i32
}
Traits: HasParent<FuncOp, ScopeOp, IfOp, SwitchOp, LoopOp>, Terminator
Operand | Description |
---|---|
input | any type |
cir.scope
(cir::ScopeOp)Represents a C/C++ scope
cir.scope
contains one region and defines a strict "scope" for all new values produced within its blocks.
The region can contain an arbitrary number of blocks but usually defaults to one and can optionally return a value (useful for representing values coming out of C++ full-expressions) via cir.yield
:
%rvalue = cir.scope {
...
cir.yield %value
}
If cir.scope
yields no value, the cir.yield
can be left out, and will be inserted implicitly.
Traits: AutomaticAllocationScope, NoRegionArguments, RecursivelySpeculatableImplTrait
Interfaces: ConditionallySpeculatable, RegionBranchOpInterface
Result | Description |
---|---|
results | any type |
cir.shift
(cir::ShiftOp)Shift
Syntax:
operation ::= `cir.shift` `(`
(`left` $isShiftleft^) : (`right`)?
`,` $value `:` type($value)
`,` $amount `:` type($amount)
`)` `->` type($result) attr-dict
Shift left
or right
, according to the first operand. Second operand is the shift target and the third the amount.
%7 = cir.shift(left, %1 : !u64i, %4 : !s32i) -> !u64i
Traits: AlwaysSpeculatableImplTrait
Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
Attribute | MLIR Type | Description |
---|---|---|
isShiftleft | ::mlir::UnitAttr | unit attribute |
Operand | Description |
---|---|
value | Integer type with arbitrary precision up to a fixed limit |
amount | Integer type with arbitrary precision up to a fixed limit |
Result | Description |
---|---|
result | Integer type with arbitrary precision up to a fixed limit |
cir.store
(cir::StoreOp)Store value to memory address
Syntax:
operation ::= `cir.store` $value `,` $addr attr-dict `:` type($value) `,` `cir.ptr` type($addr)
cir.store
stores a value (first operand) to the memory address specified in the second operand.
Example:
// Store a function argument to local storage, address in %0.
cir.store %arg0, %0 : i32, !cir.ptr<i32>
Operand | Description |
---|---|
value | any type |
addr | CIR pointer type |
cir.struct_element_addr
(cir::StructElementAddr)Get the address of a member of a struct
The cir.struct_element_addr
operaration gets the address of a particular named member from the input struct.
It expects a pointer to the base struct as well as the name of the member and its field index.
Example:
!ty_22struct2EBar22 = type !cir.struct<"struct.Bar", i32, i8>
...
%0 = cir.alloca !ty_22struct2EBar22, cir.ptr <!ty_22struct2EBar22>
...
%1 = cir.struct_element_addr %0, "Bar.a"
%2 = cir.load %1 : cir.ptr <int>, int
...
Attribute | MLIR Type | Description |
---|---|---|
member_name | ::mlir::StringAttr | string attribute |
member_index | ::mlir::IntegerAttr | index attribute |
Operand | Description |
---|---|
struct_addr | CIR pointer type |
Result | Description |
---|---|
result | CIR pointer type |
cir.switch
(cir::SwitchOp)Switch operation
Syntax:
operation ::= `cir.switch` custom<SwitchOp>(
$regions, $cases, $condition, type($condition)
)
attr-dict
The cir.switch
operation represents C/C++ switch functionality for conditionally executing multiple regions of code. The operand to an switch is an integral condition value.
A variadic list of "case" attribute operands and regions track the possible control flow within cir.switch
. A case
must be in one of the following forms:
equal, <constant>
: equality of the second case operand against the condition.anyof, [constant-list]
: equals to any of the values in a subsequent following list.default
: any other value.Each case region must be explicitly terminated.
Examples:
cir.switch (%b : i32) [
case (equal, 20) {
...
cir.yield break
},
case (anyof, [1, 2, 3] : i32) {
...
cir.return ...
}
case (default) {
...
cir.yield fallthrough
}
]
Traits: AutomaticAllocationScope, NoRegionArguments, RecursivelySpeculatableImplTrait, SameVariadicOperandSize
Interfaces: ConditionallySpeculatable, RegionBranchOpInterface
Attribute | MLIR Type | Description |
---|---|---|
cases | ::mlir::ArrayAttr | cir.switch case array attribute |
Operand | Description |
---|---|
condition | Integer type with arbitrary precision up to a fixed limit |
cir.ternary
(cir::TernaryOp)The cond ? a : b
C/C++ ternary operation
Syntax:
operation ::= `cir.ternary` `(` $cond `,`
`true` $trueRegion `,`
`false` $falseRegion
`)` `:` functional-type(operands, results) attr-dict
The cir.ternary
operation represents C/C++ ternary, much like a select
operation. First argument is a cir.bool
condition to evaluate, followed by two regions to execute (true or false). This is different from cir.if
since each region is one block sized and the cir.yield
closing the block scope should have one argument.
Example:
// x = cond ? a : b;
%x = cir.ternary (%cond, true_region {
...
cir.yield %a : i32
}, false_region {
...
cir.yield %b : i32
}) -> i32
Traits: AutomaticAllocationScope, NoRegionArguments, RecursivelySpeculatableImplTrait
Interfaces: ConditionallySpeculatable, RegionBranchOpInterface
Operand | Description |
---|---|
cond | CIR bool type |
Result | Description |
---|---|
result | any type |
cir.unary
(cir::UnaryOp)Unary operations
Syntax:
operation ::= `cir.unary` `(` $kind `,` $input `)` `:` type($input) `,` type($result) attr-dict
cir.unary
performs the unary operation according to the specified opcode kind: [inc, dec, plus, minus, not].
Note for inc and dec: the operation corresponds only to the addition/subtraction, its input is expect to come from a load and the result to be used by a corresponding store.
It requires one input operand and has one result, both types should be the same.
%7 = cir.unary(inc, %1) : i32 -> i32
%8 = cir.unary(dec, %2) : i32 -> i32
Traits: AlwaysSpeculatableImplTrait, SameOperandsAndResultType
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
Attribute | MLIR Type | Description |
---|---|---|
kind | ::mlir::cir::UnaryOpKindAttr | unary operation kind |
Operand | Description |
---|---|
input | any type |
Result | Description |
---|---|
result | any type |
cir.va.arg
(cir::VAArgOp)Fetches next variadic element as a given type
Syntax:
operation ::= `cir.va.arg` $arg_list attr-dict `:` functional-type(operands, $result)
Operand | Description |
---|---|
arg_list | CIR pointer type |
Result | Description |
---|---|
result | any type |
cir.va.copy
(cir::VACopyOp)Copies a variable argument list
Syntax:
operation ::= `cir.va.copy` $src_list `to` $dst_list attr-dict `:` type(operands)
Operand | Description |
---|---|
dst_list | CIR pointer type |
src_list | CIR pointer type |
cir.va.end
(cir::VAEndOp)Ends a variable argument list
Syntax:
operation ::= `cir.va.end` $arg_list attr-dict `:` type(operands)
Operand | Description |
---|---|
arg_list | CIR pointer type |
cir.va.start
(cir::VAStartOp)Starts a variable argument list
Syntax:
operation ::= `cir.va.start` $arg_list attr-dict `:` type(operands)
Operand | Description |
---|---|
arg_list | CIR pointer type |
cir.vtable.address_point
(cir::VTableAddrPointOp)Get the vtable (global variable) address point
Syntax:
operation ::= `cir.vtable.address_point` `(`
($name^)?
($sym_addr^ `:` type($sym_addr))?
`,`
`vtable_index` `=` $vtable_index `,`
`address_point_index` `=` $address_point_index
`)`
`:` `cir.ptr` type($addr) attr-dict
The vtable.address_point
operation retrieves the "effective" address (address point) of a C++ virtual table. An object internal __vptr
gets initializated on top of the value returned by this operation.
vtable_index
provides the appropriate vtable within the vtable group (as specified by Itanium ABI), and addr_point_index
the actual address point within that vtable.
The return type is always a !cir.ptr<!cir.ptr<() -> i32>>
.
Example:
cir.global linkonce_odr @_ZTV1B = ...
...
%3 = cir.vtable.address_point(@_ZTV1B, vtable_index = 0, address_point_index = 2) : cir.ptr <!cir.ptr<() -> i32>>
Traits: AlwaysSpeculatableImplTrait
Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface), SymbolUserOpInterface
Effects: MemoryEffects::Effect{}
Attribute | MLIR Type | Description |
---|---|---|
name | ::mlir::FlatSymbolRefAttr | flat symbol reference attribute |
vtable_index | ::mlir::IntegerAttr | 32-bit signless integer attribute |
address_point_index | ::mlir::IntegerAttr | 32-bit signless integer attribute |
Operand | Description |
---|---|
sym_addr | any type |
Result | Description |
---|---|
addr | CIR pointer type |
cir.yield
(cir::YieldOp)Terminate CIR regions
Syntax:
operation ::= `cir.yield` ($kind^)? ($args^ `:` type($args))? attr-dict
The cir.yield
operation terminates regions on different CIR operations: cir.if
, cir.scope
, cir.switch
, cir.loop
, cir.await
, cir.ternary
and cir.global
.
Might yield an SSA value and the semantics of how the values are yielded is defined by the parent operation.
Optionally, cir.yield
can be annotated with extra kind specifiers:
break
: breaking out of the innermost cir.switch
/ cir.loop
semantics, cannot be used if not dominated by these parent operations.fallthrough
: execution falls to the next region in cir.switch
case list. Only available inside cir.switch
regions.continue
: only allowed under cir.loop
, continue execution to the next loop step.nosuspend
: specific to the ready
region inside cir.await
op, it makes control-flow to be transfered back to the parent, preventing suspension.As a general rule, cir.yield
must be explicitly used whenever a region has more than one block and no terminator, or within cir.switch
regions not cir.return
terminated.
Examples:
cir.if %4 {
...
cir.yield
}
cir.switch (%5) [
case (equal, 3) {
...
cir.yield fallthrough
}, ...
]
cir.loop (cond : {...}, step : {...}) {
...
cir.yield continue
}
cir.await(init, ready : {
// Call std::suspend_always::await_ready
%18 = cir.call @_ZNSt14suspend_always11await_readyEv(...)
cir.if %18 {
// yields back to the parent.
cir.yield nosuspend
}
cir.yield // control-flow to the next region for suspension.
}, ...)
cir.scope {
...
cir.yield
}
%x = cir.scope {
...
cir.yield %val
}
%y = cir.ternary {
...
cir.yield %val : i32
} : i32
Traits: HasParent<IfOp, ScopeOp, SwitchOp, LoopOp, AwaitOp, TernaryOp, GlobalOp>, ReturnLike, Terminator
Attribute | MLIR Type | Description |
---|---|---|
kind | ::mlir::cir::YieldOpKindAttr | yield kind |
Operand | Description |
---|---|
args | any type |