MLIR

Multi-Level IR Compiler Framework

'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: 

OperandDescription
lhsany type
rhsany type

Results: 

ResultDescription
«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: 

AttributeMLIR TypeDescription
applicableOperator::mlir::StringAttrstring attribute

Operands: 

OperandDescription
operandany type

Results: 

ResultDescription
resultany 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: 

AttributeMLIR TypeDescription
callee::mlir::StringAttrstring attribute
args::mlir::ArrayAttrarray attribute
template_args::mlir::ArrayAttrarray attribute

Operands: 

OperandDescription
operandsany type

Results: 

ResultDescription
«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: 

OperandDescription
sourceany type

Results: 

ResultDescription
destany 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: 

AttributeMLIR TypeDescription
predicate::mlir::emitc::CmpPredicateAttrallowed 64-bit signless integer cases: 0, 1, 2, 3, 4, 5, 6

Operands: 

OperandDescription
lhsany type
rhsany type

Results: 

ResultDescription
«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: 

AttributeMLIR TypeDescription
value::mlir::AttributeAn opaque attribute or TypedAttr instance

Results: 

ResultDescription
«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: 

OperandDescription
«unnamed»floating-point or integer or index or EmitC opaque type
«unnamed»floating-point or integer or index or EmitC opaque type

Results: 

ResultDescription
«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: 

AttributeMLIR TypeDescription
include::mlir::StringAttrstring attribute
is_standard_include::mlir::UnitAttrunit 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: 

AttributeMLIR TypeDescription
value::mlir::StringAttrstring attribute

Results: 

ResultDescription
resultany 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: 

OperandDescription
«unnamed»floating-point or integer or index or EmitC opaque type
«unnamed»floating-point or integer or index or EmitC opaque type

Results: 

ResultDescription
«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: 

OperandDescription
«unnamed»integer or index or EmitC opaque type
«unnamed»integer or index or EmitC opaque type

Results: 

ResultDescription
«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: 

OperandDescription
lhsany type
rhsany type

Results: 

ResultDescription
«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: 

AttributeMLIR TypeDescription
value::mlir::AttributeAn opaque attribute or TypedAttr instance

Results: 

ResultDescription
«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: 

ParameterC++ typeDescription
value::llvm::StringRefthe 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: 

ParameterC++ typeDescription
value::llvm::StringRefthe 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: 

ParameterC++ typeDescription
pointeeType