'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
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
operation, C++11 is required. - If floating-point type template arguments are passed to an
emitc.call
operation, C++20 is required. - 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.
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.constant
func.func
func.return
- ‘scf’ Dialect
scf.for
scf.if
scf.yield
- ‘arith’ Dialect
arith.constant
Operation definition ¶
emitc.add
(emitc::AddOp) ¶
Addition operation
Syntax:
operation ::= `emitc.add` operands attr-dict `:` functional-type(operands, results)
With the 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;
Operands: ¶
Operand | Description |
---|---|
lhs | any type |
rhs | any type |
Results: ¶
Result | Description |
---|---|
«unnamed» | any type |
emitc.apply
(emitc::ApplyOp) ¶
Apply operation
Syntax:
operation ::= `emitc.apply` $applicableOperator `(` $operand `)` attr-dict `:` functional-type($operand, results)
With the 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) : (i32) -> !emitc.ptr<i32>
// Generic form of the same operation.
%0 = "emitc.apply"(%arg0) {applicableOperator = "&"}
: (i32) -> !emitc.ptr<i32>
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
applicableOperator | ::mlir::StringAttr | string attribute |
Operands: ¶
Operand | Description |
---|---|
operand | any type |
Results: ¶
Result | Description |
---|---|
result | any type |
emitc.call
(emitc::CallOp) ¶
Call operation
Syntax:
operation ::= `emitc.call` $callee `(` $operands `)` attr-dict `:` functional-type($operands, results)
The call
operation represents a C++ function call. 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 "foo" () : () -> i32
// Generic form of the same operation.
%0 = "emitc.call"() {callee = "foo"} : () -> i32
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 | any type |
Results: ¶
Result | Description |
---|---|
«unnamed» | any type |
emitc.cast
(emitc::CastOp) ¶
Cast operation
Syntax:
operation ::= `emitc.cast` $source attr-dict `:` type($source) `to` type($dest)
The 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: SameOperandsAndResultShape
Interfaces: CastOpInterface
Operands: ¶
Operand | Description |
---|---|
source | any type |
Results: ¶
Result | Description |
---|---|
dest | any type |
emitc.cmp
(emitc::CmpOp) ¶
Comparison operation
Syntax:
operation ::= `emitc.cmp` $predicate `,` operands attr-dict `:` functional-type(operands, results)
With the 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;
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
predicate | ::mlir::emitc::CmpPredicateAttr | allowed 64-bit signless integer cases: 0, 1, 2, 3, 4, 5, 6 |
Operands: ¶
Operand | Description |
---|---|
lhs | any type |
rhs | any type |
Results: ¶
Result | Description |
---|---|
«unnamed» | any type |
emitc.constant
(emitc::ConstantOp) ¶
Constant operation
The 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 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">}
: () -> !emitc.opaque<"char">
Traits: ConstantLike
Attributes: ¶
Attribute | MLIR Type | Description |
---|---|---|
value | ::mlir::Attribute | An opaque attribute or TypedAttr instance |
Results: ¶
Result | Description |
---|---|
«unnamed» | any type |
emitc.div
(emitc::DivOp) ¶
Division operation
Syntax:
operation ::= `emitc.div` operands attr-dict `:` functional-type(operands, results)
With the 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;
Operands: ¶
Operand | Description |
---|---|
«unnamed» | floating-point or integer or index or EmitC opaque type |
«unnamed» | floating-point or integer or index or EmitC opaque type |
Results: ¶
Result | Description |
---|---|
«unnamed» | floating-point or integer or index or EmitC opaque type |
emitc.include
(emitc::IncludeOp) ¶
Include operation
The 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"} : () -> ()
Traits: HasParent
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 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 | any type |
emitc.mul
(emitc::MulOp) ¶
Multiplication operation
Syntax:
operation ::= `emitc.mul` operands attr-dict `:` functional-type(operands, results)
With the 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;
Operands: ¶
Operand | Description |
---|---|
«unnamed» | floating-point or integer or index or EmitC opaque type |
«unnamed» | floating-point or integer or index or EmitC opaque type |
Results: ¶
Result | Description |
---|---|
«unnamed» | floating-point or integer or index or EmitC opaque type |
emitc.rem
(emitc::RemOp) ¶
Remainder operation
Syntax:
operation ::= `emitc.rem` operands attr-dict `:` functional-type(operands, results)
With the 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;
Operands: ¶
Operand | Description |
---|---|
«unnamed» | integer or index or EmitC opaque type |
«unnamed» | integer or index or EmitC opaque type |
Results: ¶
Result | Description |
---|---|
«unnamed» | integer or index or EmitC opaque type |
emitc.sub
(emitc::SubOp) ¶
Subtraction operation
Syntax:
operation ::= `emitc.sub` operands attr-dict `:` functional-type(operands, results)
With the 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.opaque<"ptrdiff_t">
// Code emitted for the operations above.
int32_t v7 = v1 - v2;
float* v8 = v3 - v4;
ptrdiff_t v9 = v5 - v6;
Operands: ¶
Operand | Description |
---|---|
lhs | any type |
rhs | any type |
Results: ¶
Result | Description |
---|---|
«unnamed» | any type |
emitc.variable
(emitc::VariableOp) ¶
Variable operation
The 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 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 variable
is emitted as a C/C++ local variable.
Example:
// Integer variable
%0 = "emitc.variable"(){value = 42 : i32} : () -> i32
// Variable emitted as `int32_t* = NULL;`
%1 = "emitc.variable"()
{value = #emitc.opaque<"NULL"> : !emitc.opaque<"int32_t*">}
: () -> !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} : () -> i32
%1 = "emitc.variable"() {value = 0 : i32} : () -> i32
%2 = emitc.apply "&"(%0) : (i32) -> !emitc.ptr<i32>
%3 = emitc.apply "&"(%1) : (i32) -> !emitc.ptr<i32>
emitc.call "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» | any type |
Attribute definition ¶
OpaqueAttr ¶
An opaque attribute
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 |
Type definition ¶
OpaqueType ¶
EmitC opaque type
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 |