'emitc' Dialect
Dialect to generate C/C++ from MLIR.
The EmitC dialect allows to convert operations from other MLIR dialects to EmitC ops. Those can be translated to C/C++ via the Cpp emitter.
The following convention is followed:
- If template arguments are passed to an
emitc.call_opaque
operation, C++ is generated. - If tensors are used, C++ is generated.
- If multiple return values are used within in a functions or an
emitc.call_opaque
operation, C++11 is required. - If floating-point type template arguments are passed to an
emitc.call_opaque
operation, C++20 is required. - If
ssize_t
is used, then the code requires the POSIX headersys/types.h
or any of the C++ headers in which the type is defined. - If
_Float16
is used, the code requires the support of C additional floating types. - If
__bf16
is used, the code requires a compiler that supports it, such as GCC or Clang. - Else the generated code is compatible with C99.
These restrictions are neither inherent to the EmitC dialect itself nor to the Cpp emitter and therefore need to be considered while implementing conversions.
Type conversions are provided for the MLIR type index
into the unsigned size_t
type and its signed counterpart ptrdiff_t
. Conversions between these two types
are only valid if the index
-typed values are within
[PTRDIFF_MIN, PTRDIFF_MAX]
.
After the conversion, C/C++ code can be emitted with mlir-translate
. The tool
supports translating MLIR to C/C++ by passing -mlir-to-cpp
. Furthermore, code
with variables declared at top can be generated by passing the additional
argument -declare-variables-at-top
.
Besides operations part of the EmitC dialect, the Cpp targets supports translating the following operations:
- ‘cf’ Dialect
cf.br
cf.cond_br
- ‘func’ Dialect
func.call
func.func
func.return
Operations ¶
emitc.add
(emitc::AddOp) ¶
Addition operation
Syntax:
operation ::= `emitc.add` operands attr-dict `:` functional-type(operands, results)
With the emitc.add
operation the arithmetic operator + (addition) can
be applied.
Example:
// Custom form of the addition operation.
%0 = emitc.add %arg0, %arg1 : (i32, i32) -> i32
%1 = emitc.add %arg2, %arg3 : (!emitc.ptr<f32>, i32) -> !emitc.ptr<f32>
// Code emitted for the operations above.
int32_t v5 = v1 + v2;
float* v6 = v3 + v4;
Traits: CExpression
Operands: ¶
Operand | Description |
---|---|
lhs | type supported by EmitC |
rhs | type supported by EmitC |
Results: ¶
Result | Description |
---|---|
«unnamed» | type supported by EmitC |
emitc.apply
(emitc::ApplyOp) ¶
Apply operation
Syntax:
operation ::= `emitc.apply` $applicableOperator `(` $operand `)` attr-dict `:` functional-type($operand, results)
With the emitc.apply
operation the operators & (address of) and * (contents of)
can be applied to a single operand.
Example:
// Custom form of applying the & operator.
%0 = emitc.apply "&"(%arg0) : (!emitc.lvalue<i32>) -> !emitc.ptr<i32>
// Generic form of the same operation.
%0 = "emitc.apply"(%arg0) {applicableOperator = "&"}
: (!emitc.lvalue<i32>) -> !emitc.ptr<i32>
Traits: CExpression
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
applicableOperator | ::mlir::StringAttr | string attribute |
Operands: ¶
Operand | Description |
---|---|
operand | type supported by EmitC or EmitC lvalue type |
Results: ¶
Result | Description |
---|---|
result | type supported by EmitC |
emitc.assign
(emitc::AssignOp) ¶
Assign operation
Syntax:
operation ::= `emitc.assign` $value `:` type($value) `to` $var `:` type($var) attr-dict
The emitc.assign
operation stores an SSA value to the location designated by an
EmitC variable. This operation doesn’t return any value. The assigned value
must be of the same type as the variable being assigned. The operation is
emitted as a C/C++ ‘=’ operator.
Example:
// Integer variable
%0 = "emitc.variable"(){value = 42 : i32} : () -> !emitc.lvalue<i32>
%1 = emitc.call_opaque "foo"() : () -> (i32)
// Assign emitted as `... = ...;`
"emitc.assign"(%0, %1) : (!emitc.lvalue<i32>, i32) -> ()
Operands: ¶
Operand | Description |
---|---|
var | EmitC lvalue type |
value | type supported by EmitC |
emitc.bitwise_and
(emitc::BitwiseAndOp) ¶
Bitwise and operation
Syntax:
operation ::= `emitc.bitwise_and` operands attr-dict `:` functional-type(operands, results)
With the emitc.bitwise_and
operation the bitwise operator & (and) can
be applied.
Example:
%0 = emitc.bitwise_and %arg0, %arg1 : (i32, i32) -> i32
// Code emitted for the operation above.
int32_t v3 = v1 & v2;
Traits: CExpression
Operands: ¶
Operand | Description |
---|---|
lhs | type supported by EmitC |
rhs | type supported by EmitC |
Results: ¶
Result | Description |
---|---|
«unnamed» | type supported by EmitC |
emitc.bitwise_left_shift
(emitc::BitwiseLeftShiftOp) ¶
Bitwise left shift operation
Syntax:
operation ::= `emitc.bitwise_left_shift` operands attr-dict `:` functional-type(operands, results)
With the emitc.bitwise_left_shift
operation the bitwise operator «
(left shift) can be applied.
Example:
%0 = emitc.bitwise_left_shift %arg0, %arg1 : (i32, i32) -> i32
// Code emitted for the operation above.
int32_t v3 = v1 << v2;
Traits: CExpression
Operands: ¶
Operand | Description |
---|---|
lhs | type supported by EmitC |
rhs | type supported by EmitC |
Results: ¶
Result | Description |
---|---|
«unnamed» | type supported by EmitC |
emitc.bitwise_not
(emitc::BitwiseNotOp) ¶
Bitwise not operation
Syntax:
operation ::= `emitc.bitwise_not` operands attr-dict `:` functional-type(operands, results)
With the emitc.bitwise_not
operation the bitwise operator ~ (not) can
be applied.
Example:
%0 = emitc.bitwise_not %arg0 : (i32) -> i32
// Code emitted for the operation above.
int32_t v2 = ~v1;
Traits: CExpression
Operands: ¶
Operand | Description |
---|---|
«unnamed» | type supported by EmitC |
Results: ¶
Result | Description |
---|---|
«unnamed» | type supported by EmitC |
emitc.bitwise_or
(emitc::BitwiseOrOp) ¶
Bitwise or operation
Syntax:
operation ::= `emitc.bitwise_or` operands attr-dict `:` functional-type(operands, results)
With the emitc.bitwise_or
operation the bitwise operator | (or)
can be applied.
Example:
%0 = emitc.bitwise_or %arg0, %arg1 : (i32, i32) -> i32
// Code emitted for the operation above.
int32_t v3 = v1 | v2;
Traits: CExpression
Operands: ¶
Operand | Description |
---|---|
lhs | type supported by EmitC |
rhs | type supported by EmitC |
Results: ¶
Result | Description |
---|---|
«unnamed» | type supported by EmitC |
emitc.bitwise_right_shift
(emitc::BitwiseRightShiftOp) ¶
Bitwise right shift operation
Syntax:
operation ::= `emitc.bitwise_right_shift` operands attr-dict `:` functional-type(operands, results)
With the emitc.bitwise_right_shift
operation the bitwise operator »
(right shift) can be applied.
Example:
%0 = emitc.bitwise_right_shift %arg0, %arg1 : (i32, i32) -> i32
// Code emitted for the operation above.
int32_t v3 = v1 >> v2;
Traits: CExpression
Operands: ¶
Operand | Description |
---|---|
lhs | type supported by EmitC |
rhs | type supported by EmitC |
Results: ¶
Result | Description |
---|---|
«unnamed» | type supported by EmitC |
emitc.bitwise_xor
(emitc::BitwiseXorOp) ¶
Bitwise xor operation
Syntax:
operation ::= `emitc.bitwise_xor` operands attr-dict `:` functional-type(operands, results)
With the emitc.bitwise_xor
operation the bitwise operator ^ (xor)
can be applied.
Example:
%0 = emitc.bitwise_xor %arg0, %arg1 : (i32, i32) -> i32
// Code emitted for the operation above.
int32_t v3 = v1 ^ v2;
Traits: CExpression
Operands: ¶
Operand | Description |
---|---|
lhs | type supported by EmitC |
rhs | type supported by EmitC |
Results: ¶
Result | Description |
---|---|
«unnamed» | type supported by EmitC |
emitc.call
(emitc::CallOp) ¶
Call operation
Syntax:
operation ::= `emitc.call` $callee `(` $operands `)` attr-dict `:` functional-type($operands, results)
The emitc.call
operation represents a direct call to an emitc.func
that is within the same symbol scope as the call. The operands and result type
of the call must match the specified function type. The callee is encoded as a
symbol reference attribute named “callee”.
Example:
%2 = emitc.call @my_add(%0, %1) : (f32, f32) -> f32
Traits: CExpression
Interfaces: CallOpInterface
, SymbolUserOpInterface
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
callee | ::mlir::FlatSymbolRefAttr | flat symbol reference attribute |
Operands: ¶
Operand | Description |
---|---|
operands | variadic of type supported by EmitC |
Results: ¶
Result | Description |
---|---|
«unnamed» | variadic of type supported by EmitC |
emitc.call_opaque
(emitc::CallOpaqueOp) ¶
Opaque call operation
Syntax:
operation ::= `emitc.call_opaque` $callee `(` $operands `)` attr-dict `:` functional-type($operands, results)
The emitc.call_opaque
operation represents a C++ function call. The callee
can be an arbitrary non-empty string. The call allows specifying order
of operands and attributes in the call as follows:
- integer value of index type refers to an operand;
- attribute which will get lowered to constant value in call;
Example:
// Custom form defining a call to `foo()`.
%0 = emitc.call_opaque "foo" () : () -> i32
// Generic form of the same operation.
%0 = "emitc.call_opaque"() {callee = "foo"} : () -> i32
Traits: CExpression
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
callee | ::mlir::StringAttr | string attribute |
args | ::mlir::ArrayAttr | array attribute |
template_args | ::mlir::ArrayAttr | array attribute |
Operands: ¶
Operand | Description |
---|---|
operands | variadic of type supported by EmitC |
Results: ¶
Result | Description |
---|---|
«unnamed» | variadic of type supported by EmitC |
emitc.cast
(emitc::CastOp) ¶
Cast operation
Syntax:
operation ::= `emitc.cast` $source attr-dict `:` type($source) `to` type($dest)
The emitc.cast
operation performs an explicit type conversion and is emitted
as a C-style cast expression. It can be applied to integer, float, index
and EmitC types.
Example:
// Cast from `int32_t` to `float`
%0 = emitc.cast %arg0: i32 to f32
// Cast from `void` to `int32_t` pointer
%1 = emitc.cast %arg1 :
!emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<i32>
Traits: CExpression
, SameOperandsAndResultShape
Interfaces: CastOpInterface
Operands: ¶
Operand | Description |
---|---|
source | type supported by EmitC |
Results: ¶
Result | Description |
---|---|
dest | type supported by EmitC |
emitc.cmp
(emitc::CmpOp) ¶
Comparison operation
Syntax:
operation ::= `emitc.cmp` $predicate `,` operands attr-dict `:` functional-type(operands, results)
With the emitc.cmp
operation the comparison operators ==, !=, <, <=, >, >=, <=>
can be applied.
Its first argument is an attribute that defines the comparison operator:
- equal to (mnemonic:
"eq"
; integer value:0
) - not equal to (mnemonic:
"ne"
; integer value:1
) - less than (mnemonic:
"lt"
; integer value:2
) - less than or equal to (mnemonic:
"le"
; integer value:3
) - greater than (mnemonic:
"gt"
; integer value:4
) - greater than or equal to (mnemonic:
"ge"
; integer value:5
) - three-way-comparison (mnemonic:
"three_way"
; integer value:6
)
Example:
// Custom form of the cmp operation.
%0 = emitc.cmp eq, %arg0, %arg1 : (i32, i32) -> i1
%1 = emitc.cmp lt, %arg2, %arg3 :
(
!emitc.opaque<"std::valarray<float>">,
!emitc.opaque<"std::valarray<float>">
) -> !emitc.opaque<"std::valarray<bool>">
// Code emitted for the operations above.
bool v5 = v1 == v2;
std::valarray<bool> v6 = v3 < v4;
Traits: CExpression
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
predicate | ::mlir::emitc::CmpPredicateAttr | allowed 64-bit signless integer cases: 0, 1, 2, 3, 4, 5, 6Enum cases:
|
Operands: ¶
Operand | Description |
---|---|
lhs | type supported by EmitC |
rhs | type supported by EmitC |
Results: ¶
Result | Description |
---|---|
«unnamed» | type supported by EmitC |
emitc.conditional
(emitc::ConditionalOp) ¶
Conditional (ternary) operation
Syntax:
operation ::= `emitc.conditional` operands attr-dict `:` type($result)
With the emitc.conditional
operation the ternary conditional operator can
be applied.
Example:
%0 = emitc.cmp gt, %arg0, %arg1 : (i32, i32) -> i1
%c0 = "emitc.constant"() {value = 10 : i32} : () -> i32
%c1 = "emitc.constant"() {value = 11 : i32} : () -> i32
%1 = emitc.conditional %0, %c0, %c1 : i32
// Code emitted for the operations above.
bool v3 = v1 > v2;
int32_t v4 = 10;
int32_t v5 = 11;
int32_t v6 = v3 ? v4 : v5;
Traits: CExpression
Operands: ¶
Operand | Description |
---|---|
condition | 1-bit signless integer |
true_value | type supported by EmitC |
false_value | type supported by EmitC |
Results: ¶
Result | Description |
---|---|
result | type supported by EmitC |
emitc.constant
(emitc::ConstantOp) ¶
Constant operation
The emitc.constant
operation produces an SSA value equal to some constant
specified by an attribute. This can be used to form simple integer and
floating point constants, as well as more exotic things like tensor
constants. The emitc.constant
operation also supports the EmitC opaque
attribute and the EmitC opaque type. Since folding is supported,
it should not be used with pointers.
Example:
// Integer constant
%0 = "emitc.constant"(){value = 42 : i32} : () -> i32
// Constant emitted as `char = CHAR_MIN;`
%1 = "emitc.constant"() {value = #emitc.opaque<"CHAR_MIN">}
: () -> !emitc.opaque<"char">
Traits: ConstantLike
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
value | ::mlir::Attribute | An opaque attribute or TypedAttr instance |
Results: ¶
Result | Description |
---|---|
«unnamed» | type supported by EmitC |
emitc.declare_func
(emitc::DeclareFuncOp) ¶
An operation to declare a function
Syntax:
operation ::= `emitc.declare_func` $sym_name attr-dict
The emitc.declare_func
operation allows to insert a function declaration for an
emitc.func
at a specific position. The operation only requires the “callee”
of the emitc.func
to be specified as an attribute.
Example:
emitc.declare_func @bar
emitc.func @foo(%arg0: i32) -> i32 {
%0 = emitc.call @bar(%arg0) : (i32) -> (i32)
emitc.return %0 : i32
}
emitc.func @bar(%arg0: i32) -> i32 {
emitc.return %arg0 : i32
}
// Code emitted for the operations above.
int32_t bar(int32_t v1);
int32_t foo(int32_t v1) {
int32_t v2 = bar(v1);
return v2;
}
int32_t bar(int32_t v1) {
return v1;
}
Interfaces: SymbolUserOpInterface
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
sym_name | ::mlir::FlatSymbolRefAttr | flat symbol reference attribute |
emitc.div
(emitc::DivOp) ¶
Division operation
Syntax:
operation ::= `emitc.div` operands attr-dict `:` functional-type(operands, results)
With the emitc.div
operation the arithmetic operator / (division) can
be applied.
Example:
// Custom form of the division operation.
%0 = emitc.div %arg0, %arg1 : (i32, i32) -> i32
%1 = emitc.div %arg2, %arg3 : (f32, f32) -> f32
// Code emitted for the operations above.
int32_t v5 = v1 / v2;
float v6 = v3 / v4;
Traits: CExpression
Operands: ¶
Operand | Description |
---|---|
«unnamed» | floating-point type supported by EmitC or integer, index or opaque type supported by EmitC |
«unnamed» | floating-point type supported by EmitC or integer, index or opaque type supported by EmitC |
Results: ¶
Result | Description |
---|---|
«unnamed» | floating-point type supported by EmitC or integer, index or opaque type supported by EmitC |
emitc.expression
(emitc::ExpressionOp) ¶
Expression operation
Syntax:
operation ::= `emitc.expression` attr-dict (`noinline` $do_not_inline^)? `:` type($result) $region
The emitc.expression
operation returns a single SSA value which is yielded by
its single-basic-block region. The operation doesn’t take any arguments.
As the operation is to be emitted as a C expression, the operations within
its body must form a single Def-Use tree of emitc ops whose result is
yielded by a terminating emitc.yield
.
Example:
%r = emitc.expression : i32 {
%0 = emitc.add %a, %b : (i32, i32) -> i32
%1 = emitc.call_opaque "foo"(%0) : (i32) -> i32
%2 = emitc.add %c, %d : (i32, i32) -> i32
%3 = emitc.mul %1, %2 : (i32, i32) -> i32
emitc.yield %3 : i32
}
May be emitted as
int32_t v7 = foo(v1 + v2) * (v3 + v4);
The operations allowed within expression body are EmitC operations with the CExpression trait.
When specified, the optional do_not_inline
indicates that the expression is
to be emitted as seen above, i.e. as the rhs of an EmitC SSA value
definition. Otherwise, the expression may be emitted inline, i.e. directly
at its use.
Traits: HasOnlyGraphRegion
, NoRegionArguments
, SingleBlockImplicitTerminator<emitc::YieldOp>
, SingleBlock
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
do_not_inline | ::mlir::UnitAttr | unit attribute |
Results: ¶
Result | Description |
---|---|
result | type supported by EmitC |
emitc.for
(emitc::ForOp) ¶
For operation
The emitc.for
operation represents a C loop of the following form:
for (T i = lb; i < ub; i += step) { /* ... */ } // where T is typeof(lb)
The operation takes 3 SSA values as operands that represent the lower bound, upper bound and step respectively, and defines an SSA value for its induction variable. It has one region capturing the loop body. The induction variable is represented as an argument of this region. This SSA value is a signless integer, or an index. The step is a value of same type.
This operation has no result. The body region must contain exactly one block
that terminates with emitc.yield
. Calling ForOp::build will create such a
region and insert the terminator implicitly if none is defined, so will the
parsing even in cases when it is absent from the custom format. For example:
// Index case.
emitc.for %iv = %lb to %ub step %step {
... // body
}
...
// Integer case.
emitc.for %iv_32 = %lb_32 to %ub_32 step %step_32 : i32 {
... // body
}
Traits: RecursiveMemoryEffects
, SingleBlockImplicitTerminator<emitc::YieldOp>
, SingleBlock
Operands: ¶
Operand | Description |
---|---|
lowerBound | integer, index or opaque type supported by EmitC |
upperBound | integer, index or opaque type supported by EmitC |
step | integer, index or opaque type supported by EmitC |
emitc.func
(emitc::FuncOp) ¶
An operation with a name containing a single SSACFG
region
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). 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.
Example:
// A function with no results:
emitc.func @foo(%arg0 : i32) {
emitc.call_opaque "bar" (%arg0) : (i32) -> ()
emitc.return
}
// A function with its argument as single result:
emitc.func @foo(%arg0 : i32) -> i32 {
emitc.return %arg0 : i32
}
// A function with specifiers attribute:
emitc.func @example_specifiers_fn_attr() -> i32
attributes {specifiers = ["static","inline"]} {
%0 = emitc.call_opaque "foo" (): () -> i32
emitc.return %0 : i32
}
// An external function definition:
emitc.func private @extern_func(i32)
attributes {specifiers = ["extern"]}
Traits: AutomaticAllocationScope
, IsolatedFromAbove
Interfaces: CallableOpInterface
, FunctionOpInterface
, Symbol
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
sym_name | ::mlir::StringAttr | string attribute |
function_type | ::mlir::TypeAttr | type attribute of function type |
specifiers | ::mlir::ArrayAttr | string array attribute |
arg_attrs | ::mlir::ArrayAttr | Array of dictionary attributes |
res_attrs | ::mlir::ArrayAttr | Array of dictionary attributes |
emitc.get_global
(emitc::GetGlobalOp) ¶
Obtain access to a global variable
Syntax:
operation ::= `emitc.get_global` $name `:` type($result) attr-dict
The emitc.get_global
operation retrieves the lvalue of a
named global variable. If the global variable is marked constant, assigning
to that lvalue is undefined.
Example:
%x = emitc.get_global @foo : !emitc.array<2xf32>
%y = emitc.get_global @bar : !emitc.lvalue<i32>
Traits: AlwaysSpeculatableImplTrait
Interfaces: ConditionallySpeculatable
, NoMemoryEffect (MemoryEffectOpInterface)
, SymbolUserOpInterface
Effects: MemoryEffects::Effect{}
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
name | ::mlir::FlatSymbolRefAttr | flat symbol reference attribute |
Results: ¶
Result | Description |
---|---|
result | EmitC array type or EmitC lvalue type |
emitc.global
(emitc::GlobalOp) ¶
A global variable
Syntax:
operation ::= `emitc.global` (`extern` $extern_specifier^)?
(`static` $static_specifier^)?
(`const` $const_specifier^)?
$sym_name
`:` custom<EmitCGlobalOpTypeAndInitialValue>($type, $initial_value)
attr-dict
The emitc.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.
Optionally, an initial_value
can be provided.
Internal linkage can be specified using the static_specifier
unit attribute
and external linkage can be specified using the extern_specifier
unit attribute.
Note that the default linkage without those two keywords depends on whether
the target is C or C++ and whether the global variable is const
.
The global variable can also be marked constant using the const_specifier
unit attribute. Writing to such constant global variables is
undefined.
The global variable can be accessed by using the emitc.get_global
to
retrieve the value for the global variable.
Example:
// Global variable with an initial value.
emitc.global @x : !emitc.array<2xf32> = dense<0.0>
// Global variable with an initial values.
emitc.global @x : !emitc.array<3xi32> = dense<[0, 1, 2]>
// External global variable
emitc.global extern @x : !emitc.array<2xf32>
// Constant global variable with internal linkage
emitc.global static const @x : i32 = 0
Interfaces: Symbol
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
sym_name | ::mlir::StringAttr | string attribute |
type | ::mlir::TypeAttr | any type attribute |
initial_value | ::mlir::Attribute | An opaque attribute or TypedAttr instance |
extern_specifier | ::mlir::UnitAttr | unit attribute |
static_specifier | ::mlir::UnitAttr | unit attribute |
const_specifier | ::mlir::UnitAttr | unit attribute |
emitc.if
(emitc::IfOp) ¶
If-then-else operation
The emitc.if
operation represents an if-then-else construct for
conditionally executing two regions of code. The operand to an if operation
is a boolean value. For example:
emitc.if %b {
...
} else {
...
}
The “then” region has exactly 1 block. The “else” region may have 0 or 1
blocks. The blocks are always terminated with emitc.yield
, which can be
left out to be inserted implicitly. This operation doesn’t produce any
results.
Traits: NoRegionArguments
, RecursiveMemoryEffects
, SingleBlockImplicitTerminator<emitc::YieldOp>
, SingleBlock
Interfaces: RegionBranchOpInterface
Operands: ¶
Operand | Description |
---|---|
condition | 1-bit signless integer |
emitc.include
(emitc::IncludeOp) ¶
Include operation
The emitc.include
operation allows to define a source file inclusion via the
#include
directive.
Example:
// Custom form defining the inclusion of `<myheader>`.
emitc.include <"myheader.h">
// Generic form of the same operation.
"emitc.include" (){include = "myheader.h", is_standard_include} : () -> ()
// Custom form defining the inclusion of `"myheader"`.
emitc.include "myheader.h"
// Generic form of the same operation.
"emitc.include" (){include = "myheader.h"} : () -> ()
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
include | ::mlir::StringAttr | string attribute |
is_standard_include | ::mlir::UnitAttr | unit attribute |
emitc.literal
(emitc::LiteralOp) ¶
Literal operation
Syntax:
operation ::= `emitc.literal` $value attr-dict `:` type($result)
The emitc.literal
operation produces an SSA value equal to some constant
specified by an attribute.
Traits: AlwaysSpeculatableImplTrait
Interfaces: ConditionallySpeculatable
, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
value | ::mlir::StringAttr | string attribute |
Results: ¶
Result | Description |
---|---|
result | type supported by EmitC |
emitc.load
(emitc::LoadOp) ¶
Load an lvalue into an SSA value.
Syntax:
operation ::= `emitc.load` $operand attr-dict `:` type($operand)
This operation loads the content of a modifiable lvalue into an SSA value. Modifications of the lvalue executed after the load are not observable on the produced value.
Example:
%1 = emitc.load %0 : !emitc.lvalue<i32>
// Code emitted for the operation above.
int32_t v2 = v1;
Operands: ¶
Operand | Description |
---|---|
operand | EmitC lvalue type |
Results: ¶
Result | Description |
---|---|
result | any type |
emitc.logical_and
(emitc::LogicalAndOp) ¶
Logical and operation
Syntax:
operation ::= `emitc.logical_and` operands attr-dict `:` type(operands)
With the emitc.logical_and
operation the logical operator && (and) can
be applied.
Example:
%0 = emitc.logical_and %arg0, %arg1 : i32, i32
// Code emitted for the operation above.
bool v3 = v1 && v2;
Traits: CExpression
Operands: ¶
Operand | Description |
---|---|
lhs | type supported by EmitC |
rhs | type supported by EmitC |
Results: ¶
Result | Description |
---|---|
«unnamed» | 1-bit signless integer |
emitc.logical_not
(emitc::LogicalNotOp) ¶
Logical not operation
Syntax:
operation ::= `emitc.logical_not` operands attr-dict `:` type(operands)
With the emitc.logical_not
operation the logical operator ! (negation) can
be applied.
Example:
%0 = emitc.logical_not %arg0 : i32
// Code emitted for the operation above.
bool v2 = !v1;
Traits: CExpression
Operands: ¶
Operand | Description |
---|---|
«unnamed» | type supported by EmitC |
Results: ¶
Result | Description |
---|---|
«unnamed» | 1-bit signless integer |
emitc.logical_or
(emitc::LogicalOrOp) ¶
Logical or operation
Syntax:
operation ::= `emitc.logical_or` operands attr-dict `:` type(operands)
With the emitc.logical_or
operation the logical operator || (inclusive or)
can be applied.
Example:
%0 = emitc.logical_or %arg0, %arg1 : i32, i32
// Code emitted for the operation above.
bool v3 = v1 || v2;
Traits: CExpression
Operands: ¶
Operand | Description |
---|---|
lhs | type supported by EmitC |
rhs | type supported by EmitC |
Results: ¶
Result | Description |
---|---|
«unnamed» | 1-bit signless integer |
emitc.member
(emitc::MemberOp) ¶
Member operation
With the emitc.member
operation the member access operator .
can be
applied.
Example:
%0 = "emitc.member" (%arg0) {member = "a"}
: (!emitc.lvalue<!emitc.opaque<"mystruct">>) -> !emitc.lvalue<i32>
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
member | ::mlir::StringAttr | string attribute |
Operands: ¶
Operand | Description |
---|---|
operand | emitc.lvalue of EmitC opaque type values |
Results: ¶
Result | Description |
---|---|
«unnamed» | emitc.lvalue of type supported by EmitC values |
emitc.member_of_ptr
(emitc::MemberOfPtrOp) ¶
Member of pointer operation
With the emitc.member_of_ptr
operation the member access operator ->
can be applied.
Example:
%0 = "emitc.member_of_ptr" (%arg0) {member = "a"}
: (!emitc.lvalue<!emitc.ptr<!emitc.opaque<"mystruct">>>)
-> !emitc.lvalue<i32>
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
member | ::mlir::StringAttr | string attribute |
Operands: ¶
Operand | Description |
---|---|
operand | emitc.lvalue of EmitC opaque type or EmitC pointer type values |
Results: ¶
Result | Description |
---|---|
«unnamed» | emitc.lvalue of type supported by EmitC values |
emitc.mul
(emitc::MulOp) ¶
Multiplication operation
Syntax:
operation ::= `emitc.mul` operands attr-dict `:` functional-type(operands, results)
With the emitc.mul
operation the arithmetic operator * (multiplication) can
be applied.
Example:
// Custom form of the multiplication operation.
%0 = emitc.mul %arg0, %arg1 : (i32, i32) -> i32
%1 = emitc.mul %arg2, %arg3 : (f32, f32) -> f32
// Code emitted for the operations above.
int32_t v5 = v1 * v2;
float v6 = v3 * v4;
Traits: CExpression
Operands: ¶
Operand | Description |
---|---|
«unnamed» | floating-point type supported by EmitC or integer, index or opaque type supported by EmitC |
«unnamed» | floating-point type supported by EmitC or integer, index or opaque type supported by EmitC |
Results: ¶
Result | Description |
---|---|
«unnamed» | floating-point type supported by EmitC or integer, index or opaque type supported by EmitC |
emitc.rem
(emitc::RemOp) ¶
Remainder operation
Syntax:
operation ::= `emitc.rem` operands attr-dict `:` functional-type(operands, results)
With the emitc.rem
operation the arithmetic operator % (remainder) can
be applied.
Example:
// Custom form of the remainder operation.
%0 = emitc.rem %arg0, %arg1 : (i32, i32) -> i32
// Code emitted for the operation above.
int32_t v5 = v1 % v2;
Traits: CExpression
Operands: ¶
Operand | Description |
---|---|
«unnamed» | integer, index or opaque type supported by EmitC |
«unnamed» | integer, index or opaque type supported by EmitC |
Results: ¶
Result | Description |
---|---|
«unnamed» | integer, index or opaque type supported by EmitC |
emitc.return
(emitc::ReturnOp) ¶
Function return operation
Syntax:
operation ::= `emitc.return` attr-dict ($operand^ `:` type($operand))?
The emitc.return
operation represents a return operation within a function.
The operation takes zero or exactly one operand and produces no results.
The operand number and type must match the signature of the function
that contains the operation.
Example:
emitc.func @foo() : (i32) {
...
emitc.return %0 : i32
}
Traits: AlwaysSpeculatableImplTrait
, HasParent<FuncOp>
, ReturnLike
, Terminator
Interfaces: ConditionallySpeculatable
, NoMemoryEffect (MemoryEffectOpInterface)
, RegionBranchTerminatorOpInterface
Effects: MemoryEffects::Effect{}
Operands: ¶
Operand | Description |
---|---|
operand | type supported by EmitC |
emitc.sub
(emitc::SubOp) ¶
Subtraction operation
Syntax:
operation ::= `emitc.sub` operands attr-dict `:` functional-type(operands, results)
With the emitc.sub
operation the arithmetic operator - (subtraction) can
be applied.
Example:
// Custom form of the substraction operation.
%0 = emitc.sub %arg0, %arg1 : (i32, i32) -> i32
%1 = emitc.sub %arg2, %arg3 : (!emitc.ptr<f32>, i32) -> !emitc.ptr<f32>
%2 = emitc.sub %arg4, %arg5 : (!emitc.ptr<i32>, !emitc.ptr<i32>)
-> !emitc.ptrdiff_t
// Code emitted for the operations above.
int32_t v7 = v1 - v2;
float* v8 = v3 - v4;
ptrdiff_t v9 = v5 - v6;
Traits: CExpression
Operands: ¶
Operand | Description |
---|---|
lhs | type supported by EmitC |
rhs | type supported by EmitC |
Results: ¶
Result | Description |
---|---|
«unnamed» | type supported by EmitC |
emitc.subscript
(emitc::SubscriptOp) ¶
Subscript operation
Syntax:
operation ::= `emitc.subscript` $value `[` $indices `]` attr-dict `:` functional-type(operands, results)
With the emitc.subscript
operation the subscript operator []
can be applied
to variables or arguments of array, pointer and opaque type.
Example:
%i = index.constant 1
%j = index.constant 7
%0 = emitc.subscript %arg0[%i, %j] : (!emitc.array<4x8xf32>, index, index)
-> !emitc.lvalue<f32>
%1 = emitc.subscript %arg1[%i] : (!emitc.ptr<i32>, index)
-> !emitc.lvalue<i32>
Operands: ¶
Operand | Description |
---|---|
value | EmitC array type or EmitC opaque type or EmitC pointer type |
indices | variadic of type supported by EmitC |
Results: ¶
Result | Description |
---|---|
result | EmitC lvalue type |
emitc.switch
(emitc::SwitchOp) ¶
Switch operation
Syntax:
operation ::= `emitc.switch` $arg `:` type($arg) attr-dict custom<SwitchCases>($cases, $caseRegions) `\n`
`` `default` $defaultRegion
The emitc.switch
is a control-flow operation that branches to one of
the given regions based on the values of the argument and the cases.
The operand to a switch operation is a opaque, integral or pointer
wide types.
The operation always has a “default” region and any number of case regions denoted by integer constants. Control-flow transfers to the case region whose constant value equals the value of the argument. If the argument does not equal any of the case values, control-flow transfer to the “default” region.
The operation does not return any value. Moreover, case regions must be
explicitly terminated using the emitc.yield
operation. Default region is
yielded implicitly.
Example:
// Example:
emitc.switch %0 : i32
case 2 {
%1 = emitc.call_opaque "func_b" () : () -> i32
emitc.yield
}
case 5 {
%2 = emitc.call_opaque "func_a" () : () -> i32
emitc.yield
}
default {
%3 = "emitc.constant"(){value = 42.0 : f32} : () -> f32
emitc.call_opaque "func2" (%3) : (f32) -> ()
}
// Code emitted for the operations above.
switch (v1) {
case 2: {
int32_t v2 = func_b();
break;
}
case 5: {
int32_t v3 = func_a();
break;
}
default: {
float v4 = 4.200000000e+01f;
func2(v4);
break;
}
}
Traits: RecursiveMemoryEffects
, SingleBlockImplicitTerminator<emitc::YieldOp>
, SingleBlock
Interfaces: RegionBranchOpInterface
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
cases | ::mlir::DenseI64ArrayAttr | i64 dense array attribute |
Operands: ¶
Operand | Description |
---|---|
arg | integer, index or opaque type supported by EmitC |
emitc.unary_minus
(emitc::UnaryMinusOp) ¶
Unary minus operation
Syntax:
operation ::= `emitc.unary_minus` operands attr-dict `:` functional-type(operands, results)
With the emitc.unary_minus
operation the unary operator - (minus) can be
applied.
Example:
%0 = emitc.unary_minus %arg0 : (i32) -> i32
// Code emitted for the operation above.
int32_t v2 = -v1;
Traits: CExpression
Operands: ¶
Operand | Description |
---|---|
«unnamed» | type supported by EmitC |
Results: ¶
Result | Description |
---|---|
«unnamed» | type supported by EmitC |
emitc.unary_plus
(emitc::UnaryPlusOp) ¶
Unary plus operation
Syntax:
operation ::= `emitc.unary_plus` operands attr-dict `:` functional-type(operands, results)
With the emitc.unary_plus
operation the unary operator + (plus) can be
applied.
Example:
%0 = emitc.unary_plus %arg0 : (i32) -> i32
// Code emitted for the operation above.
int32_t v2 = +v1;
Traits: CExpression
Operands: ¶
Operand | Description |
---|---|
«unnamed» | type supported by EmitC |
Results: ¶
Result | Description |
---|---|
«unnamed» | type supported by EmitC |
emitc.variable
(emitc::VariableOp) ¶
Variable operation
The emitc.variable
operation produces an SSA value equal to some value
specified by an attribute. This can be used to form simple integer and
floating point variables, as well as more exotic things like tensor
variables. The emitc.variable
operation also supports the EmitC opaque
attribute and the EmitC opaque type. If further supports the EmitC
pointer type, whereas folding is not supported.
The emitc.variable
is emitted as a C/C++ local variable.
Example:
// Integer variable
%0 = "emitc.variable"(){value = 42 : i32} : () -> !emitc.lvalue<i32>
// Variable emitted as `int32_t* = NULL;`
%1 = "emitc.variable"() {value = #emitc.opaque<"NULL">}
: () -> !emitc.lvalue<!emitc.ptr<!emitc.opaque<"int32_t">>>
Since folding is not supported, it can be used with pointers.
As an example, it is valid to create pointers to variable
operations
by using apply
operations and pass these to a call
operation.
%0 = "emitc.variable"() {value = 0 : i32} : () -> !emitc.lvalue<i32>
%1 = "emitc.variable"() {value = 0 : i32} : () -> !emitc.lvalue<i32>
%2 = emitc.apply "&"(%0) : (!emitc.lvalue<i32>) -> !emitc.ptr<i32>
%3 = emitc.apply "&"(%1) : (!emitc.lvalue<i32>) -> !emitc.ptr<i32>
emitc.call_opaque "write"(%2, %3)
: (!emitc.ptr<i32>, !emitc.ptr<i32>) -> ()
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
value | ::mlir::Attribute | An opaque attribute or TypedAttr instance |
Results: ¶
Result | Description |
---|---|
«unnamed» | EmitC array type or EmitC lvalue type |
emitc.verbatim
(emitc::VerbatimOp) ¶
Verbatim operation
Syntax:
operation ::= `emitc.verbatim` $value attr-dict
The emitc.verbatim
operation produces no results and the value is emitted as is
followed by a line break (’\n’ character) during translation.
Note: Use with caution. This operation can have arbitrary effects on the semantics of the emitted code. Use semantically more meaningful operations whenever possible. Additionally this op is NOT intended to be used to inject large snippets of code.
This operation can be used in situations where a more suitable operation is not yet implemented in the dialect or where preprocessor directives interfere with the structure of the code. One example of this is to declare the linkage of external symbols to make the generated code usable in both C and C++ contexts:
#ifdef __cplusplus
extern "C" {
#endif
...
#ifdef __cplusplus
}
#endif
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
value | ::mlir::StringAttr | string attribute |
emitc.yield
(emitc::YieldOp) ¶
Block termination operation
Syntax:
operation ::= `emitc.yield` attr-dict ($result^ `:` type($result))?
The emitc.yield
terminates its parent EmitC op’s region, optionally yielding
an SSA value. The semantics of how the values are yielded is defined by the
parent operation.
If emitc.yield
has an operand, the operand must match the parent operation’s
result. If the parent operation defines no values, then the emitc.yield
may be left out in the custom syntax and the builders will insert one
implicitly. Otherwise, it has to be present in the syntax to indicate which
value is yielded.
Traits: AlwaysSpeculatableImplTrait
, HasParent<ExpressionOp, IfOp, ForOp, SwitchOp>
, Terminator
Interfaces: ConditionallySpeculatable
, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
Operands: ¶
Operand | Description |
---|---|
result | type supported by EmitC |
Attributes ¶
OpaqueAttr ¶
An opaque attribute
Syntax:
#emitc.opaque<
::llvm::StringRef # value
>
An opaque attribute of which the value gets emitted as is.
Example:
#emitc.opaque<"">
#emitc.opaque<"NULL">
#emitc.opaque<"nullptr">
Parameters: ¶
Parameter | C++ type | Description |
---|---|---|
value | ::llvm::StringRef | the opaque value |
Types ¶
ArrayType ¶
EmitC array type
An array data type.
Example:
// Array emitted as `int32_t[10]`
!emitc.array<10xi32>
// Array emitted as `float[10][20]`
!emitc.array<10x20xf32>
Parameters: ¶
Parameter | C++ type | Description |
---|---|---|
shape | ::llvm::ArrayRef<int64_t> | |
elementType | Type |
LValueType ¶
EmitC lvalue type
Syntax:
!emitc.lvalue<
Type # valueType
>
Values of this type can be assigned to and their address can be taken.
Parameters: ¶
Parameter | C++ type | Description |
---|---|---|
valueType | Type |
OpaqueType ¶
EmitC opaque type
Syntax:
!emitc.opaque<
::llvm::StringRef # value
>
An opaque data type of which the value gets emitted as is.
Example:
!emitc.opaque<"int">
!emitc.opaque<"mytype">
!emitc.opaque<"std::vector<std::string>">
Parameters: ¶
Parameter | C++ type | Description |
---|---|---|
value | ::llvm::StringRef | the opaque value |
PointerType ¶
EmitC pointer type
Syntax:
!emitc.ptr<
Type # pointee
>
A pointer data type.
Example:
// Pointer emitted as `int32_t*`
!emitc.ptr<i32>
// Pointer emitted as `float*`
!emitc.ptr<f32>
// Pointer emitted as `int*`
!emitc.ptr<!emitc.opaque<"int">>
Parameters: ¶
Parameter | C++ type | Description |
---|---|---|
pointee | Type |
PtrDiffTType ¶
EmitC signed pointer diff type
Syntax: !emitc.ptrdiff_t
Signed data type as wide as platform-specific pointer types.
In particular, it is as wide as emitc.size_t
.
It corresponds to ptrdiff_t
found in <stddef.h>
.
SignedSizeTType ¶
EmitC signed size type
Syntax: !emitc.ssize_t
Data type representing all values of emitc.size_t
, plus -1.
It corresponds to ssize_t
found in <sys/types.h>
.
Use of this type causes the code to be non-C99 compliant.
SizeTType ¶
EmitC unsigned size type
Syntax: !emitc.size_t
Unsigned data type as wide as platform-specific pointer types.
It corresponds to size_t
found in <stddef.h>
.
Enums ¶
CmpPredicate ¶
allowed 64-bit signless integer cases: 0, 1, 2, 3, 4, 5, 6
Cases: ¶
Symbol | Value | String |
---|---|---|
eq | 0 | eq |
ne | 1 | ne |
lt | 2 | lt |
le | 3 | le |
gt | 4 | gt |
ge | 5 | ge |
three_way | 6 | three_way |