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_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 header sys/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 

source

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: 

OperandDescription
lhstype supported by EmitC
rhstype supported by EmitC

Results: 

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

AttributeMLIR TypeDescription
applicableOperator::mlir::StringAttrstring attribute

Operands: 

OperandDescription
operandtype supported by EmitC or EmitC lvalue type

Results: 

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

OperandDescription
varEmitC lvalue type
valuetype 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: 

OperandDescription
lhstype supported by EmitC
rhstype supported by EmitC

Results: 

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

OperandDescription
lhstype supported by EmitC
rhstype supported by EmitC

Results: 

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

OperandDescription
«unnamed»type supported by EmitC

Results: 

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

OperandDescription
lhstype supported by EmitC
rhstype supported by EmitC

Results: 

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

OperandDescription
lhstype supported by EmitC
rhstype supported by EmitC

Results: 

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

OperandDescription
lhstype supported by EmitC
rhstype supported by EmitC

Results: 

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

AttributeMLIR TypeDescription
callee::mlir::FlatSymbolRefAttrflat symbol reference attribute

Operands: 

OperandDescription
operandsvariadic of type supported by EmitC

Results: 

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

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

Operands: 

OperandDescription
operandsvariadic of type supported by EmitC

Results: 

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

OperandDescription
sourcetype supported by EmitC

Results: 

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

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

Enum cases:

  • eq (eq)
  • ne (ne)
  • lt (lt)
  • le (le)
  • gt (gt)
  • ge (ge)
  • three_way (three_way)

Operands: 

OperandDescription
lhstype supported by EmitC
rhstype supported by EmitC

Results: 

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

OperandDescription
condition1-bit signless integer
true_valuetype supported by EmitC
false_valuetype supported by EmitC

Results: 

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

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

Results: 

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

AttributeMLIR TypeDescription
sym_name::mlir::FlatSymbolRefAttrflat 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: 

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

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

AttributeMLIR TypeDescription
do_not_inline::mlir::UnitAttrunit attribute

Results: 

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

OperandDescription
lowerBoundinteger, index or opaque type supported by EmitC
upperBoundinteger, index or opaque type supported by EmitC
stepinteger, 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: 

AttributeMLIR TypeDescription
sym_name::mlir::StringAttrstring attribute
function_type::mlir::TypeAttrtype attribute of function type
specifiers::mlir::ArrayAttrstring array attribute
arg_attrs::mlir::ArrayAttrArray of dictionary attributes
res_attrs::mlir::ArrayAttrArray 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: 

AttributeMLIR TypeDescription
name::mlir::FlatSymbolRefAttrflat symbol reference attribute

Results: 

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

AttributeMLIR TypeDescription
sym_name::mlir::StringAttrstring attribute
type::mlir::TypeAttrany type attribute
initial_value::mlir::AttributeAn opaque attribute or TypedAttr instance
extern_specifier::mlir::UnitAttrunit attribute
static_specifier::mlir::UnitAttrunit attribute
const_specifier::mlir::UnitAttrunit 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: 

OperandDescription
condition1-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: 

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

AttributeMLIR TypeDescription
value::mlir::StringAttrstring attribute

Results: 

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

OperandDescription
operandEmitC lvalue type

Results: 

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

OperandDescription
lhstype supported by EmitC
rhstype supported by EmitC

Results: 

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

OperandDescription
«unnamed»type supported by EmitC

Results: 

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

OperandDescription
lhstype supported by EmitC
rhstype supported by EmitC

Results: 

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

AttributeMLIR TypeDescription
member::mlir::StringAttrstring attribute

Operands: 

OperandDescription
operandemitc.lvalue of EmitC opaque type values

Results: 

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

AttributeMLIR TypeDescription
member::mlir::StringAttrstring attribute

Operands: 

OperandDescription
operandemitc.lvalue of EmitC opaque type or EmitC pointer type values

Results: 

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

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

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

OperandDescription
«unnamed»integer, index or opaque type supported by EmitC
«unnamed»integer, index or opaque type supported by EmitC

Results: 

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

OperandDescription
operandtype 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: 

OperandDescription
lhstype supported by EmitC
rhstype supported by EmitC

Results: 

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

OperandDescription
valueEmitC array type or EmitC opaque type or EmitC pointer type
indicesvariadic of type supported by EmitC

Results: 

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

AttributeMLIR TypeDescription
cases::mlir::DenseI64ArrayAttri64 dense array attribute

Operands: 

OperandDescription
arginteger, 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: 

OperandDescription
«unnamed»type supported by EmitC

Results: 

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

OperandDescription
«unnamed»type supported by EmitC

Results: 

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

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

Results: 

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

AttributeMLIR TypeDescription
value::mlir::StringAttrstring 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: 

OperandDescription
resulttype 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: 

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

ParameterC++ typeDescription
shape::llvm::ArrayRef<int64_t>
elementTypeType

LValueType 

EmitC lvalue type

Syntax:

!emitc.lvalue<
  Type   # valueType
>

Values of this type can be assigned to and their address can be taken.

Parameters: 

ParameterC++ typeDescription
valueTypeType

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: 

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

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: 

SymbolValueString
eq0eq
ne1ne
lt2lt
le3le
gt4gt
ge5ge
three_way6three_way