[Mlir-commits] [mlir] [mlir][emitc][NFC] Clean up EmitC (PR #152327)
Andrey Timonin
llvmlistbot at llvm.org
Wed Aug 6 08:36:27 PDT 2025
https://github.com/EtoAndruwa created https://github.com/llvm/llvm-project/pull/152327
This MR cleans up EmitC source and test files.
**Changes**:
- `mlir/test/mlir-translate/emitc_classops.mlir` was moved to `mlir/test/Target/Cpp/class.mlir`.
- `mlir/test/Dialect/EmitC/wrap_emitc_func_in_class.mlir` and `mlir/test/Dialect/EmitC/wrap_emitc_func_in_class_noAttr.mlir were moved` to `mlir/test/Dialect/EmitC/transforms.mlir`.
- Test for `emitc.class`, `emitc.field` and `emitc.get_field` was added to `mlir/test/Dialect/EmitC/ops.mlir`.
- Unnecessary `-verify-diagnostics` flags were removed from tests.
- Alphabetical order was restored in `mlir/include/mlir/Dialect/EmitC/IR/EmitC.td` and `mlir/lib/Dialect/EmitC/IR/EmitC.cpp`.
- Case 16 in `bool mlir::emitc::isSupportedFloatType(Type type)` in `EmitC.cpp` was refactored to:
```
case 16:
if (!llvm::isa<Float16Type, BFloat16Type>(type))
return false;
LLVM_FALLTHROUGH;
```
>From 5e86788a48d73521af324384ea32bfa0cdb16074 Mon Sep 17 00:00:00 2001
From: EtoAndruwa <timonina1909 at gmail.com>
Date: Wed, 6 Aug 2025 18:13:50 +0300
Subject: [PATCH] [mlir][emitc][NFC] Clean up EmitC
---
mlir/include/mlir/Dialect/EmitC/IR/EmitC.td | 1486 ++++++++---------
mlir/lib/Dialect/EmitC/IR/EmitC.cpp | 1000 +++++------
mlir/test/Dialect/EmitC/attrs.mlir | 4 +-
mlir/test/Dialect/EmitC/ops.mlir | 12 +
mlir/test/Dialect/EmitC/transforms.mlir | 247 +--
mlir/test/Dialect/EmitC/types.mlir | 6 +-
.../EmitC/wrap_emitc_func_in_class.mlir | 40 -
.../wrap_emitc_func_in_class_noAttr.mlir | 17 -
mlir/test/Target/Cpp/class.mlir | 78 +
mlir/test/mlir-translate/emitc_classops.mlir | 78 -
10 files changed, 1492 insertions(+), 1476 deletions(-)
delete mode 100644 mlir/test/Dialect/EmitC/wrap_emitc_func_in_class.mlir
delete mode 100644 mlir/test/Dialect/EmitC/wrap_emitc_func_in_class_noAttr.mlir
create mode 100644 mlir/test/Target/Cpp/class.mlir
delete mode 100644 mlir/test/mlir-translate/emitc_classops.mlir
diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
index 937b34a625628..8d5ae60c80452 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
@@ -67,52 +67,6 @@ def IntegerIndexOrOpaqueType : Type<CPred<"emitc::isIntegerIndexOrOpaqueType($_s
"integer, index or opaque type supported by EmitC">;
def FloatIntegerIndexOrOpaqueType : AnyTypeOf<[EmitCFloatType, IntegerIndexOrOpaqueType]>;
-def EmitC_FileOp
- : EmitC_Op<"file", [IsolatedFromAbove, NoRegionArguments, SymbolTable,
- OpAsmOpInterface]#GraphRegionNoTerminator.traits> {
- let summary = "A file container operation";
- let description = [{
- A `file` represents a single C/C++ file.
-
- `mlir-translate` ignores the body of all `emitc.file` ops
- unless the `-file-id=id` flag is used. With that flag, all `emitc.file` ops
- with matching id are emitted.
-
- Example:
-
- ```mlir
- emitc.file "main" {
- emitc.func @func_one() {
- emitc.return
- }
- }
- ```
- }];
-
- let arguments = (ins Builtin_StringAttr:$id);
- let regions = (region SizedRegion<1>:$bodyRegion);
-
- let assemblyFormat = "$id attr-dict-with-keyword $bodyRegion";
- let builders = [OpBuilder<(ins CArg<"StringRef">:$id)>];
- let extraClassDeclaration = [{
- /// Construct a file op from the given location with a name.
- static FileOp create(Location loc, StringRef name);
-
- //===------------------------------------------------------------------===//
- // OpAsmOpInterface Methods
- //===------------------------------------------------------------------===//
-
- /// EmitC ops in the body can omit their 'emitc.' prefix in the assembly.
- static ::llvm::StringRef getDefaultDialect() {
- return "emitc";
- }
- }];
-
- // We need to ensure that the body region has a block;
- // the auto-generated builders do not guarantee that.
- let skipDefaultBuilders = 1;
-}
-
def EmitC_AddOp : EmitC_BinaryOp<"add", []> {
let summary = "Addition operation";
let description = [{
@@ -172,6 +126,35 @@ def EmitC_ApplyOp : EmitC_Op<"apply", [CExpressionInterface]> {
let hasVerifier = 1;
}
+def EmitC_AssignOp : EmitC_Op<"assign", []> {
+ let summary = "Assign operation";
+ let description = [{
+ 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:
+
+ ```mlir
+ // 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) -> ()
+ ```
+ }];
+
+ let arguments = (ins
+ Res<EmitC_LValueType, "", [MemWrite<DefaultResource, 1, FullEffect>]>:$var,
+ EmitCType:$value);
+ let results = (outs);
+
+ let hasVerifier = 1;
+ let assemblyFormat = "$value `:` type($value) `to` $var `:` type($var) attr-dict";
+}
+
def EmitC_BitwiseAndOp : EmitC_BinaryOp<"bitwise_and", []> {
let summary = "Bitwise and operation";
let description = [{
@@ -280,6 +263,88 @@ def EmitC_BitwiseXorOp : EmitC_BinaryOp<"bitwise_xor", []> {
}];
}
+def EmitC_CallOp : EmitC_Op<"call",
+ [CallOpInterface, CExpressionInterface,
+ DeclareOpInterfaceMethods<SymbolUserOpInterface>]> {
+ let summary = "Call operation";
+ let description = [{
+ 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:
+
+ ```mlir
+ %2 = emitc.call @my_add(%0, %1) : (f32, f32) -> f32
+ ```
+ }];
+ let arguments = (ins
+ FlatSymbolRefAttr:$callee,
+ Variadic<EmitCType>:$operands,
+ OptionalAttr<DictArrayAttr>:$arg_attrs,
+ OptionalAttr<DictArrayAttr>:$res_attrs
+ );
+
+ let results = (outs Variadic<EmitCType>);
+
+ let builders = [
+ OpBuilder<(ins "FuncOp":$callee, CArg<"ValueRange", "{}">:$operands), [{
+ $_state.addOperands(operands);
+ $_state.addAttribute("callee", SymbolRefAttr::get(callee));
+ $_state.addTypes(callee.getFunctionType().getResults());
+ }]>,
+ OpBuilder<(ins "SymbolRefAttr":$callee, "TypeRange":$results,
+ CArg<"ValueRange", "{}">:$operands), [{
+ $_state.addOperands(operands);
+ $_state.addAttribute("callee", callee);
+ $_state.addTypes(results);
+ }]>,
+ OpBuilder<(ins "StringAttr":$callee, "TypeRange":$results,
+ CArg<"ValueRange", "{}">:$operands), [{
+ build($_builder, $_state, SymbolRefAttr::get(callee), results, operands);
+ }]>,
+ OpBuilder<(ins "StringRef":$callee, "TypeRange":$results,
+ CArg<"ValueRange", "{}">:$operands), [{
+ build($_builder, $_state, StringAttr::get($_builder.getContext(), callee),
+ results, operands);
+ }]>];
+
+ let extraClassDeclaration = [{
+ FunctionType getCalleeType();
+
+ /// Get the argument operands to the called function.
+ operand_range getArgOperands() {
+ return {arg_operand_begin(), arg_operand_end()};
+ }
+
+ MutableOperandRange getArgOperandsMutable() {
+ return getOperandsMutable();
+ }
+
+ operand_iterator arg_operand_begin() { return operand_begin(); }
+ operand_iterator arg_operand_end() { return operand_end(); }
+
+ /// Return the callee of this operation.
+ CallInterfaceCallable getCallableForCallee() {
+ return (*this)->getAttrOfType<SymbolRefAttr>("callee");
+ }
+
+ /// Set the callee for this operation.
+ void setCalleeFromCallable(CallInterfaceCallable callee) {
+ (*this)->setAttr("callee", cast<SymbolRefAttr>(callee));
+ }
+
+ bool hasSideEffects() {
+ return false;
+ }
+ }];
+
+ let assemblyFormat = [{
+ $callee `(` $operands `)` attr-dict `:` functional-type($operands, results)
+ }];
+}
+
def EmitC_CallOpaqueOp : EmitC_Op<"call_opaque", [CExpressionInterface]> {
let summary = "Opaque call operation";
let description = [{
@@ -399,6 +464,42 @@ def EmitC_CmpOp : EmitC_BinaryOp<"cmp", []> {
let assemblyFormat = "$predicate `,` operands attr-dict `:` functional-type(operands, results)";
}
+def EmitC_ConditionalOp : EmitC_Op<"conditional",
+ [AllTypesMatch<["true_value", "false_value", "result"]>, CExpressionInterface]> {
+ let summary = "Conditional (ternary) operation";
+ let description = [{
+ With the `emitc.conditional` operation the ternary conditional operator can
+ be applied.
+
+ Example:
+
+ ```mlir
+ %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
+ ```
+ ```c++
+ // Code emitted for the operations above.
+ bool v3 = v1 > v2;
+ int32_t v4 = 10;
+ int32_t v5 = 11;
+ int32_t v6 = v3 ? v4 : v5;
+ ```
+ }];
+ let arguments = (ins I1:$condition, EmitCType:$true_value, EmitCType:$false_value);
+ let results = (outs EmitCType:$result);
+ let assemblyFormat = "operands attr-dict `:` type($result)";
+
+ let extraClassDeclaration = [{
+ bool hasSideEffects() {
+ return false;
+ }
+ }];
+}
+
def EmitC_ConstantOp : EmitC_Op<"constant", [ConstantLike]> {
let summary = "Constant operation";
let description = [{
@@ -428,43 +529,138 @@ def EmitC_ConstantOp : EmitC_Op<"constant", [ConstantLike]> {
let hasVerifier = 1;
}
-def EmitC_DivOp : EmitC_BinaryOp<"div", []> {
- let summary = "Division operation";
+def EmitC_ClassOp
+ : EmitC_Op<"class", [AutomaticAllocationScope, IsolatedFromAbove,
+ OpAsmOpInterface, SymbolTable,
+ Symbol]#GraphRegionNoTerminator.traits> {
+ let summary =
+ "Represents a C++ class definition, encapsulating fields and methods.";
+
let description = [{
- With the `emitc.div` operation the arithmetic operator / (division) can
- be applied.
+ The `emitc.class` operation defines a C++ class, acting as a container
+ for its data fields (`emitc.field`) and methods (`emitc.func`).
+ It creates a distinct scope, isolating its contents from the surrounding
+ MLIR region, similar to how C++ classes encapsulate their internals.
Example:
```mlir
- // Custom form of the division operation.
- %0 = emitc.div %arg0, %arg1 : (i32, i32) -> i32
- %1 = emitc.div %arg2, %arg3 : (f32, f32) -> f32
- ```
- ```c++
- // Code emitted for the operations above.
- int32_t v5 = v1 / v2;
- float v6 = v3 / v4;
+ emitc.class @modelClass {
+ emitc.field @fieldName0 : !emitc.array<1xf32> = {emitc.opaque = "input_tensor"}
+ emitc.func @execute() {
+ %0 = "emitc.constant"() <{value = 0 : index}> : () -> !emitc.size_t
+ %1 = get_field @fieldName0 : !emitc.array<1xf32>
+ %2 = subscript %1[%0] : (!emitc.array<1xf32>, !emitc.size_t) -> !emitc.lvalue<f32>
+ return
+ }
+ }
+ // Class with a final speciferAdd commentMore actions
+ emitc.class final @modelClass {
+ emitc.field @fieldName0 : !emitc.array<1xf32> = {emitc.opaque = "input_tensor"}
+ emitc.func @execute() {
+ %0 = "emitc.constant"() <{value = 0 : index}> : () -> !emitc.size_t
+ %1 = get_field @fieldName0 : !emitc.array<1xf32>
+ %2 = subscript %1[%0] : (!emitc.array<1xf32>, !emitc.size_t) -> !emitc.lvalue<f32>
+ return
+ }
+ }
```
}];
- let arguments = (ins FloatIntegerIndexOrOpaqueType, FloatIntegerIndexOrOpaqueType);
- let results = (outs FloatIntegerIndexOrOpaqueType);
-}
+ let arguments = (ins SymbolNameAttr:$sym_name, UnitAttr:$final_specifier);
-def EmitC_ExpressionOp : EmitC_Op<"expression",
- [HasOnlyGraphRegion, OpAsmOpInterface,
- SingleBlockImplicitTerminator<"emitc::YieldOp">, NoRegionArguments]> {
- let summary = "Expression operation";
- let description = [{
- 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.
+ let regions = (region AnyRegion:$body);
- 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`.
+ let extraClassDeclaration = [{
+ // Returns the body block containing class members and methods.
+ Block &getBlock();
+ }];
- Example:
+ let hasCustomAssemblyFormat = 1;
+
+ let assemblyFormat =
+ [{ (`final` $final_specifier^)? $sym_name attr-dict-with-keyword $body }];
+}
+
+def EmitC_DeclareFuncOp : EmitC_Op<"declare_func", [
+ DeclareOpInterfaceMethods<SymbolUserOpInterface>
+]> {
+ let summary = "An operation to declare a function";
+ let description = [{
+ 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:
+
+ ```mlir
+ 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
+ }
+ ```
+
+ ```c++
+ // 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;
+ }
+ ```
+ }];
+ let arguments = (ins FlatSymbolRefAttr:$sym_name);
+ let assemblyFormat = [{
+ $sym_name attr-dict
+ }];
+}
+
+def EmitC_DivOp : EmitC_BinaryOp<"div", []> {
+ let summary = "Division operation";
+ let description = [{
+ With the `emitc.div` operation the arithmetic operator / (division) can
+ be applied.
+
+ Example:
+
+ ```mlir
+ // Custom form of the division operation.
+ %0 = emitc.div %arg0, %arg1 : (i32, i32) -> i32
+ %1 = emitc.div %arg2, %arg3 : (f32, f32) -> f32
+ ```
+ ```c++
+ // Code emitted for the operations above.
+ int32_t v5 = v1 / v2;
+ float v6 = v3 / v4;
+ ```
+ }];
+
+ let arguments = (ins FloatIntegerIndexOrOpaqueType, FloatIntegerIndexOrOpaqueType);
+ let results = (outs FloatIntegerIndexOrOpaqueType);
+}
+
+def EmitC_ExpressionOp : EmitC_Op<"expression",
+ [HasOnlyGraphRegion, OpAsmOpInterface,
+ SingleBlockImplicitTerminator<"emitc::YieldOp">, NoRegionArguments]> {
+ let summary = "Expression operation";
+ let description = [{
+ 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:
```mlir
%r = emitc.expression : i32 {
@@ -519,6 +715,85 @@ def EmitC_ExpressionOp : EmitC_Op<"expression",
}];
}
+def EmitC_FieldOp : EmitC_Op<"field", [Symbol]> {
+ let summary = "A field within a class";
+ let description = [{
+ The `emitc.field` operation declares a named field within an `emitc.class`
+ operation. The field's type must be an EmitC type.
+
+ Example:
+
+ ```mlir
+ // Example with an attribute:
+ emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.opaque = "another_feature"}
+ // Example with no attribute:
+ emitc.field @fieldName0 : !emitc.array<1xf32>
+ // Example with an initial value:
+ emitc.field @fieldName0 : !emitc.array<1xf32> = dense<0.0>
+ // Example with an initial value and attributes:
+ emitc.field @fieldName0 : !emitc.array<1xf32> = dense<0.0> {
+ emitc.opaque = "input_tensor"}
+ ```
+ }];
+
+ let arguments = (ins SymbolNameAttr:$sym_name, TypeAttr:$type,
+ OptionalAttr<EmitC_OpaqueOrTypedAttr>:$initial_value);
+
+ let assemblyFormat = [{
+ $sym_name
+ `:` custom<EmitCFieldOpTypeAndInitialValue>($type, $initial_value)
+ attr-dict
+ }];
+
+ let hasVerifier = 1;
+}
+
+def EmitC_FileOp
+ : EmitC_Op<"file", [IsolatedFromAbove, NoRegionArguments, SymbolTable,
+ OpAsmOpInterface]#GraphRegionNoTerminator.traits> {
+ let summary = "A file container operation";
+ let description = [{
+ A `file` represents a single C/C++ file.
+
+ `mlir-translate` ignores the body of all `emitc.file` ops
+ unless the `-file-id=id` flag is used. With that flag, all `emitc.file` ops
+ with matching id are emitted.
+
+ Example:
+
+ ```mlir
+ emitc.file "main" {
+ emitc.func @func_one() {
+ emitc.return
+ }
+ }
+ ```
+ }];
+
+ let arguments = (ins Builtin_StringAttr:$id);
+ let regions = (region SizedRegion<1>:$bodyRegion);
+
+ let assemblyFormat = "$id attr-dict-with-keyword $bodyRegion";
+ let builders = [OpBuilder<(ins CArg<"StringRef">:$id)>];
+ let extraClassDeclaration = [{
+ /// Construct a file op from the given location with a name.
+ static FileOp create(Location loc, StringRef name);
+
+ //===------------------------------------------------------------------===//
+ // OpAsmOpInterface Methods
+ //===------------------------------------------------------------------===//
+
+ /// EmitC ops in the body can omit their 'emitc.' prefix in the assembly.
+ static ::llvm::StringRef getDefaultDialect() {
+ return "emitc";
+ }
+ }];
+
+ // We need to ensure that the body region has a block;
+ // the auto-generated builders do not guarantee that.
+ let skipDefaultBuilders = 1;
+}
+
def EmitC_ForOp : EmitC_Op<"for",
[AllTypesMatch<["lowerBound", "upperBound", "step"]>,
OpAsmOpInterface, SingleBlockImplicitTerminator<"emitc::YieldOp">,
@@ -589,172 +864,48 @@ def EmitC_ForOp : EmitC_Op<"for",
let hasRegionVerifier = 1;
}
-def EmitC_CallOp : EmitC_Op<"call",
- [CallOpInterface, CExpressionInterface,
- DeclareOpInterfaceMethods<SymbolUserOpInterface>]> {
- let summary = "Call operation";
+def EmitC_FuncOp : EmitC_Op<"func", [
+ AutomaticAllocationScope,
+ FunctionOpInterface, IsolatedFromAbove, OpAsmOpInterface
+]> {
+ let summary = "An operation with a name containing a single `SSACFG` region";
let description = [{
- 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".
+ 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:
```mlir
- %2 = emitc.call @my_add(%0, %1) : (f32, f32) -> f32
- ```
- }];
- let arguments = (ins
- FlatSymbolRefAttr:$callee,
- Variadic<EmitCType>:$operands,
- OptionalAttr<DictArrayAttr>:$arg_attrs,
- OptionalAttr<DictArrayAttr>:$res_attrs
- );
-
- let results = (outs Variadic<EmitCType>);
-
- let builders = [
- OpBuilder<(ins "FuncOp":$callee, CArg<"ValueRange", "{}">:$operands), [{
- $_state.addOperands(operands);
- $_state.addAttribute("callee", SymbolRefAttr::get(callee));
- $_state.addTypes(callee.getFunctionType().getResults());
- }]>,
- OpBuilder<(ins "SymbolRefAttr":$callee, "TypeRange":$results,
- CArg<"ValueRange", "{}">:$operands), [{
- $_state.addOperands(operands);
- $_state.addAttribute("callee", callee);
- $_state.addTypes(results);
- }]>,
- OpBuilder<(ins "StringAttr":$callee, "TypeRange":$results,
- CArg<"ValueRange", "{}">:$operands), [{
- build($_builder, $_state, SymbolRefAttr::get(callee), results, operands);
- }]>,
- OpBuilder<(ins "StringRef":$callee, "TypeRange":$results,
- CArg<"ValueRange", "{}">:$operands), [{
- build($_builder, $_state, StringAttr::get($_builder.getContext(), callee),
- results, operands);
- }]>];
-
- let extraClassDeclaration = [{
- FunctionType getCalleeType();
-
- /// Get the argument operands to the called function.
- operand_range getArgOperands() {
- return {arg_operand_begin(), arg_operand_end()};
- }
-
- MutableOperandRange getArgOperandsMutable() {
- return getOperandsMutable();
+ // A function with no results:
+ emitc.func @foo(%arg0 : i32) {
+ emitc.call_opaque "bar" (%arg0) : (i32) -> ()
+ emitc.return
}
- operand_iterator arg_operand_begin() { return operand_begin(); }
- operand_iterator arg_operand_end() { return operand_end(); }
-
- /// Return the callee of this operation.
- CallInterfaceCallable getCallableForCallee() {
- return (*this)->getAttrOfType<SymbolRefAttr>("callee");
+ // A function with its argument as single result:
+ emitc.func @foo(%arg0 : i32) -> i32 {
+ emitc.return %arg0 : i32
}
- /// Set the callee for this operation.
- void setCalleeFromCallable(CallInterfaceCallable callee) {
- (*this)->setAttr("callee", cast<SymbolRefAttr>(callee));
+ // 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
}
- bool hasSideEffects() {
- return false;
- }
- }];
-
- let assemblyFormat = [{
- $callee `(` $operands `)` attr-dict `:` functional-type($operands, results)
- }];
-}
-
-def EmitC_DeclareFuncOp : EmitC_Op<"declare_func", [
- DeclareOpInterfaceMethods<SymbolUserOpInterface>
-]> {
- let summary = "An operation to declare a function";
- let description = [{
- 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:
-
- ```mlir
- 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
- }
- ```
-
- ```c++
- // 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;
- }
- ```
- }];
- let arguments = (ins FlatSymbolRefAttr:$sym_name);
- let assemblyFormat = [{
- $sym_name attr-dict
- }];
-}
-
-def EmitC_FuncOp : EmitC_Op<"func", [
- AutomaticAllocationScope,
- FunctionOpInterface, IsolatedFromAbove, OpAsmOpInterface
-]> {
- let summary = "An operation with a name containing a single `SSACFG` region";
- let description = [{
- 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:
-
- ```mlir
- // 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"]}
- ```
+ // An external function definition:
+ emitc.func private @extern_func(i32)
+ attributes {specifiers = ["extern"]}
+ ```
}];
let arguments = (ins SymbolNameAttr:$sym_name,
TypeAttrOf<FunctionType>:$function_type,
@@ -797,28 +948,162 @@ def EmitC_FuncOp : EmitC_Op<"func", [
let hasVerifier = 1;
}
-def EmitC_ReturnOp : EmitC_Op<"return", [Pure, HasParent<"FuncOp">,
- ReturnLike, Terminator]> {
- let summary = "Function return operation";
+def EmitC_GetFieldOp
+ : EmitC_Op<"get_field", [Pure, DeclareOpInterfaceMethods<
+ SymbolUserOpInterface>]> {
+ let summary = "Obtain access to a field within a class instance";
let description = [{
- 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.
+ The `emitc.get_field` operation retrieves the lvalue of a
+ named field from a given class instance.
+
+ Example:
+
+ ```mlir
+ %0 = get_field @fieldName0 : !emitc.array<1xf32>
+ ```
+ }];
+
+ let arguments = (ins FlatSymbolRefAttr:$field_name);
+ let results = (outs EmitCType:$result);
+ let assemblyFormat = "$field_name `:` type($result) attr-dict";
+}
+
+def EmitC_GetGlobalOp : EmitC_Op<"get_global",
+ [Pure, DeclareOpInterfaceMethods<SymbolUserOpInterface>]> {
+ let summary = "Obtain access to a global variable";
+ let description = [{
+ 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:
+
+ ```mlir
+ %x = emitc.get_global @foo : !emitc.array<2xf32>
+ %y = emitc.get_global @bar : !emitc.lvalue<i32>
+ ```
+ }];
+
+ let arguments = (ins FlatSymbolRefAttr:$name);
+ let results = (outs AnyTypeOf<[EmitC_ArrayType, EmitC_LValueType]>:$result);
+ let assemblyFormat = "$name `:` type($result) attr-dict";
+}
+
+def EmitC_GlobalOp : EmitC_Op<"global", [Symbol]> {
+ let summary = "A global variable";
+ let description = [{
+ The `emitc.global` operation declares or defines a named global variable.
+ The backing memory for the variable is allocated statically and described by
+ the variable's type, which must be an EmitC type.
+ 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:
```mlir
- emitc.func @foo() -> (i32) {
+ // 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]>
+ // Global variable with an opaque initial value.
+ emitc.global @x : !emitc.opaque<"char"> = #emitc.opaque<"CHAR_MIN">
+ // External global variable
+ emitc.global extern @x : !emitc.array<2xf32>
+ // Constant global variable with internal linkage
+ emitc.global static const @x : i32 = 0
+ ```
+ }];
+
+ let arguments = (ins SymbolNameAttr:$sym_name,
+ TypeAttr:$type,
+ OptionalAttr<EmitC_OpaqueOrTypedAttr>:$initial_value,
+ UnitAttr:$extern_specifier,
+ UnitAttr:$static_specifier,
+ UnitAttr:$const_specifier);
+
+ let assemblyFormat = [{
+ (`extern` $extern_specifier^)?
+ (`static` $static_specifier^)?
+ (`const` $const_specifier^)?
+ $sym_name
+ `:` custom<EmitCGlobalOpTypeAndInitialValue>($type, $initial_value)
+ attr-dict
+ }];
+
+ let hasVerifier = 1;
+}
+
+def EmitC_IfOp : EmitC_Op<"if",
+ [DeclareOpInterfaceMethods<RegionBranchOpInterface, [
+ "getNumRegionInvocations", "getRegionInvocationBounds",
+ "getEntrySuccessorRegions"]>, OpAsmOpInterface, SingleBlock,
+ SingleBlockImplicitTerminator<"emitc::YieldOp">,
+ RecursiveMemoryEffects, NoRegionArguments]> {
+ let summary = "If-then-else operation";
+ let description = [{
+ 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:
+
+ ```mlir
+ emitc.if %b {
+ ...
+ } else {
...
- emitc.return %0 : i32
}
```
+
+ 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.
}];
- let arguments = (ins Optional<EmitCType>:$operand);
+ let arguments = (ins I1:$condition);
+ let results = (outs);
+ let regions = (region SizedRegion<1>:$thenRegion,
+ MaxSizedRegion<1>:$elseRegion);
- let assemblyFormat = "attr-dict ($operand^ `:` type($operand))?";
- let hasVerifier = 1;
+ let skipDefaultBuilders = 1;
+ let builders = [
+ OpBuilder<(ins "Value":$cond)>,
+ OpBuilder<(ins "Value":$cond, "bool":$addThenBlock, "bool":$addElseBlock)>,
+ OpBuilder<(ins "Value":$cond, "bool":$withElseRegion)>,
+ OpBuilder<(ins "Value":$cond,
+ CArg<"function_ref<void(OpBuilder &, Location)>",
+ "buildTerminatedBody">:$thenBuilder,
+ CArg<"function_ref<void(OpBuilder &, Location)>",
+ "nullptr">:$elseBuilder)>,
+ ];
+
+ let extraClassDeclaration = [{
+ OpBuilder getThenBodyBuilder(OpBuilder::Listener *listener = nullptr) {
+ Block* body = getBody(0);
+ return OpBuilder::atBlockEnd(body, listener);
+ }
+ OpBuilder getElseBodyBuilder(OpBuilder::Listener *listener = nullptr) {
+ Block* body = getBody(1);
+ return OpBuilder::atBlockEnd(body, listener);
+ }
+
+ //===------------------------------------------------------------------===//
+ // OpAsmOpInterface Methods
+ //===------------------------------------------------------------------===//
+
+ /// EmitC ops in the body can omit their 'emitc.' prefix in the assembly.
+ static ::llvm::StringRef getDefaultDialect() {
+ return "emitc";
+ }
+ }];
+ let hasCustomAssemblyFormat = 1;
}
def EmitC_IncludeOp
@@ -876,69 +1161,6 @@ def EmitC_LiteralOp : EmitC_Op<"literal", [Pure]> {
let assemblyFormat = "$value attr-dict `:` type($result)";
}
-def EmitC_LogicalAndOp : EmitC_BinaryOp<"logical_and", []> {
- let summary = "Logical and operation";
- let description = [{
- With the `emitc.logical_and` operation the logical operator && (and) can
- be applied.
-
- Example:
-
- ```mlir
- %0 = emitc.logical_and %arg0, %arg1 : i32, i32
- ```
- ```c++
- // Code emitted for the operation above.
- bool v3 = v1 && v2;
- ```
- }];
-
- let results = (outs I1);
- let assemblyFormat = "operands attr-dict `:` type(operands)";
-}
-
-def EmitC_LogicalNotOp : EmitC_UnaryOp<"logical_not", []> {
- let summary = "Logical not operation";
- let description = [{
- With the `emitc.logical_not` operation the logical operator ! (negation) can
- be applied.
-
- Example:
-
- ```mlir
- %0 = emitc.logical_not %arg0 : i32
- ```
- ```c++
- // Code emitted for the operation above.
- bool v2 = !v1;
- ```
- }];
-
- let results = (outs I1);
- let assemblyFormat = "operands attr-dict `:` type(operands)";
-}
-
-def EmitC_LogicalOrOp : EmitC_BinaryOp<"logical_or", []> {
- let summary = "Logical or operation";
- let description = [{
- With the `emitc.logical_or` operation the logical operator || (inclusive or)
- can be applied.
-
- Example:
-
- ```mlir
- %0 = emitc.logical_or %arg0, %arg1 : i32, i32
- ```
- ```c++
- // Code emitted for the operation above.
- bool v3 = v1 || v2;
- ```
- }];
-
- let results = (outs I1);
- let assemblyFormat = "operands attr-dict `:` type(operands)";
-}
-
def EmitC_LoadOp : EmitC_Op<"load", [CExpressionInterface,
TypesMatchWith<"result type matches value type of 'operand'",
"operand", "result",
@@ -946,8 +1168,8 @@ def EmitC_LoadOp : EmitC_Op<"load", [CExpressionInterface,
]> {
let summary = "Load an lvalue into an SSA value.";
let description = [{
- 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
+ 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:
@@ -961,83 +1183,74 @@ def EmitC_LoadOp : EmitC_Op<"load", [CExpressionInterface,
```
}];
- let arguments = (ins
+ let arguments = (ins
Res<EmitC_LValueType, "", [MemRead<DefaultResource, 0, FullEffect>]>:$operand);
let results = (outs AnyType:$result);
- let assemblyFormat = "$operand attr-dict `:` type($operand)";
+ let assemblyFormat = "$operand attr-dict `:` type($operand)";
}
-def EmitC_MulOp : EmitC_BinaryOp<"mul", []> {
- let summary = "Multiplication operation";
+def EmitC_LogicalAndOp : EmitC_BinaryOp<"logical_and", []> {
+ let summary = "Logical and operation";
let description = [{
- With the `emitc.mul` operation the arithmetic operator * (multiplication) can
+ With the `emitc.logical_and` operation the logical operator && (and) can
be applied.
Example:
```mlir
- // Custom form of the multiplication operation.
- %0 = emitc.mul %arg0, %arg1 : (i32, i32) -> i32
- %1 = emitc.mul %arg2, %arg3 : (f32, f32) -> f32
+ %0 = emitc.logical_and %arg0, %arg1 : i32, i32
```
```c++
- // Code emitted for the operations above.
- int32_t v5 = v1 * v2;
- float v6 = v3 * v4;
+ // Code emitted for the operation above.
+ bool v3 = v1 && v2;
```
}];
- let arguments = (ins FloatIntegerIndexOrOpaqueType, FloatIntegerIndexOrOpaqueType);
- let results = (outs FloatIntegerIndexOrOpaqueType);
+ let results = (outs I1);
+ let assemblyFormat = "operands attr-dict `:` type(operands)";
}
-def EmitC_RemOp : EmitC_BinaryOp<"rem", []> {
- let summary = "Remainder operation";
+def EmitC_LogicalNotOp : EmitC_UnaryOp<"logical_not", []> {
+ let summary = "Logical not operation";
let description = [{
- With the `emitc.rem` operation the arithmetic operator % (remainder) can
+ With the `emitc.logical_not` operation the logical operator ! (negation) can
be applied.
Example:
```mlir
- // Custom form of the remainder operation.
- %0 = emitc.rem %arg0, %arg1 : (i32, i32) -> i32
+ %0 = emitc.logical_not %arg0 : i32
```
```c++
// Code emitted for the operation above.
- int32_t v5 = v1 % v2;
+ bool v2 = !v1;
```
}];
- let arguments = (ins IntegerIndexOrOpaqueType, IntegerIndexOrOpaqueType);
- let results = (outs IntegerIndexOrOpaqueType);
+ let results = (outs I1);
+ let assemblyFormat = "operands attr-dict `:` type(operands)";
}
-def EmitC_SubOp : EmitC_BinaryOp<"sub", []> {
- let summary = "Subtraction operation";
+def EmitC_LogicalOrOp : EmitC_BinaryOp<"logical_or", []> {
+ let summary = "Logical or operation";
let description = [{
- With the `emitc.sub` operation the arithmetic operator - (subtraction) can
- be applied.
+ With the `emitc.logical_or` operation the logical operator || (inclusive or)
+ can be applied.
Example:
```mlir
- // 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
+ %0 = emitc.logical_or %arg0, %arg1 : i32, i32
```
```c++
- // Code emitted for the operations above.
- int32_t v7 = v1 - v2;
- float* v8 = v3 - v4;
- ptrdiff_t v9 = v5 - v6;
+ // Code emitted for the operation above.
+ bool v3 = v1 || v2;
```
}];
- let hasVerifier = 1;
+ let results = (outs I1);
+ let assemblyFormat = "operands attr-dict `:` type(operands)";
}
def EmitC_MemberOp : EmitC_Op<"member"> {
@@ -1083,371 +1296,100 @@ def EmitC_MemberOfPtrOp : EmitC_Op<"member_of_ptr"> {
let results = (outs EmitC_LValueOf<[EmitCType]>);
}
-def EmitC_ConditionalOp : EmitC_Op<"conditional",
- [AllTypesMatch<["true_value", "false_value", "result"]>, CExpressionInterface]> {
- let summary = "Conditional (ternary) operation";
+def EmitC_MulOp : EmitC_BinaryOp<"mul", []> {
+ let summary = "Multiplication operation";
let description = [{
- With the `emitc.conditional` operation the ternary conditional operator can
+ With the `emitc.mul` operation the arithmetic operator * (multiplication) can
be applied.
Example:
```mlir
- %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
+ // Custom form of the multiplication operation.
+ %0 = emitc.mul %arg0, %arg1 : (i32, i32) -> i32
+ %1 = emitc.mul %arg2, %arg3 : (f32, f32) -> f32
```
```c++
// Code emitted for the operations above.
- bool v3 = v1 > v2;
- int32_t v4 = 10;
- int32_t v5 = 11;
- int32_t v6 = v3 ? v4 : v5;
- ```
- }];
- let arguments = (ins I1:$condition, EmitCType:$true_value, EmitCType:$false_value);
- let results = (outs EmitCType:$result);
- let assemblyFormat = "operands attr-dict `:` type($result)";
-
- let extraClassDeclaration = [{
- bool hasSideEffects() {
- return false;
- }
- }];
-}
-
-def EmitC_UnaryMinusOp : EmitC_UnaryOp<"unary_minus", []> {
- let summary = "Unary minus operation";
- let description = [{
- With the `emitc.unary_minus` operation the unary operator - (minus) can be
- applied.
-
- Example:
-
- ```mlir
- %0 = emitc.unary_minus %arg0 : (i32) -> i32
- ```
- ```c++
- // Code emitted for the operation above.
- int32_t v2 = -v1;
- ```
- }];
-}
-
-def EmitC_UnaryPlusOp : EmitC_UnaryOp<"unary_plus", []> {
- let summary = "Unary plus operation";
- let description = [{
- With the `emitc.unary_plus` operation the unary operator + (plus) can be
- applied.
-
- Example:
-
- ```mlir
- %0 = emitc.unary_plus %arg0 : (i32) -> i32
- ```
- ```c++
- // Code emitted for the operation above.
- int32_t v2 = +v1;
- ```
- }];
-}
-
-def EmitC_VariableOp : EmitC_Op<"variable", []> {
- let summary = "Variable operation";
- let description = [{
- 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:
-
- ```mlir
- // 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.
- ```mlir
- %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>) -> ()
- ```
- }];
-
- let arguments = (ins EmitC_OpaqueOrTypedAttr:$value);
- let results = (outs Res<AnyTypeOf<[EmitC_ArrayType, EmitC_LValueType]>, "",
- [MemAlloc<DefaultResource, 0, FullEffect>]>);
-
- let hasVerifier = 1;
-}
-
-def EmitC_GlobalOp : EmitC_Op<"global", [Symbol]> {
- let summary = "A global variable";
- let description = [{
- The `emitc.global` operation declares or defines a named global variable.
- The backing memory for the variable is allocated statically and described by
- the variable's type, which must be an EmitC type.
- 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:
-
- ```mlir
- // 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]>
- // Global variable with an opaque initial value.
- emitc.global @x : !emitc.opaque<"char"> = #emitc.opaque<"CHAR_MIN">
- // External global variable
- emitc.global extern @x : !emitc.array<2xf32>
- // Constant global variable with internal linkage
- emitc.global static const @x : i32 = 0
+ int32_t v5 = v1 * v2;
+ float v6 = v3 * v4;
```
}];
- let arguments = (ins SymbolNameAttr:$sym_name,
- TypeAttr:$type,
- OptionalAttr<EmitC_OpaqueOrTypedAttr>:$initial_value,
- UnitAttr:$extern_specifier,
- UnitAttr:$static_specifier,
- UnitAttr:$const_specifier);
-
- let assemblyFormat = [{
- (`extern` $extern_specifier^)?
- (`static` $static_specifier^)?
- (`const` $const_specifier^)?
- $sym_name
- `:` custom<EmitCGlobalOpTypeAndInitialValue>($type, $initial_value)
- attr-dict
- }];
-
- let hasVerifier = 1;
-}
-
-def EmitC_GetGlobalOp : EmitC_Op<"get_global",
- [Pure, DeclareOpInterfaceMethods<SymbolUserOpInterface>]> {
- let summary = "Obtain access to a global variable";
- let description = [{
- 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:
-
- ```mlir
- %x = emitc.get_global @foo : !emitc.array<2xf32>
- %y = emitc.get_global @bar : !emitc.lvalue<i32>
- ```
- }];
-
- let arguments = (ins FlatSymbolRefAttr:$name);
- let results = (outs AnyTypeOf<[EmitC_ArrayType, EmitC_LValueType]>:$result);
- let assemblyFormat = "$name `:` type($result) attr-dict";
+ let arguments = (ins FloatIntegerIndexOrOpaqueType, FloatIntegerIndexOrOpaqueType);
+ let results = (outs FloatIntegerIndexOrOpaqueType);
}
-def EmitC_VerbatimOp : EmitC_Op<"verbatim"> {
- let summary = "Verbatim operation";
+def EmitC_RemOp : EmitC_BinaryOp<"rem", []> {
+ let summary = "Remainder operation";
let description = [{
- 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:
-
- ```c++
- #ifdef __cplusplus
- extern "C" {
- #endif
-
- ...
-
- #ifdef __cplusplus
- }
- #endif
- ```
-
- If the `emitc.verbatim` op has operands, then the `value` is interpreted as
- format string, where `{}` is a placeholder for an operand in their order.
- For example, `emitc.verbatim "#pragma my src={} dst={}" %src, %dest : i32, i32`
- would be emitted as `#pragma my src=a dst=b` if `%src` became `a` and
- `%dest` became `b` in the C code.
- `{{` in the format string is interpreted as a single `{` and doesn't introduce
- a placeholder.
+ With the `emitc.rem` operation the arithmetic operator % (remainder) can
+ be applied.
Example:
```mlir
- emitc.verbatim "typedef float f32;"
- emitc.verbatim "#pragma my var={} property" args %arg : f32
+ // Custom form of the remainder operation.
+ %0 = emitc.rem %arg0, %arg1 : (i32, i32) -> i32
```
```c++
// Code emitted for the operation above.
- typedef float f32;
- #pragma my var=v1 property
- ```
- }];
-
- let extraClassDeclaration = [{
- FailureOr<SmallVector<::mlir::emitc::ReplacementItem>> parseFormatString();
- }];
-
- let arguments = (ins StrAttr:$value, Variadic<AnyTypeOf<[EmitCType, EmitC_LValueType]>>:$fmtArgs);
-
- let builders = [OpBuilder<(ins "::mlir::StringAttr":$value),
- [{ build($_builder, $_state, value, {}); }]>];
- let builders = [OpBuilder<(ins "::llvm::StringRef":$value),
- [{ build($_builder, $_state, value, {}); }]>];
- let hasVerifier = 1;
- let assemblyFormat =
- "$value (`args` $fmtArgs^ `:` type($fmtArgs))? attr-dict";
-}
-
-def EmitC_AssignOp : EmitC_Op<"assign", []> {
- let summary = "Assign operation";
- let description = [{
- 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:
-
- ```mlir
- // 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) -> ()
+ int32_t v5 = v1 % v2;
```
}];
- let arguments = (ins
- Res<EmitC_LValueType, "", [MemWrite<DefaultResource, 1, FullEffect>]>:$var,
- EmitCType:$value);
- let results = (outs);
-
- let hasVerifier = 1;
- let assemblyFormat = "$value `:` type($value) `to` $var `:` type($var) attr-dict";
-}
-
-def EmitC_YieldOp : EmitC_Op<"yield",
- [Pure, Terminator, ParentOneOf<["ExpressionOp", "IfOp", "ForOp", "SwitchOp"]>]> {
- let summary = "Block termination operation";
- let description = [{
- 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.
- }];
-
- let arguments = (ins Optional<EmitCType>:$result);
- let builders = [OpBuilder<(ins), [{ /* nothing to do */ }]>];
-
- let hasVerifier = 1;
- let assemblyFormat = [{ attr-dict ($result^ `:` type($result))? }];
+ let arguments = (ins IntegerIndexOrOpaqueType, IntegerIndexOrOpaqueType);
+ let results = (outs IntegerIndexOrOpaqueType);
}
-def EmitC_IfOp : EmitC_Op<"if",
- [DeclareOpInterfaceMethods<RegionBranchOpInterface, [
- "getNumRegionInvocations", "getRegionInvocationBounds",
- "getEntrySuccessorRegions"]>, OpAsmOpInterface, SingleBlock,
- SingleBlockImplicitTerminator<"emitc::YieldOp">,
- RecursiveMemoryEffects, NoRegionArguments]> {
- let summary = "If-then-else operation";
+def EmitC_ReturnOp : EmitC_Op<"return", [Pure, HasParent<"FuncOp">,
+ ReturnLike, Terminator]> {
+ let summary = "Function return operation";
let description = [{
- 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:
-
- ```mlir
- 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.
- }];
- let arguments = (ins I1:$condition);
- let results = (outs);
- let regions = (region SizedRegion<1>:$thenRegion,
- MaxSizedRegion<1>:$elseRegion);
-
- let skipDefaultBuilders = 1;
- let builders = [
- OpBuilder<(ins "Value":$cond)>,
- OpBuilder<(ins "Value":$cond, "bool":$addThenBlock, "bool":$addElseBlock)>,
- OpBuilder<(ins "Value":$cond, "bool":$withElseRegion)>,
- OpBuilder<(ins "Value":$cond,
- CArg<"function_ref<void(OpBuilder &, Location)>",
- "buildTerminatedBody">:$thenBuilder,
- CArg<"function_ref<void(OpBuilder &, Location)>",
- "nullptr">:$elseBuilder)>,
- ];
+ 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.
- let extraClassDeclaration = [{
- OpBuilder getThenBodyBuilder(OpBuilder::Listener *listener = nullptr) {
- Block* body = getBody(0);
- return OpBuilder::atBlockEnd(body, listener);
- }
- OpBuilder getElseBodyBuilder(OpBuilder::Listener *listener = nullptr) {
- Block* body = getBody(1);
- return OpBuilder::atBlockEnd(body, listener);
+ Example:
+
+ ```mlir
+ emitc.func @foo() -> (i32) {
+ ...
+ emitc.return %0 : i32
}
+ ```
+ }];
+ let arguments = (ins Optional<EmitCType>:$operand);
- //===------------------------------------------------------------------===//
- // OpAsmOpInterface Methods
- //===------------------------------------------------------------------===//
+ let assemblyFormat = "attr-dict ($operand^ `:` type($operand))?";
+ let hasVerifier = 1;
+}
- /// EmitC ops in the body can omit their 'emitc.' prefix in the assembly.
- static ::llvm::StringRef getDefaultDialect() {
- return "emitc";
- }
+def EmitC_SubOp : EmitC_BinaryOp<"sub", []> {
+ let summary = "Subtraction operation";
+ let description = [{
+ With the `emitc.sub` operation the arithmetic operator - (subtraction) can
+ be applied.
+
+ Example:
+
+ ```mlir
+ // 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
+ ```
+ ```c++
+ // Code emitted for the operations above.
+ int32_t v7 = v1 - v2;
+ float* v8 = v3 - v4;
+ ptrdiff_t v9 = v5 - v6;
+ ```
}];
- let hasCustomAssemblyFormat = 1;
+
+ let hasVerifier = 1;
}
def EmitC_SubscriptOp : EmitC_Op<"subscript", []> {
@@ -1593,110 +1535,168 @@ def EmitC_SwitchOp : EmitC_Op<"switch", [RecursiveMemoryEffects,
let hasVerifier = 1;
}
-def EmitC_ClassOp
- : EmitC_Op<"class", [AutomaticAllocationScope, IsolatedFromAbove,
- OpAsmOpInterface, SymbolTable,
- Symbol]#GraphRegionNoTerminator.traits> {
- let summary =
- "Represents a C++ class definition, encapsulating fields and methods.";
+def EmitC_UnaryMinusOp : EmitC_UnaryOp<"unary_minus", []> {
+ let summary = "Unary minus operation";
+ let description = [{
+ With the `emitc.unary_minus` operation the unary operator - (minus) can be
+ applied.
+
+ Example:
+
+ ```mlir
+ %0 = emitc.unary_minus %arg0 : (i32) -> i32
+ ```
+ ```c++
+ // Code emitted for the operation above.
+ int32_t v2 = -v1;
+ ```
+ }];
+}
+def EmitC_UnaryPlusOp : EmitC_UnaryOp<"unary_plus", []> {
+ let summary = "Unary plus operation";
let description = [{
- The `emitc.class` operation defines a C++ class, acting as a container
- for its data fields (`emitc.field`) and methods (`emitc.func`).
- It creates a distinct scope, isolating its contents from the surrounding
- MLIR region, similar to how C++ classes encapsulate their internals.
+ With the `emitc.unary_plus` operation the unary operator + (plus) can be
+ applied.
Example:
```mlir
- emitc.class @modelClass {
- emitc.field @fieldName0 : !emitc.array<1xf32> = {emitc.opaque = "input_tensor"}
- emitc.func @execute() {
- %0 = "emitc.constant"() <{value = 0 : index}> : () -> !emitc.size_t
- %1 = get_field @fieldName0 : !emitc.array<1xf32>
- %2 = subscript %1[%0] : (!emitc.array<1xf32>, !emitc.size_t) -> !emitc.lvalue<f32>
- return
- }
- }
- // Class with a final speciferAdd commentMore actions
- emitc.class final @modelClass {
- emitc.field @fieldName0 : !emitc.array<1xf32> = {emitc.opaque = "input_tensor"}
- emitc.func @execute() {
- %0 = "emitc.constant"() <{value = 0 : index}> : () -> !emitc.size_t
- %1 = get_field @fieldName0 : !emitc.array<1xf32>
- %2 = subscript %1[%0] : (!emitc.array<1xf32>, !emitc.size_t) -> !emitc.lvalue<f32>
- return
- }
- }
+ %0 = emitc.unary_plus %arg0 : (i32) -> i32
+ ```
+ ```c++
+ // Code emitted for the operation above.
+ int32_t v2 = +v1;
```
}];
+}
- let arguments = (ins SymbolNameAttr:$sym_name, UnitAttr:$final_specifier);
+def EmitC_VariableOp : EmitC_Op<"variable", []> {
+ let summary = "Variable operation";
+ let description = [{
+ 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.
- let regions = (region AnyRegion:$body);
+ Example:
- let extraClassDeclaration = [{
- // Returns the body block containing class members and methods.
- Block &getBlock();
+ ```mlir
+ // 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.
+ ```mlir
+ %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>) -> ()
+ ```
}];
- let hasCustomAssemblyFormat = 1;
+ let arguments = (ins EmitC_OpaqueOrTypedAttr:$value);
+ let results = (outs Res<AnyTypeOf<[EmitC_ArrayType, EmitC_LValueType]>, "",
+ [MemAlloc<DefaultResource, 0, FullEffect>]>);
- let assemblyFormat =
- [{ (`final` $final_specifier^)? $sym_name attr-dict-with-keyword $body }];
+ let hasVerifier = 1;
}
-def EmitC_FieldOp : EmitC_Op<"field", [Symbol]> {
- let summary = "A field within a class";
+def EmitC_VerbatimOp : EmitC_Op<"verbatim"> {
+ let summary = "Verbatim operation";
let description = [{
- The `emitc.field` operation declares a named field within an `emitc.class`
- operation. The field's type must be an EmitC type.
+ 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:
+
+ ```c++
+ #ifdef __cplusplus
+ extern "C" {
+ #endif
+
+ ...
+
+ #ifdef __cplusplus
+ }
+ #endif
+ ```
+
+ If the `emitc.verbatim` op has operands, then the `value` is interpreted as
+ format string, where `{}` is a placeholder for an operand in their order.
+ For example, `emitc.verbatim "#pragma my src={} dst={}" %src, %dest : i32, i32`
+ would be emitted as `#pragma my src=a dst=b` if `%src` became `a` and
+ `%dest` became `b` in the C code.
+ `{{` in the format string is interpreted as a single `{` and doesn't introduce
+ a placeholder.
Example:
```mlir
- // Example with an attribute:
- emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.opaque = "another_feature"}
- // Example with no attribute:
- emitc.field @fieldName0 : !emitc.array<1xf32>
- // Example with an initial value:
- emitc.field @fieldName0 : !emitc.array<1xf32> = dense<0.0>
- // Example with an initial value and attributes:
- emitc.field @fieldName0 : !emitc.array<1xf32> = dense<0.0> {
- emitc.opaque = "input_tensor"}
+ emitc.verbatim "typedef float f32;"
+ emitc.verbatim "#pragma my var={} property" args %arg : f32
+ ```
+ ```c++
+ // Code emitted for the operation above.
+ typedef float f32;
+ #pragma my var=v1 property
```
}];
- let arguments = (ins SymbolNameAttr:$sym_name, TypeAttr:$type,
- OptionalAttr<EmitC_OpaqueOrTypedAttr>:$initial_value);
-
- let assemblyFormat = [{
- $sym_name
- `:` custom<EmitCFieldOpTypeAndInitialValue>($type, $initial_value)
- attr-dict
+ let extraClassDeclaration = [{
+ FailureOr<SmallVector<::mlir::emitc::ReplacementItem>> parseFormatString();
}];
+ let arguments = (ins StrAttr:$value, Variadic<AnyTypeOf<[EmitCType, EmitC_LValueType]>>:$fmtArgs);
+
+ let builders = [OpBuilder<(ins "::mlir::StringAttr":$value),
+ [{ build($_builder, $_state, value, {}); }]>];
+ let builders = [OpBuilder<(ins "::llvm::StringRef":$value),
+ [{ build($_builder, $_state, value, {}); }]>];
let hasVerifier = 1;
+ let assemblyFormat =
+ "$value (`args` $fmtArgs^ `:` type($fmtArgs))? attr-dict";
}
-def EmitC_GetFieldOp
- : EmitC_Op<"get_field", [Pure, DeclareOpInterfaceMethods<
- SymbolUserOpInterface>]> {
- let summary = "Obtain access to a field within a class instance";
+def EmitC_YieldOp : EmitC_Op<"yield",
+ [Pure, Terminator, ParentOneOf<["ExpressionOp", "IfOp", "ForOp", "SwitchOp"]>]> {
+ let summary = "Block termination operation";
let description = [{
- The `emitc.get_field` operation retrieves the lvalue of a
- named field from a given class instance.
-
- Example:
-
- ```mlir
- %0 = get_field @fieldName0 : !emitc.array<1xf32>
- ```
+ 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.
}];
- let arguments = (ins FlatSymbolRefAttr:$field_name);
- let results = (outs EmitCType:$result);
- let assemblyFormat = "$field_name `:` type($result) attr-dict";
+ let arguments = (ins Optional<EmitCType>:$result);
+ let builders = [OpBuilder<(ins), [{ /* nothing to do */ }]>];
+
+ let hasVerifier = 1;
+ let assemblyFormat = [{ attr-dict ($result^ `:` type($result))? }];
}
#endif // MLIR_DIALECT_EMITC_IR_EMITC
diff --git a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
index e6a3154721faa..cf1641b923049 100644
--- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
+++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
@@ -114,11 +114,10 @@ bool mlir::emitc::isIntegerIndexOrOpaqueType(Type type) {
bool mlir::emitc::isSupportedFloatType(Type type) {
if (auto floatType = llvm::dyn_cast<FloatType>(type)) {
switch (floatType.getWidth()) {
- case 16: {
- if (llvm::isa<Float16Type, BFloat16Type>(type))
- return true;
- return false;
- }
+ case 16:
+ if (!llvm::isa<Float16Type, BFloat16Type>(type))
+ return false;
+ LLVM_FALLTHROUGH;
case 32:
case 64:
return true;
@@ -294,25 +293,46 @@ LogicalResult emitc::AssignOp::verify() {
}
//===----------------------------------------------------------------------===//
-// CastOp
+// CallOp
//===----------------------------------------------------------------------===//
-bool CastOp::areCastCompatible(TypeRange inputs, TypeRange outputs) {
- Type input = inputs.front(), output = outputs.front();
+LogicalResult CallOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
+ // Check that the callee attribute was specified.
+ auto fnAttr = (*this)->getAttrOfType<FlatSymbolRefAttr>("callee");
+ if (!fnAttr)
+ return emitOpError("requires a 'callee' symbol reference attribute");
+ FuncOp fn = symbolTable.lookupNearestSymbolFrom<FuncOp>(*this, fnAttr);
+ if (!fn)
+ return emitOpError() << "'" << fnAttr.getValue()
+ << "' does not reference a valid function";
- if (auto arrayType = dyn_cast<emitc::ArrayType>(input)) {
- if (auto pointerType = dyn_cast<emitc::PointerType>(output)) {
- return (arrayType.getElementType() == pointerType.getPointee()) &&
- arrayType.getShape().size() == 1 && arrayType.getShape()[0] >= 1;
+ // Verify that the operand and result types match the callee.
+ auto fnType = fn.getFunctionType();
+ if (fnType.getNumInputs() != getNumOperands())
+ return emitOpError("incorrect number of operands for callee");
+
+ for (unsigned i = 0, e = fnType.getNumInputs(); i != e; ++i)
+ if (getOperand(i).getType() != fnType.getInput(i))
+ return emitOpError("operand type mismatch: expected operand type ")
+ << fnType.getInput(i) << ", but provided "
+ << getOperand(i).getType() << " for operand number " << i;
+
+ if (fnType.getNumResults() != getNumResults())
+ return emitOpError("incorrect number of results for callee");
+
+ for (unsigned i = 0, e = fnType.getNumResults(); i != e; ++i)
+ if (getResult(i).getType() != fnType.getResult(i)) {
+ auto diag = emitOpError("result type mismatch at index ") << i;
+ diag.attachNote() << " op result types: " << getResultTypes();
+ diag.attachNote() << "function result types: " << fnType.getResults();
+ return diag;
}
- return false;
- }
- return (
- (emitc::isIntegerIndexOrOpaqueType(input) ||
- emitc::isSupportedFloatType(input) || isa<emitc::PointerType>(input)) &&
- (emitc::isIntegerIndexOrOpaqueType(output) ||
- emitc::isSupportedFloatType(output) || isa<emitc::PointerType>(output)));
+ return success();
+}
+
+FunctionType CallOp::getCalleeType() {
+ return FunctionType::get(getContext(), getOperandTypes(), getResultTypes());
}
//===----------------------------------------------------------------------===//
@@ -357,6 +377,28 @@ LogicalResult emitc::CallOpaqueOp::verify() {
return success();
}
+//===----------------------------------------------------------------------===//
+// CastOp
+//===----------------------------------------------------------------------===//
+
+bool CastOp::areCastCompatible(TypeRange inputs, TypeRange outputs) {
+ Type input = inputs.front(), output = outputs.front();
+
+ if (auto arrayType = dyn_cast<emitc::ArrayType>(input)) {
+ if (auto pointerType = dyn_cast<emitc::PointerType>(output)) {
+ return (arrayType.getElementType() == pointerType.getPointee()) &&
+ arrayType.getShape().size() == 1 && arrayType.getShape()[0] >= 1;
+ }
+ return false;
+ }
+
+ return (
+ (emitc::isIntegerIndexOrOpaqueType(input) ||
+ emitc::isSupportedFloatType(input) || isa<emitc::PointerType>(input)) &&
+ (emitc::isIntegerIndexOrOpaqueType(output) ||
+ emitc::isSupportedFloatType(output) || isa<emitc::PointerType>(output)));
+}
+
//===----------------------------------------------------------------------===//
// ConstantOp
//===----------------------------------------------------------------------===//
@@ -374,6 +416,24 @@ LogicalResult emitc::ConstantOp::verify() {
OpFoldResult emitc::ConstantOp::fold(FoldAdaptor adaptor) { return getValue(); }
+//===----------------------------------------------------------------------===//
+// DeclareFuncOp
+//===----------------------------------------------------------------------===//
+
+LogicalResult
+DeclareFuncOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
+ // Check that the sym_name attribute was specified.
+ auto fnAttr = getSymNameAttr();
+ if (!fnAttr)
+ return emitOpError("requires a 'sym_name' symbol reference attribute");
+ FuncOp fn = symbolTable.lookupNearestSymbolFrom<FuncOp>(*this, fnAttr);
+ if (!fn)
+ return emitOpError() << "'" << fnAttr.getValue()
+ << "' does not reference a valid function";
+
+ return success();
+}
+
//===----------------------------------------------------------------------===//
// ExpressionOp
//===----------------------------------------------------------------------===//
@@ -424,6 +484,74 @@ LogicalResult ExpressionOp::verify() {
return success();
}
+//===----------------------------------------------------------------------===//
+// FieldOp
+//===----------------------------------------------------------------------===//
+
+static void printEmitCFieldOpTypeAndInitialValue(OpAsmPrinter &p, FieldOp op,
+ TypeAttr type,
+ Attribute initialValue) {
+ p << type;
+ if (initialValue) {
+ p << " = ";
+ p.printAttributeWithoutType(initialValue);
+ }
+}
+
+static Type getInitializerTypeForField(Type type) {
+ if (auto array = llvm::dyn_cast<ArrayType>(type))
+ return RankedTensorType::get(array.getShape(), array.getElementType());
+ return type;
+}
+
+static ParseResult
+parseEmitCFieldOpTypeAndInitialValue(OpAsmParser &parser, TypeAttr &typeAttr,
+ Attribute &initialValue) {
+ Type type;
+ if (parser.parseType(type))
+ return failure();
+
+ typeAttr = TypeAttr::get(type);
+
+ if (parser.parseOptionalEqual())
+ return success();
+
+ if (parser.parseAttribute(initialValue, getInitializerTypeForField(type)))
+ return failure();
+
+ if (!llvm::isa<ElementsAttr, IntegerAttr, FloatAttr, emitc::OpaqueAttr>(
+ initialValue))
+ return parser.emitError(parser.getNameLoc())
+ << "initial value should be a integer, float, elements or opaque "
+ "attribute";
+ return success();
+}
+
+LogicalResult FieldOp::verify() {
+ if (!isSupportedEmitCType(getType()))
+ return emitOpError("expected valid emitc type");
+
+ Operation *parentOp = getOperation()->getParentOp();
+ if (!parentOp || !isa<emitc::ClassOp>(parentOp))
+ return emitOpError("field must be nested within an emitc.class operation");
+
+ StringAttr symName = getSymNameAttr();
+ if (!symName || symName.getValue().empty())
+ return emitOpError("field must have a non-empty symbol name");
+
+ return success();
+}
+
+//===----------------------------------------------------------------------===//
+// FileOp
+//===----------------------------------------------------------------------===//
+
+void FileOp::build(OpBuilder &builder, OperationState &state, StringRef id) {
+ state.addRegion()->emplaceBlock();
+ state.attributes.push_back(
+ builder.getNamedAttr("id", builder.getStringAttr(id)));
+}
+
//===----------------------------------------------------------------------===//
// ForOp
//===----------------------------------------------------------------------===//
@@ -518,67 +646,6 @@ LogicalResult ForOp::verifyRegions() {
return success();
}
-//===----------------------------------------------------------------------===//
-// CallOp
-//===----------------------------------------------------------------------===//
-
-LogicalResult CallOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
- // Check that the callee attribute was specified.
- auto fnAttr = (*this)->getAttrOfType<FlatSymbolRefAttr>("callee");
- if (!fnAttr)
- return emitOpError("requires a 'callee' symbol reference attribute");
- FuncOp fn = symbolTable.lookupNearestSymbolFrom<FuncOp>(*this, fnAttr);
- if (!fn)
- return emitOpError() << "'" << fnAttr.getValue()
- << "' does not reference a valid function";
-
- // Verify that the operand and result types match the callee.
- auto fnType = fn.getFunctionType();
- if (fnType.getNumInputs() != getNumOperands())
- return emitOpError("incorrect number of operands for callee");
-
- for (unsigned i = 0, e = fnType.getNumInputs(); i != e; ++i)
- if (getOperand(i).getType() != fnType.getInput(i))
- return emitOpError("operand type mismatch: expected operand type ")
- << fnType.getInput(i) << ", but provided "
- << getOperand(i).getType() << " for operand number " << i;
-
- if (fnType.getNumResults() != getNumResults())
- return emitOpError("incorrect number of results for callee");
-
- for (unsigned i = 0, e = fnType.getNumResults(); i != e; ++i)
- if (getResult(i).getType() != fnType.getResult(i)) {
- auto diag = emitOpError("result type mismatch at index ") << i;
- diag.attachNote() << " op result types: " << getResultTypes();
- diag.attachNote() << "function result types: " << fnType.getResults();
- return diag;
- }
-
- return success();
-}
-
-FunctionType CallOp::getCalleeType() {
- return FunctionType::get(getContext(), getOperandTypes(), getResultTypes());
-}
-
-//===----------------------------------------------------------------------===//
-// DeclareFuncOp
-//===----------------------------------------------------------------------===//
-
-LogicalResult
-DeclareFuncOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
- // Check that the sym_name attribute was specified.
- auto fnAttr = getSymNameAttr();
- if (!fnAttr)
- return emitOpError("requires a 'sym_name' symbol reference attribute");
- FuncOp fn = symbolTable.lookupNearestSymbolFrom<FuncOp>(*this, fnAttr);
- if (!fn)
- return emitOpError() << "'" << fnAttr.getValue()
- << "' does not reference a valid function";
-
- return success();
-}
-
//===----------------------------------------------------------------------===//
// FuncOp
//===----------------------------------------------------------------------===//
@@ -634,35 +701,153 @@ LogicalResult FuncOp::verify() {
}
//===----------------------------------------------------------------------===//
-// ReturnOp
+// GetFieldOp
//===----------------------------------------------------------------------===//
-LogicalResult ReturnOp::verify() {
- auto function = cast<FuncOp>((*this)->getParentOp());
+LogicalResult GetFieldOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
+ mlir::FlatSymbolRefAttr fieldNameAttr = getFieldNameAttr();
+ FieldOp fieldOp =
+ symbolTable.lookupNearestSymbolFrom<FieldOp>(*this, fieldNameAttr);
+ if (!fieldOp)
+ return emitOpError("field '")
+ << fieldNameAttr << "' not found in the class";
- // The operand number and types must match the function signature.
- if (getNumOperands() != function.getNumResults())
- return emitOpError("has ")
- << getNumOperands() << " operands, but enclosing function (@"
- << function.getName() << ") returns " << function.getNumResults();
+ Type getFieldResultType = getResult().getType();
+ Type fieldType = fieldOp.getType();
+
+ if (fieldType != getFieldResultType)
+ return emitOpError("result type ")
+ << getFieldResultType << " does not match field '" << fieldNameAttr
+ << "' type " << fieldType;
- if (function.getNumResults() == 1)
- if (getOperand().getType() != function.getResultTypes()[0])
- return emitError() << "type of the return operand ("
- << getOperand().getType()
- << ") doesn't match function result type ("
- << function.getResultTypes()[0] << ")"
- << " in function @" << function.getName();
return success();
}
//===----------------------------------------------------------------------===//
-// IfOp
+// GetGlobalOp
//===----------------------------------------------------------------------===//
-void IfOp::build(OpBuilder &builder, OperationState &result, Value cond,
- bool addThenBlock, bool addElseBlock) {
- assert((!addElseBlock || addThenBlock) &&
+LogicalResult
+GetGlobalOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
+ // Verify that the type matches the type of the global variable.
+ auto global =
+ symbolTable.lookupNearestSymbolFrom<GlobalOp>(*this, getNameAttr());
+ if (!global)
+ return emitOpError("'")
+ << getName() << "' does not reference a valid emitc.global";
+
+ Type resultType = getResult().getType();
+ Type globalType = global.getType();
+
+ // global has array type
+ if (llvm::isa<ArrayType>(globalType)) {
+ if (globalType != resultType)
+ return emitOpError("on array type expects result type ")
+ << resultType << " to match type " << globalType
+ << " of the global @" << getName();
+ return success();
+ }
+
+ // global has non-array type
+ auto lvalueType = dyn_cast<LValueType>(resultType);
+ if (!lvalueType || lvalueType.getValueType() != globalType)
+ return emitOpError("on non-array type expects result inner type ")
+ << lvalueType.getValueType() << " to match type " << globalType
+ << " of the global @" << getName();
+ return success();
+}
+
+//===----------------------------------------------------------------------===//
+// GlobalOp
+//===----------------------------------------------------------------------===//
+
+static void printEmitCGlobalOpTypeAndInitialValue(OpAsmPrinter &p, GlobalOp op,
+ TypeAttr type,
+ Attribute initialValue) {
+ p << type;
+ if (initialValue) {
+ p << " = ";
+ p.printAttributeWithoutType(initialValue);
+ }
+}
+
+static Type getInitializerTypeForGlobal(Type type) {
+ if (auto array = llvm::dyn_cast<ArrayType>(type))
+ return RankedTensorType::get(array.getShape(), array.getElementType());
+ return type;
+}
+
+static ParseResult
+parseEmitCGlobalOpTypeAndInitialValue(OpAsmParser &parser, TypeAttr &typeAttr,
+ Attribute &initialValue) {
+ Type type;
+ if (parser.parseType(type))
+ return failure();
+
+ typeAttr = TypeAttr::get(type);
+
+ if (parser.parseOptionalEqual())
+ return success();
+
+ if (parser.parseAttribute(initialValue, getInitializerTypeForGlobal(type)))
+ return failure();
+
+ if (!llvm::isa<ElementsAttr, IntegerAttr, FloatAttr, emitc::OpaqueAttr>(
+ initialValue))
+ return parser.emitError(parser.getNameLoc())
+ << "initial value should be a integer, float, elements or opaque "
+ "attribute";
+ return success();
+}
+
+LogicalResult GlobalOp::verify() {
+ if (!isSupportedEmitCType(getType())) {
+ return emitOpError("expected valid emitc type");
+ }
+ if (getInitialValue().has_value()) {
+ Attribute initValue = getInitialValue().value();
+ // Check that the type of the initial value is compatible with the type of
+ // the global variable.
+ if (auto elementsAttr = llvm::dyn_cast<ElementsAttr>(initValue)) {
+ auto arrayType = llvm::dyn_cast<ArrayType>(getType());
+ if (!arrayType)
+ return emitOpError("expected array type, but got ") << getType();
+
+ Type initType = elementsAttr.getType();
+ Type tensorType = getInitializerTypeForGlobal(getType());
+ if (initType != tensorType) {
+ return emitOpError("initial value expected to be of type ")
+ << getType() << ", but was of type " << initType;
+ }
+ } else if (auto intAttr = dyn_cast<IntegerAttr>(initValue)) {
+ if (intAttr.getType() != getType()) {
+ return emitOpError("initial value expected to be of type ")
+ << getType() << ", but was of type " << intAttr.getType();
+ }
+ } else if (auto floatAttr = dyn_cast<FloatAttr>(initValue)) {
+ if (floatAttr.getType() != getType()) {
+ return emitOpError("initial value expected to be of type ")
+ << getType() << ", but was of type " << floatAttr.getType();
+ }
+ } else if (!isa<emitc::OpaqueAttr>(initValue)) {
+ return emitOpError("initial value should be a integer, float, elements "
+ "or opaque attribute, but got ")
+ << initValue;
+ }
+ }
+ if (getStaticSpecifier() && getExternSpecifier()) {
+ return emitOpError("cannot have both static and extern specifiers");
+ }
+ return success();
+}
+
+//===----------------------------------------------------------------------===//
+// IfOp
+//===----------------------------------------------------------------------===//
+
+void IfOp::build(OpBuilder &builder, OperationState &result, Value cond,
+ bool addThenBlock, bool addElseBlock) {
+ assert((!addElseBlock || addThenBlock) &&
"must not create else block w/o then block");
result.addOperands(cond);
@@ -861,6 +1046,30 @@ LogicalResult emitc::LiteralOp::verify() {
return emitOpError() << "value must not be empty";
return success();
}
+
+//===----------------------------------------------------------------------===//
+// ReturnOp
+//===----------------------------------------------------------------------===//
+
+LogicalResult ReturnOp::verify() {
+ auto function = cast<FuncOp>((*this)->getParentOp());
+
+ // The operand number and types must match the function signature.
+ if (getNumOperands() != function.getNumResults())
+ return emitOpError("has ")
+ << getNumOperands() << " operands, but enclosing function (@"
+ << function.getName() << ") returns " << function.getNumResults();
+
+ if (function.getNumResults() == 1)
+ if (getOperand().getType() != function.getResultTypes()[0])
+ return emitError() << "type of the return operand ("
+ << getOperand().getType()
+ << ") doesn't match function result type ("
+ << function.getResultTypes()[0] << ")"
+ << " in function @" << function.getName();
+ return success();
+}
+
//===----------------------------------------------------------------------===//
// SubOp
//===----------------------------------------------------------------------===//
@@ -885,31 +1094,6 @@ LogicalResult SubOp::verify() {
return success();
}
-//===----------------------------------------------------------------------===//
-// VariableOp
-//===----------------------------------------------------------------------===//
-
-LogicalResult emitc::VariableOp::verify() {
- return verifyInitializationAttribute(getOperation(), getValueAttr());
-}
-
-//===----------------------------------------------------------------------===//
-// YieldOp
-//===----------------------------------------------------------------------===//
-
-LogicalResult emitc::YieldOp::verify() {
- Value result = getResult();
- Operation *containingOp = getOperation()->getParentOp();
-
- if (result && containingOp->getNumResults() != 1)
- return emitOpError() << "yields a value not returned by parent";
-
- if (!result && containingOp->getNumResults() != 0)
- return emitOpError() << "does not yield a value to be returned by parent";
-
- return success();
-}
-
//===----------------------------------------------------------------------===//
// SubscriptOp
//===----------------------------------------------------------------------===//
@@ -976,57 +1160,214 @@ LogicalResult emitc::SubscriptOp::verify() {
}
//===----------------------------------------------------------------------===//
-// VerbatimOp
+// SwitchOp
//===----------------------------------------------------------------------===//
-LogicalResult emitc::VerbatimOp::verify() {
- auto errorCallback = [&]() -> InFlightDiagnostic {
- return this->emitOpError();
- };
- FailureOr<SmallVector<ReplacementItem>> fmt =
- ::parseFormatString(getValue(), getFmtArgs(), errorCallback);
- if (failed(fmt))
- return failure();
-
- size_t numPlaceholders = llvm::count_if(*fmt, [](ReplacementItem &item) {
- return std::holds_alternative<Placeholder>(item);
- });
-
- if (numPlaceholders != getFmtArgs().size()) {
- return emitOpError()
- << "requires operands for each placeholder in the format string";
+/// Parse the case regions and values.
+static ParseResult
+parseSwitchCases(OpAsmParser &parser, DenseI64ArrayAttr &cases,
+ SmallVectorImpl<std::unique_ptr<Region>> &caseRegions) {
+ SmallVector<int64_t> caseValues;
+ while (succeeded(parser.parseOptionalKeyword("case"))) {
+ int64_t value;
+ Region ®ion = *caseRegions.emplace_back(std::make_unique<Region>());
+ if (parser.parseInteger(value) ||
+ parser.parseRegion(region, /*arguments=*/{}))
+ return failure();
+ caseValues.push_back(value);
}
+ cases = parser.getBuilder().getDenseI64ArrayAttr(caseValues);
return success();
}
-FailureOr<SmallVector<ReplacementItem>> emitc::VerbatimOp::parseFormatString() {
- // Error checking is done in verify.
- return ::parseFormatString(getValue(), getFmtArgs());
+/// Print the case regions and values.
+static void printSwitchCases(OpAsmPrinter &p, Operation *op,
+ DenseI64ArrayAttr cases, RegionRange caseRegions) {
+ for (auto [value, region] : llvm::zip(cases.asArrayRef(), caseRegions)) {
+ p.printNewline();
+ p << "case " << value << ' ';
+ p.printRegion(*region, /*printEntryBlockArgs=*/false);
+ }
}
-//===----------------------------------------------------------------------===//
-// EmitC Enums
-//===----------------------------------------------------------------------===//
+static LogicalResult verifyRegion(emitc::SwitchOp op, Region ®ion,
+ const Twine &name) {
+ auto yield = dyn_cast<emitc::YieldOp>(region.front().back());
+ if (!yield)
+ return op.emitOpError("expected region to end with emitc.yield, but got ")
+ << region.front().back().getName();
-#include "mlir/Dialect/EmitC/IR/EmitCEnums.cpp.inc"
+ if (yield.getNumOperands() != 0) {
+ return (op.emitOpError("expected each region to return ")
+ << "0 values, but " << name << " returns "
+ << yield.getNumOperands())
+ .attachNote(yield.getLoc())
+ << "see yield operation here";
+ }
-//===----------------------------------------------------------------------===//
-// EmitC Attributes
-//===----------------------------------------------------------------------===//
+ return success();
+}
-#define GET_ATTRDEF_CLASSES
-#include "mlir/Dialect/EmitC/IR/EmitCAttributes.cpp.inc"
+LogicalResult emitc::SwitchOp::verify() {
+ if (!isIntegerIndexOrOpaqueType(getArg().getType()))
+ return emitOpError("unsupported type ") << getArg().getType();
-//===----------------------------------------------------------------------===//
-// EmitC Types
-//===----------------------------------------------------------------------===//
+ if (getCases().size() != getCaseRegions().size()) {
+ return emitOpError("has ")
+ << getCaseRegions().size() << " case regions but "
+ << getCases().size() << " case values";
+ }
-#define GET_TYPEDEF_CLASSES
-#include "mlir/Dialect/EmitC/IR/EmitCTypes.cpp.inc"
+ DenseSet<int64_t> valueSet;
+ for (int64_t value : getCases())
+ if (!valueSet.insert(value).second)
+ return emitOpError("has duplicate case value: ") << value;
-//===----------------------------------------------------------------------===//
-// ArrayType
-//===----------------------------------------------------------------------===//
+ if (failed(verifyRegion(*this, getDefaultRegion(), "default region")))
+ return failure();
+
+ for (auto [idx, caseRegion] : llvm::enumerate(getCaseRegions()))
+ if (failed(verifyRegion(*this, caseRegion, "case region #" + Twine(idx))))
+ return failure();
+
+ return success();
+}
+
+unsigned emitc::SwitchOp::getNumCases() { return getCases().size(); }
+
+Block &emitc::SwitchOp::getDefaultBlock() { return getDefaultRegion().front(); }
+
+Block &emitc::SwitchOp::getCaseBlock(unsigned idx) {
+ assert(idx < getNumCases() && "case index out-of-bounds");
+ return getCaseRegions()[idx].front();
+}
+
+void SwitchOp::getSuccessorRegions(
+ RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> &successors) {
+ llvm::append_range(successors, getRegions());
+}
+
+void SwitchOp::getEntrySuccessorRegions(
+ ArrayRef<Attribute> operands,
+ SmallVectorImpl<RegionSuccessor> &successors) {
+ FoldAdaptor adaptor(operands, *this);
+
+ // If a constant was not provided, all regions are possible successors.
+ auto arg = dyn_cast_or_null<IntegerAttr>(adaptor.getArg());
+ if (!arg) {
+ llvm::append_range(successors, getRegions());
+ return;
+ }
+
+ // Otherwise, try to find a case with a matching value. If not, the
+ // default region is the only successor.
+ for (auto [caseValue, caseRegion] : llvm::zip(getCases(), getCaseRegions())) {
+ if (caseValue == arg.getInt()) {
+ successors.emplace_back(&caseRegion);
+ return;
+ }
+ }
+ successors.emplace_back(&getDefaultRegion());
+}
+
+void SwitchOp::getRegionInvocationBounds(
+ ArrayRef<Attribute> operands, SmallVectorImpl<InvocationBounds> &bounds) {
+ auto operandValue = llvm::dyn_cast_or_null<IntegerAttr>(operands.front());
+ if (!operandValue) {
+ // All regions are invoked at most once.
+ bounds.append(getNumRegions(), InvocationBounds(/*lb=*/0, /*ub=*/1));
+ return;
+ }
+
+ unsigned liveIndex = getNumRegions() - 1;
+ const auto *iteratorToInt = llvm::find(getCases(), operandValue.getInt());
+
+ liveIndex = iteratorToInt != getCases().end()
+ ? std::distance(getCases().begin(), iteratorToInt)
+ : liveIndex;
+
+ for (unsigned regIndex = 0, regNum = getNumRegions(); regIndex < regNum;
+ ++regIndex)
+ bounds.emplace_back(/*lb=*/0, /*ub=*/regIndex == liveIndex);
+}
+
+//===----------------------------------------------------------------------===//
+// VariableOp
+//===----------------------------------------------------------------------===//
+
+LogicalResult emitc::VariableOp::verify() {
+ return verifyInitializationAttribute(getOperation(), getValueAttr());
+}
+
+//===----------------------------------------------------------------------===//
+// VerbatimOp
+//===----------------------------------------------------------------------===//
+
+LogicalResult emitc::VerbatimOp::verify() {
+ auto errorCallback = [&]() -> InFlightDiagnostic {
+ return this->emitOpError();
+ };
+ FailureOr<SmallVector<ReplacementItem>> fmt =
+ ::parseFormatString(getValue(), getFmtArgs(), errorCallback);
+ if (failed(fmt))
+ return failure();
+
+ size_t numPlaceholders = llvm::count_if(*fmt, [](ReplacementItem &item) {
+ return std::holds_alternative<Placeholder>(item);
+ });
+
+ if (numPlaceholders != getFmtArgs().size()) {
+ return emitOpError()
+ << "requires operands for each placeholder in the format string";
+ }
+ return success();
+}
+
+FailureOr<SmallVector<ReplacementItem>> emitc::VerbatimOp::parseFormatString() {
+ // Error checking is done in verify.
+ return ::parseFormatString(getValue(), getFmtArgs());
+}
+
+//===----------------------------------------------------------------------===//
+// YieldOp
+//===----------------------------------------------------------------------===//
+
+LogicalResult emitc::YieldOp::verify() {
+ Value result = getResult();
+ Operation *containingOp = getOperation()->getParentOp();
+
+ if (result && containingOp->getNumResults() != 1)
+ return emitOpError() << "yields a value not returned by parent";
+
+ if (!result && containingOp->getNumResults() != 0)
+ return emitOpError() << "does not yield a value to be returned by parent";
+
+ return success();
+}
+
+//===----------------------------------------------------------------------===//
+// EmitC Enums
+//===----------------------------------------------------------------------===//
+
+#include "mlir/Dialect/EmitC/IR/EmitCEnums.cpp.inc"
+
+//===----------------------------------------------------------------------===//
+// EmitC Attributes
+//===----------------------------------------------------------------------===//
+
+#define GET_ATTRDEF_CLASSES
+#include "mlir/Dialect/EmitC/IR/EmitCAttributes.cpp.inc"
+
+//===----------------------------------------------------------------------===//
+// EmitC Types
+//===----------------------------------------------------------------------===//
+
+#define GET_TYPEDEF_CLASSES
+#include "mlir/Dialect/EmitC/IR/EmitCTypes.cpp.inc"
+
+//===----------------------------------------------------------------------===//
+// ArrayType
+//===----------------------------------------------------------------------===//
Type emitc::ArrayType::parse(AsmParser &parser) {
if (parser.parseLess())
@@ -1137,343 +1478,6 @@ LogicalResult mlir::emitc::PointerType::verify(
return success();
}
-//===----------------------------------------------------------------------===//
-// GlobalOp
-//===----------------------------------------------------------------------===//
-static void printEmitCGlobalOpTypeAndInitialValue(OpAsmPrinter &p, GlobalOp op,
- TypeAttr type,
- Attribute initialValue) {
- p << type;
- if (initialValue) {
- p << " = ";
- p.printAttributeWithoutType(initialValue);
- }
-}
-
-static Type getInitializerTypeForGlobal(Type type) {
- if (auto array = llvm::dyn_cast<ArrayType>(type))
- return RankedTensorType::get(array.getShape(), array.getElementType());
- return type;
-}
-
-static ParseResult
-parseEmitCGlobalOpTypeAndInitialValue(OpAsmParser &parser, TypeAttr &typeAttr,
- Attribute &initialValue) {
- Type type;
- if (parser.parseType(type))
- return failure();
-
- typeAttr = TypeAttr::get(type);
-
- if (parser.parseOptionalEqual())
- return success();
-
- if (parser.parseAttribute(initialValue, getInitializerTypeForGlobal(type)))
- return failure();
-
- if (!llvm::isa<ElementsAttr, IntegerAttr, FloatAttr, emitc::OpaqueAttr>(
- initialValue))
- return parser.emitError(parser.getNameLoc())
- << "initial value should be a integer, float, elements or opaque "
- "attribute";
- return success();
-}
-
-LogicalResult GlobalOp::verify() {
- if (!isSupportedEmitCType(getType())) {
- return emitOpError("expected valid emitc type");
- }
- if (getInitialValue().has_value()) {
- Attribute initValue = getInitialValue().value();
- // Check that the type of the initial value is compatible with the type of
- // the global variable.
- if (auto elementsAttr = llvm::dyn_cast<ElementsAttr>(initValue)) {
- auto arrayType = llvm::dyn_cast<ArrayType>(getType());
- if (!arrayType)
- return emitOpError("expected array type, but got ") << getType();
-
- Type initType = elementsAttr.getType();
- Type tensorType = getInitializerTypeForGlobal(getType());
- if (initType != tensorType) {
- return emitOpError("initial value expected to be of type ")
- << getType() << ", but was of type " << initType;
- }
- } else if (auto intAttr = dyn_cast<IntegerAttr>(initValue)) {
- if (intAttr.getType() != getType()) {
- return emitOpError("initial value expected to be of type ")
- << getType() << ", but was of type " << intAttr.getType();
- }
- } else if (auto floatAttr = dyn_cast<FloatAttr>(initValue)) {
- if (floatAttr.getType() != getType()) {
- return emitOpError("initial value expected to be of type ")
- << getType() << ", but was of type " << floatAttr.getType();
- }
- } else if (!isa<emitc::OpaqueAttr>(initValue)) {
- return emitOpError("initial value should be a integer, float, elements "
- "or opaque attribute, but got ")
- << initValue;
- }
- }
- if (getStaticSpecifier() && getExternSpecifier()) {
- return emitOpError("cannot have both static and extern specifiers");
- }
- return success();
-}
-
-//===----------------------------------------------------------------------===//
-// GetGlobalOp
-//===----------------------------------------------------------------------===//
-
-LogicalResult
-GetGlobalOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
- // Verify that the type matches the type of the global variable.
- auto global =
- symbolTable.lookupNearestSymbolFrom<GlobalOp>(*this, getNameAttr());
- if (!global)
- return emitOpError("'")
- << getName() << "' does not reference a valid emitc.global";
-
- Type resultType = getResult().getType();
- Type globalType = global.getType();
-
- // global has array type
- if (llvm::isa<ArrayType>(globalType)) {
- if (globalType != resultType)
- return emitOpError("on array type expects result type ")
- << resultType << " to match type " << globalType
- << " of the global @" << getName();
- return success();
- }
-
- // global has non-array type
- auto lvalueType = dyn_cast<LValueType>(resultType);
- if (!lvalueType || lvalueType.getValueType() != globalType)
- return emitOpError("on non-array type expects result inner type ")
- << lvalueType.getValueType() << " to match type " << globalType
- << " of the global @" << getName();
- return success();
-}
-
-//===----------------------------------------------------------------------===//
-// SwitchOp
-//===----------------------------------------------------------------------===//
-
-/// Parse the case regions and values.
-static ParseResult
-parseSwitchCases(OpAsmParser &parser, DenseI64ArrayAttr &cases,
- SmallVectorImpl<std::unique_ptr<Region>> &caseRegions) {
- SmallVector<int64_t> caseValues;
- while (succeeded(parser.parseOptionalKeyword("case"))) {
- int64_t value;
- Region ®ion = *caseRegions.emplace_back(std::make_unique<Region>());
- if (parser.parseInteger(value) ||
- parser.parseRegion(region, /*arguments=*/{}))
- return failure();
- caseValues.push_back(value);
- }
- cases = parser.getBuilder().getDenseI64ArrayAttr(caseValues);
- return success();
-}
-
-/// Print the case regions and values.
-static void printSwitchCases(OpAsmPrinter &p, Operation *op,
- DenseI64ArrayAttr cases, RegionRange caseRegions) {
- for (auto [value, region] : llvm::zip(cases.asArrayRef(), caseRegions)) {
- p.printNewline();
- p << "case " << value << ' ';
- p.printRegion(*region, /*printEntryBlockArgs=*/false);
- }
-}
-
-static LogicalResult verifyRegion(emitc::SwitchOp op, Region ®ion,
- const Twine &name) {
- auto yield = dyn_cast<emitc::YieldOp>(region.front().back());
- if (!yield)
- return op.emitOpError("expected region to end with emitc.yield, but got ")
- << region.front().back().getName();
-
- if (yield.getNumOperands() != 0) {
- return (op.emitOpError("expected each region to return ")
- << "0 values, but " << name << " returns "
- << yield.getNumOperands())
- .attachNote(yield.getLoc())
- << "see yield operation here";
- }
-
- return success();
-}
-
-LogicalResult emitc::SwitchOp::verify() {
- if (!isIntegerIndexOrOpaqueType(getArg().getType()))
- return emitOpError("unsupported type ") << getArg().getType();
-
- if (getCases().size() != getCaseRegions().size()) {
- return emitOpError("has ")
- << getCaseRegions().size() << " case regions but "
- << getCases().size() << " case values";
- }
-
- DenseSet<int64_t> valueSet;
- for (int64_t value : getCases())
- if (!valueSet.insert(value).second)
- return emitOpError("has duplicate case value: ") << value;
-
- if (failed(verifyRegion(*this, getDefaultRegion(), "default region")))
- return failure();
-
- for (auto [idx, caseRegion] : llvm::enumerate(getCaseRegions()))
- if (failed(verifyRegion(*this, caseRegion, "case region #" + Twine(idx))))
- return failure();
-
- return success();
-}
-
-unsigned emitc::SwitchOp::getNumCases() { return getCases().size(); }
-
-Block &emitc::SwitchOp::getDefaultBlock() { return getDefaultRegion().front(); }
-
-Block &emitc::SwitchOp::getCaseBlock(unsigned idx) {
- assert(idx < getNumCases() && "case index out-of-bounds");
- return getCaseRegions()[idx].front();
-}
-
-void SwitchOp::getSuccessorRegions(
- RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> &successors) {
- llvm::append_range(successors, getRegions());
-}
-
-void SwitchOp::getEntrySuccessorRegions(
- ArrayRef<Attribute> operands,
- SmallVectorImpl<RegionSuccessor> &successors) {
- FoldAdaptor adaptor(operands, *this);
-
- // If a constant was not provided, all regions are possible successors.
- auto arg = dyn_cast_or_null<IntegerAttr>(adaptor.getArg());
- if (!arg) {
- llvm::append_range(successors, getRegions());
- return;
- }
-
- // Otherwise, try to find a case with a matching value. If not, the
- // default region is the only successor.
- for (auto [caseValue, caseRegion] : llvm::zip(getCases(), getCaseRegions())) {
- if (caseValue == arg.getInt()) {
- successors.emplace_back(&caseRegion);
- return;
- }
- }
- successors.emplace_back(&getDefaultRegion());
-}
-
-void SwitchOp::getRegionInvocationBounds(
- ArrayRef<Attribute> operands, SmallVectorImpl<InvocationBounds> &bounds) {
- auto operandValue = llvm::dyn_cast_or_null<IntegerAttr>(operands.front());
- if (!operandValue) {
- // All regions are invoked at most once.
- bounds.append(getNumRegions(), InvocationBounds(/*lb=*/0, /*ub=*/1));
- return;
- }
-
- unsigned liveIndex = getNumRegions() - 1;
- const auto *iteratorToInt = llvm::find(getCases(), operandValue.getInt());
-
- liveIndex = iteratorToInt != getCases().end()
- ? std::distance(getCases().begin(), iteratorToInt)
- : liveIndex;
-
- for (unsigned regIndex = 0, regNum = getNumRegions(); regIndex < regNum;
- ++regIndex)
- bounds.emplace_back(/*lb=*/0, /*ub=*/regIndex == liveIndex);
-}
-
-//===----------------------------------------------------------------------===//
-// FileOp
-//===----------------------------------------------------------------------===//
-void FileOp::build(OpBuilder &builder, OperationState &state, StringRef id) {
- state.addRegion()->emplaceBlock();
- state.attributes.push_back(
- builder.getNamedAttr("id", builder.getStringAttr(id)));
-}
-
-//===----------------------------------------------------------------------===//
-// FieldOp
-//===----------------------------------------------------------------------===//
-static void printEmitCFieldOpTypeAndInitialValue(OpAsmPrinter &p, FieldOp op,
- TypeAttr type,
- Attribute initialValue) {
- p << type;
- if (initialValue) {
- p << " = ";
- p.printAttributeWithoutType(initialValue);
- }
-}
-
-static Type getInitializerTypeForField(Type type) {
- if (auto array = llvm::dyn_cast<ArrayType>(type))
- return RankedTensorType::get(array.getShape(), array.getElementType());
- return type;
-}
-
-static ParseResult
-parseEmitCFieldOpTypeAndInitialValue(OpAsmParser &parser, TypeAttr &typeAttr,
- Attribute &initialValue) {
- Type type;
- if (parser.parseType(type))
- return failure();
-
- typeAttr = TypeAttr::get(type);
-
- if (parser.parseOptionalEqual())
- return success();
-
- if (parser.parseAttribute(initialValue, getInitializerTypeForField(type)))
- return failure();
-
- if (!llvm::isa<ElementsAttr, IntegerAttr, FloatAttr, emitc::OpaqueAttr>(
- initialValue))
- return parser.emitError(parser.getNameLoc())
- << "initial value should be a integer, float, elements or opaque "
- "attribute";
- return success();
-}
-
-LogicalResult FieldOp::verify() {
- if (!isSupportedEmitCType(getType()))
- return emitOpError("expected valid emitc type");
-
- Operation *parentOp = getOperation()->getParentOp();
- if (!parentOp || !isa<emitc::ClassOp>(parentOp))
- return emitOpError("field must be nested within an emitc.class operation");
-
- StringAttr symName = getSymNameAttr();
- if (!symName || symName.getValue().empty())
- return emitOpError("field must have a non-empty symbol name");
-
- return success();
-}
-
-//===----------------------------------------------------------------------===//
-// GetFieldOp
-//===----------------------------------------------------------------------===//
-LogicalResult GetFieldOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
- mlir::FlatSymbolRefAttr fieldNameAttr = getFieldNameAttr();
- FieldOp fieldOp =
- symbolTable.lookupNearestSymbolFrom<FieldOp>(*this, fieldNameAttr);
- if (!fieldOp)
- return emitOpError("field '")
- << fieldNameAttr << "' not found in the class";
-
- Type getFieldResultType = getResult().getType();
- Type fieldType = fieldOp.getType();
-
- if (fieldType != getFieldResultType)
- return emitOpError("result type ")
- << getFieldResultType << " does not match field '" << fieldNameAttr
- << "' type " << fieldType;
-
- return success();
-}
-
//===----------------------------------------------------------------------===//
// TableGen'd op method definitions
//===----------------------------------------------------------------------===//
diff --git a/mlir/test/Dialect/EmitC/attrs.mlir b/mlir/test/Dialect/EmitC/attrs.mlir
index 11251b88ff0c9..5a219c462678e 100644
--- a/mlir/test/Dialect/EmitC/attrs.mlir
+++ b/mlir/test/Dialect/EmitC/attrs.mlir
@@ -1,6 +1,6 @@
-// RUN: mlir-opt -verify-diagnostics %s | FileCheck %s
+// RUN: mlir-opt %s | FileCheck %s
// check parser
-// RUN: mlir-opt -verify-diagnostics %s | mlir-opt -verify-diagnostics | FileCheck %s
+// RUN: mlir-opt %s | mlir-opt | FileCheck %s
// CHECK-LABEL: func @opaque_attrs() {
func.func @opaque_attrs() {
diff --git a/mlir/test/Dialect/EmitC/ops.mlir b/mlir/test/Dialect/EmitC/ops.mlir
index ad40313f95df9..acee0a8d53fe4 100644
--- a/mlir/test/Dialect/EmitC/ops.mlir
+++ b/mlir/test/Dialect/EmitC/ops.mlir
@@ -310,3 +310,15 @@ func.func @switch() {
return
}
+
+emitc.class final @finalClass {
+ emitc.field @fieldName0 : !emitc.array<1xf32>
+ emitc.field @fieldName1 : !emitc.array<1xf32>
+ emitc.func @execute() {
+ %0 = "emitc.constant"() <{value = 0 : index}> : () -> !emitc.size_t
+ %1 = get_field @fieldName0 : !emitc.array<1xf32>
+ %2 = get_field @fieldName1 : !emitc.array<1xf32>
+ %3 = subscript %1[%0] : (!emitc.array<1xf32>, !emitc.size_t) -> !emitc.lvalue<f32>
+ return
+ }
+}
diff --git a/mlir/test/Dialect/EmitC/transforms.mlir b/mlir/test/Dialect/EmitC/transforms.mlir
index a38f396dad953..d9f4ee0c3fc41 100644
--- a/mlir/test/Dialect/EmitC/transforms.mlir
+++ b/mlir/test/Dialect/EmitC/transforms.mlir
@@ -1,16 +1,17 @@
-// RUN: mlir-opt %s --form-expressions --verify-diagnostics --split-input-file | FileCheck %s
-
-// CHECK-LABEL: func.func @single_expression(
-// CHECK-SAME: %[[VAL_0:.*]]: i32, %[[VAL_1:.*]]: i32, %[[VAL_2:.*]]: i32, %[[VAL_3:.*]]: i32) -> i1 {
-// CHECK: %[[VAL_4:.*]] = "emitc.constant"() <{value = 42 : i32}> : () -> i32
-// CHECK: %[[VAL_5:.*]] = emitc.expression : i1 {
-// CHECK: %[[VAL_6:.*]] = mul %[[VAL_0]], %[[VAL_4]] : (i32, i32) -> i32
-// CHECK: %[[VAL_7:.*]] = sub %[[VAL_6]], %[[VAL_2]] : (i32, i32) -> i32
-// CHECK: %[[VAL_8:.*]] = cmp lt, %[[VAL_7]], %[[VAL_3]] : (i32, i32) -> i1
-// CHECK: yield %[[VAL_8]] : i1
-// CHECK: }
-// CHECK: return %[[VAL_5]] : i1
-// CHECK: }
+// RUN: mlir-opt %s -form-expressions | FileCheck %s -check-prefix=CHECK-FORM-EXPRESSIONS
+// RUN: mlir-opt %s -wrap-emitc-func-in-class -split-input-file | FileCheck %s -check-prefix=CHECK-WRAP-IN-CLASS
+
+// CHECK-FORM-EXPRESSIONS-LABEL: func.func @single_expression(
+// CHECK-FORM-EXPRESSIONS-SAME: %[[VAL_0:.*]]: i32, %[[VAL_1:.*]]: i32, %[[VAL_2:.*]]: i32, %[[VAL_3:.*]]: i32) -> i1 {
+// CHECK-FORM-EXPRESSIONS: %[[VAL_4:.*]] = "emitc.constant"() <{value = 42 : i32}> : () -> i32
+// CHECK-FORM-EXPRESSIONS: %[[VAL_5:.*]] = emitc.expression : i1 {
+// CHECK-FORM-EXPRESSIONS: %[[VAL_6:.*]] = mul %[[VAL_0]], %[[VAL_4]] : (i32, i32) -> i32
+// CHECK-FORM-EXPRESSIONS: %[[VAL_7:.*]] = sub %[[VAL_6]], %[[VAL_2]] : (i32, i32) -> i32
+// CHECK-FORM-EXPRESSIONS: %[[VAL_8:.*]] = cmp lt, %[[VAL_7]], %[[VAL_3]] : (i32, i32) -> i1
+// CHECK-FORM-EXPRESSIONS: yield %[[VAL_8]] : i1
+// CHECK-FORM-EXPRESSIONS: }
+// CHECK-FORM-EXPRESSIONS: return %[[VAL_5]] : i1
+// CHECK-FORM-EXPRESSIONS: }
func.func @single_expression(%arg0: i32, %arg1: i32, %arg2: i32, %arg3: i32) -> i1 {
%c42 = "emitc.constant"(){value = 42 : i32} : () -> i32
@@ -20,20 +21,20 @@ func.func @single_expression(%arg0: i32, %arg1: i32, %arg2: i32, %arg3: i32) ->
return %c : i1
}
-// CHECK-LABEL: func.func @multiple_expressions(
-// CHECK-SAME: %[[VAL_0:.*]]: i32, %[[VAL_1:.*]]: i32, %[[VAL_2:.*]]: i32, %[[VAL_3:.*]]: i32) -> (i32, i32) {
-// CHECK: %[[VAL_4:.*]] = emitc.expression : i32 {
-// CHECK: %[[VAL_5:.*]] = mul %[[VAL_0]], %[[VAL_1]] : (i32, i32) -> i32
-// CHECK: %[[VAL_6:.*]] = sub %[[VAL_5]], %[[VAL_2]] : (i32, i32) -> i32
-// CHECK: yield %[[VAL_6]] : i32
-// CHECK: }
-// CHECK: %[[VAL_7:.*]] = emitc.expression : i32 {
-// CHECK: %[[VAL_8:.*]] = add %[[VAL_1]], %[[VAL_3]] : (i32, i32) -> i32
-// CHECK: %[[VAL_9:.*]] = div %[[VAL_8]], %[[VAL_2]] : (i32, i32) -> i32
-// CHECK: yield %[[VAL_9]] : i32
-// CHECK: }
-// CHECK: return %[[VAL_4]], %[[VAL_7]] : i32, i32
-// CHECK: }
+// CHECK-FORM-EXPRESSIONS-LABEL: func.func @multiple_expressions(
+// CHECK-FORM-EXPRESSIONS-SAME: %[[VAL_0:.*]]: i32, %[[VAL_1:.*]]: i32, %[[VAL_2:.*]]: i32, %[[VAL_3:.*]]: i32) -> (i32, i32) {
+// CHECK-FORM-EXPRESSIONS: %[[VAL_4:.*]] = emitc.expression : i32 {
+// CHECK-FORM-EXPRESSIONS: %[[VAL_5:.*]] = mul %[[VAL_0]], %[[VAL_1]] : (i32, i32) -> i32
+// CHECK-FORM-EXPRESSIONS: %[[VAL_6:.*]] = sub %[[VAL_5]], %[[VAL_2]] : (i32, i32) -> i32
+// CHECK-FORM-EXPRESSIONS: yield %[[VAL_6]] : i32
+// CHECK-FORM-EXPRESSIONS: }
+// CHECK-FORM-EXPRESSIONS: %[[VAL_7:.*]] = emitc.expression : i32 {
+// CHECK-FORM-EXPRESSIONS: %[[VAL_8:.*]] = add %[[VAL_1]], %[[VAL_3]] : (i32, i32) -> i32
+// CHECK-FORM-EXPRESSIONS: %[[VAL_9:.*]] = div %[[VAL_8]], %[[VAL_2]] : (i32, i32) -> i32
+// CHECK-FORM-EXPRESSIONS: yield %[[VAL_9]] : i32
+// CHECK-FORM-EXPRESSIONS: }
+// CHECK-FORM-EXPRESSIONS: return %[[VAL_4]], %[[VAL_7]] : i32, i32
+// CHECK-FORM-EXPRESSIONS: }
func.func @multiple_expressions(%arg0: i32, %arg1: i32, %arg2: i32, %arg3: i32) -> (i32, i32) {
%a = emitc.mul %arg0, %arg1 : (i32, i32) -> i32
@@ -43,19 +44,19 @@ func.func @multiple_expressions(%arg0: i32, %arg1: i32, %arg2: i32, %arg3: i32)
return %b, %d : i32, i32
}
-// CHECK-LABEL: func.func @expression_with_call(
-// CHECK-SAME: %[[VAL_0:.*]]: i32, %[[VAL_1:.*]]: i32, %[[VAL_2:.*]]: i32, %[[VAL_3:.*]]: i32) -> i1 {
-// CHECK: %[[VAL_4:.*]] = emitc.expression : i32 {
-// CHECK: %[[VAL_5:.*]] = mul %[[VAL_0]], %[[VAL_1]] : (i32, i32) -> i32
-// CHECK: %[[VAL_6:.*]] = call_opaque "foo"(%[[VAL_5]], %[[VAL_2]]) : (i32, i32) -> i32
-// CHECK: yield %[[VAL_6]] : i32
-// CHECK: }
-// CHECK: %[[VAL_7:.*]] = emitc.expression : i1 {
-// CHECK: %[[VAL_8:.*]] = cmp lt, %[[VAL_4]], %[[VAL_1]] : (i32, i32) -> i1
-// CHECK: yield %[[VAL_8]] : i1
-// CHECK: }
-// CHECK: return %[[VAL_7]] : i1
-// CHECK: }
+// CHECK-FORM-EXPRESSIONS-LABEL: func.func @expression_with_call(
+// CHECK-FORM-EXPRESSIONS-SAME: %[[VAL_0:.*]]: i32, %[[VAL_1:.*]]: i32, %[[VAL_2:.*]]: i32, %[[VAL_3:.*]]: i32) -> i1 {
+// CHECK-FORM-EXPRESSIONS: %[[VAL_4:.*]] = emitc.expression : i32 {
+// CHECK-FORM-EXPRESSIONS: %[[VAL_5:.*]] = mul %[[VAL_0]], %[[VAL_1]] : (i32, i32) -> i32
+// CHECK-FORM-EXPRESSIONS: %[[VAL_6:.*]] = call_opaque "foo"(%[[VAL_5]], %[[VAL_2]]) : (i32, i32) -> i32
+// CHECK-FORM-EXPRESSIONS: yield %[[VAL_6]] : i32
+// CHECK-FORM-EXPRESSIONS: }
+// CHECK-FORM-EXPRESSIONS: %[[VAL_7:.*]] = emitc.expression : i1 {
+// CHECK-FORM-EXPRESSIONS: %[[VAL_8:.*]] = cmp lt, %[[VAL_4]], %[[VAL_1]] : (i32, i32) -> i1
+// CHECK-FORM-EXPRESSIONS: yield %[[VAL_8]] : i1
+// CHECK-FORM-EXPRESSIONS: }
+// CHECK-FORM-EXPRESSIONS: return %[[VAL_7]] : i1
+// CHECK-FORM-EXPRESSIONS: }
func.func @expression_with_call(%arg0: i32, %arg1: i32, %arg2: i32, %arg3: i32) -> i1 {
%a = emitc.mul %arg0, %arg1 : (i32, i32) -> i32
@@ -64,17 +65,17 @@ func.func @expression_with_call(%arg0: i32, %arg1: i32, %arg2: i32, %arg3: i32)
return %c : i1
}
-// CHECK-LABEL: func.func @expression_with_dereference(
-// CHECK-SAME: %[[VAL_0:.*]]: i32, %[[VAL_1:.*]]: i32, %[[VAL_2:.*]]: !emitc.ptr<i32>) -> i1 {
-// CHECK: %[[VAL_3:.*]] = emitc.expression : i32 {
-// CHECK: %[[VAL_4:.*]] = apply "*"(%[[VAL_2]]) : (!emitc.ptr<i32>) -> i32
-// CHECK: yield %[[VAL_4]] : i32
-// CHECK: }
-// CHECK: %[[VAL_5:.*]] = emitc.expression : i1 {
-// CHECK: %[[VAL_6:.*]] = mul %[[VAL_0]], %[[VAL_1]] : (i32, i32) -> i32
-// CHECK: %[[VAL_7:.*]] = cmp lt, %[[VAL_6]], %[[VAL_3]] : (i32, i32) -> i1
-// CHECK: return %[[VAL_5]] : i1
-// CHECK: }
+// CHECK-FORM-EXPRESSIONS-LABEL: func.func @expression_with_dereference(
+// CHECK-FORM-EXPRESSIONS-SAME: %[[VAL_0:.*]]: i32, %[[VAL_1:.*]]: i32, %[[VAL_2:.*]]: !emitc.ptr<i32>) -> i1 {
+// CHECK-FORM-EXPRESSIONS: %[[VAL_3:.*]] = emitc.expression : i32 {
+// CHECK-FORM-EXPRESSIONS: %[[VAL_4:.*]] = apply "*"(%[[VAL_2]]) : (!emitc.ptr<i32>) -> i32
+// CHECK-FORM-EXPRESSIONS: yield %[[VAL_4]] : i32
+// CHECK-FORM-EXPRESSIONS: }
+// CHECK-FORM-EXPRESSIONS: %[[VAL_5:.*]] = emitc.expression : i1 {
+// CHECK-FORM-EXPRESSIONS: %[[VAL_6:.*]] = mul %[[VAL_0]], %[[VAL_1]] : (i32, i32) -> i32
+// CHECK-FORM-EXPRESSIONS: %[[VAL_7:.*]] = cmp lt, %[[VAL_6]], %[[VAL_3]] : (i32, i32) -> i1
+// CHECK-FORM-EXPRESSIONS: return %[[VAL_5]] : i1
+// CHECK-FORM-EXPRESSIONS: }
func.func @expression_with_dereference(%arg0: i32, %arg1: i32, %arg2: !emitc.ptr<i32>) -> i1 {
%a = emitc.mul %arg0, %arg1 : (i32, i32) -> i32
@@ -84,17 +85,17 @@ func.func @expression_with_dereference(%arg0: i32, %arg1: i32, %arg2: !emitc.ptr
}
-// CHECK-LABEL: func.func @expression_with_address_taken(
-// CHECK-SAME: %[[VAL_0:.*]]: i32, %[[VAL_1:.*]]: i32, %[[VAL_2:.*]]: !emitc.ptr<i32>) -> i1 {
-// CHECK: %[[VAL_3:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i32>
-// CHECK: %[[VAL_4:.*]] = emitc.expression : i1 {
-// CHECK: %[[VAL_5:.*]] = apply "&"(%[[VAL_3]]) : (!emitc.lvalue<i32>) -> !emitc.ptr<i32>
-// CHECK: %[[VAL_6:.*]] = add %[[VAL_5]], %[[VAL_1]] : (!emitc.ptr<i32>, i32) -> !emitc.ptr<i32>
-// CHECK: %[[VAL_7:.*]] = cmp lt, %[[VAL_6]], %[[VAL_2]] : (!emitc.ptr<i32>, !emitc.ptr<i32>) -> i1
-// CHECK: yield %[[VAL_7]] : i1
-// CHECK: }
-// CHECK: return %[[VAL_4]] : i1
-// CHECK: }
+// CHECK-FORM-EXPRESSIONS-LABEL: func.func @expression_with_address_taken(
+// CHECK-FORM-EXPRESSIONS-SAME: %[[VAL_0:.*]]: i32, %[[VAL_1:.*]]: i32, %[[VAL_2:.*]]: !emitc.ptr<i32>) -> i1 {
+// CHECK-FORM-EXPRESSIONS: %[[VAL_3:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i32>
+// CHECK-FORM-EXPRESSIONS: %[[VAL_4:.*]] = emitc.expression : i1 {
+// CHECK-FORM-EXPRESSIONS: %[[VAL_5:.*]] = apply "&"(%[[VAL_3]]) : (!emitc.lvalue<i32>) -> !emitc.ptr<i32>
+// CHECK-FORM-EXPRESSIONS: %[[VAL_6:.*]] = add %[[VAL_5]], %[[VAL_1]] : (!emitc.ptr<i32>, i32) -> !emitc.ptr<i32>
+// CHECK-FORM-EXPRESSIONS: %[[VAL_7:.*]] = cmp lt, %[[VAL_6]], %[[VAL_2]] : (!emitc.ptr<i32>, !emitc.ptr<i32>) -> i1
+// CHECK-FORM-EXPRESSIONS: yield %[[VAL_7]] : i1
+// CHECK-FORM-EXPRESSIONS: }
+// CHECK-FORM-EXPRESSIONS: return %[[VAL_4]] : i1
+// CHECK-FORM-EXPRESSIONS: }
func.func @expression_with_address_taken(%arg0: i32, %arg1: i32, %arg2: !emitc.ptr<i32>) -> i1 {
%0 = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i32>
@@ -104,14 +105,14 @@ func.func @expression_with_address_taken(%arg0: i32, %arg1: i32, %arg2: !emitc.p
return %c : i1
}
-// CHECK-LABEL: func.func @no_nested_expression(
-// CHECK-SAME: %[[VAL_0:.*]]: i32, %[[VAL_1:.*]]: i32) -> i1 {
-// CHECK: %[[VAL_2:.*]] = emitc.expression : i1 {
-// CHECK: %[[VAL_3:.*]] = cmp lt, %[[VAL_0]], %[[VAL_1]] : (i32, i32) -> i1
-// CHECK: yield %[[VAL_3]] : i1
-// CHECK: }
-// CHECK: return %[[VAL_2]] : i1
-// CHECK: }
+// CHECK-FORM-EXPRESSIONS-LABEL: func.func @no_nested_expression(
+// CHECK-FORM-EXPRESSIONS-SAME: %[[VAL_0:.*]]: i32, %[[VAL_1:.*]]: i32) -> i1 {
+// CHECK-FORM-EXPRESSIONS: %[[VAL_2:.*]] = emitc.expression : i1 {
+// CHECK-FORM-EXPRESSIONS: %[[VAL_3:.*]] = cmp lt, %[[VAL_0]], %[[VAL_1]] : (i32, i32) -> i1
+// CHECK-FORM-EXPRESSIONS: yield %[[VAL_3]] : i1
+// CHECK-FORM-EXPRESSIONS: }
+// CHECK-FORM-EXPRESSIONS: return %[[VAL_2]] : i1
+// CHECK-FORM-EXPRESSIONS: }
func.func @no_nested_expression(%arg0: i32, %arg1: i32) -> i1 {
%a = emitc.expression : i1 {
@@ -121,37 +122,35 @@ func.func @no_nested_expression(%arg0: i32, %arg1: i32) -> i1 {
return %a : i1
}
-
-// CHECK-LABEL: func.func @single_result_requirement
-// CHECK-NOT: emitc.expression
+// CHECK-FORM-EXPRESSIONS-LABEL: func.func @single_result_requirement
+// CHECK-FORM-EXPRESSIONS-NOT: emitc.expression
func.func @single_result_requirement() -> (i32, i32) {
%0:2 = emitc.call_opaque "foo" () : () -> (i32, i32)
return %0#0, %0#1 : i32, i32
}
-// CHECK-LABEL: func.func @expression_with_load(
-// CHECK-SAME: %[[VAL_0:.*]]: i32,
-// CHECK-SAME: %[[VAL_1:.*]]: !emitc.ptr<i32>) -> i1 {
-// CHECK: %[[VAL_2:.*]] = "emitc.constant"() <{value = 0 : i64}> : () -> i64
-// CHECK: %[[VAL_3:.*]] = "emitc.variable"() <{value = #emitc.opaque<"42">}> : () -> !emitc.lvalue<i32>
-// CHECK: %[[VAL_4:.*]] = emitc.expression : i32 {
-// CHECK: %[[VAL_5:.*]] = load %[[VAL_3]] : <i32>
-// CHECK: yield %[[VAL_5]] : i32
-// CHECK: }
-// CHECK: %[[VAL_6:.*]] = emitc.subscript %[[VAL_1]]{{\[}}%[[VAL_2]]] : (!emitc.ptr<i32>, i64) -> !emitc.lvalue<i32>
-// CHECK: %[[VAL_7:.*]] = emitc.expression : i32 {
-// CHECK: %[[VAL_8:.*]] = load %[[VAL_6]] : <i32>
-// CHECK: yield %[[VAL_8]] : i32
-// CHECK: }
-// CHECK: %[[VAL_9:.*]] = emitc.expression : i1 {
-// CHECK: %[[VAL_10:.*]] = add %[[VAL_4]], %[[VAL_7]] : (i32, i32) -> i32
-// CHECK: %[[VAL_11:.*]] = cmp lt, %[[VAL_10]], %[[VAL_0]] : (i32, i32) -> i1
-// CHECK: yield %[[VAL_11]] : i1
-// CHECK: }
-// CHECK: return %[[VAL_9]] : i1
-// CHECK: }
-
+// CHECK-FORM-EXPRESSIONS-LABEL: func.func @expression_with_load(
+// CHECK-FORM-EXPRESSIONS-SAME: %[[VAL_0:.*]]: i32,
+// CHECK-FORM-EXPRESSIONS-SAME: %[[VAL_1:.*]]: !emitc.ptr<i32>) -> i1 {
+// CHECK-FORM-EXPRESSIONS: %[[VAL_2:.*]] = "emitc.constant"() <{value = 0 : i64}> : () -> i64
+// CHECK-FORM-EXPRESSIONS: %[[VAL_3:.*]] = "emitc.variable"() <{value = #emitc.opaque<"42">}> : () -> !emitc.lvalue<i32>
+// CHECK-FORM-EXPRESSIONS: %[[VAL_4:.*]] = emitc.expression : i32 {
+// CHECK-FORM-EXPRESSIONS: %[[VAL_5:.*]] = load %[[VAL_3]] : <i32>
+// CHECK-FORM-EXPRESSIONS: yield %[[VAL_5]] : i32
+// CHECK-FORM-EXPRESSIONS: }
+// CHECK-FORM-EXPRESSIONS: %[[VAL_6:.*]] = emitc.subscript %[[VAL_1]]{{\[}}%[[VAL_2]]] : (!emitc.ptr<i32>, i64) -> !emitc.lvalue<i32>
+// CHECK-FORM-EXPRESSIONS: %[[VAL_7:.*]] = emitc.expression : i32 {
+// CHECK-FORM-EXPRESSIONS: %[[VAL_8:.*]] = load %[[VAL_6]] : <i32>
+// CHECK-FORM-EXPRESSIONS: yield %[[VAL_8]] : i32
+// CHECK-FORM-EXPRESSIONS: }
+// CHECK-FORM-EXPRESSIONS: %[[VAL_9:.*]] = emitc.expression : i1 {
+// CHECK-FORM-EXPRESSIONS: %[[VAL_10:.*]] = add %[[VAL_4]], %[[VAL_7]] : (i32, i32) -> i32
+// CHECK-FORM-EXPRESSIONS: %[[VAL_11:.*]] = cmp lt, %[[VAL_10]], %[[VAL_0]] : (i32, i32) -> i1
+// CHECK-FORM-EXPRESSIONS: yield %[[VAL_11]] : i1
+// CHECK-FORM-EXPRESSIONS: }
+// CHECK-FORM-EXPRESSIONS: return %[[VAL_9]] : i1
+// CHECK-FORM-EXPRESSIONS: }
func.func @expression_with_load(%arg0: i32, %arg1: !emitc.ptr<i32>) -> i1 {
%c0 = "emitc.constant"() {value = 0 : i64} : () -> i64
@@ -163,3 +162,61 @@ func.func @expression_with_load(%arg0: i32, %arg1: !emitc.ptr<i32>) -> i1 {
%c = emitc.cmp lt, %b, %arg0 :(i32, i32) -> i1
return %c : i1
}
+
+// -----
+
+emitc.func @foo(%arg0 : !emitc.array<1xf32>) {
+ emitc.call_opaque "bar" (%arg0) : (!emitc.array<1xf32>) -> ()
+ emitc.return
+}
+
+// CHECK-WRAP-IN-CLASS: module {
+// CHECK-WRAP-IN-CLASS: emitc.class @fooClass {
+// CHECK-WRAP-IN-CLASS: emitc.field @fieldName0 : !emitc.array<1xf32>
+// CHECK-WRAP-IN-CLASS: emitc.func @execute() {
+// CHECK-WRAP-IN-CLASS: %0 = get_field @fieldName0 : !emitc.array<1xf32>
+// CHECK-WRAP-IN-CLASS: call_opaque "bar"(%0) : (!emitc.array<1xf32>) -> ()
+// CHECK-WRAP-IN-CLASS: return
+// CHECK-WRAP-IN-CLASS: }
+// CHECK-WRAP-IN-CLASS: }
+// CHECK-WRAP-IN-CLASS: }
+
+// -----
+
+module attributes { } {
+ emitc.func @model(%arg0: !emitc.array<1xf32> {emitc.name_hint = "another_feature"},
+ %arg1: !emitc.array<1xf32> {emitc.name_hint = "some_feature"},
+ %arg2: !emitc.array<1xf32> {emitc.name_hint = "output_0"}) attributes { } {
+ %0 = "emitc.constant"() <{value = 0 : index}> : () -> !emitc.size_t
+ %1 = subscript %arg1[%0] : (!emitc.array<1xf32>, !emitc.size_t) -> !emitc.lvalue<f32>
+ %2 = load %1 : <f32>
+ %3 = subscript %arg0[%0] : (!emitc.array<1xf32>, !emitc.size_t) -> !emitc.lvalue<f32>
+ %4 = load %3 : <f32>
+ %5 = add %2, %4 : (f32, f32) -> f32
+ %6 = subscript %arg2[%0] : (!emitc.array<1xf32>, !emitc.size_t) -> !emitc.lvalue<f32>
+ assign %5 : f32 to %6 : <f32>
+ return
+ }
+}
+
+// CHECK-WRAP-IN-CLASS: module {
+// CHECK-WRAP-IN-CLASS: emitc.class @modelClass {
+// CHECK-WRAP-IN-CLASS: emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.name_hint = "another_feature"}
+// CHECK-WRAP-IN-CLASS: emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.name_hint = "some_feature"}
+// CHECK-WRAP-IN-CLASS: emitc.field @fieldName2 : !emitc.array<1xf32> {emitc.name_hint = "output_0"}
+// CHECK-WRAP-IN-CLASS: emitc.func @execute() {
+// CHECK-WRAP-IN-CLASS: get_field @fieldName0 : !emitc.array<1xf32>
+// CHECK-WRAP-IN-CLASS: get_field @fieldName1 : !emitc.array<1xf32>
+// CHECK-WRAP-IN-CLASS: get_field @fieldName2 : !emitc.array<1xf32>
+// CHECK-WRAP-IN-CLASS: "emitc.constant"() <{value = 0 : index}> : () -> !emitc.size_t
+// CHECK-WRAP-IN-CLASS: subscript {{.*}}[{{.*}}] : (!emitc.array<1xf32>, !emitc.size_t) -> !emitc.lvalue<f32>
+// CHECK-WRAP-IN-CLASS: load {{.*}} : <f32>
+// CHECK-WRAP-IN-CLASS: subscript {{.*}}[{{.*}}] : (!emitc.array<1xf32>, !emitc.size_t) -> !emitc.lvalue<f32>
+// CHECK-WRAP-IN-CLASS: load {{.*}} : <f32>
+// CHECK-WRAP-IN-CLASS: add {{.*}}, {{.*}} : (f32, f32) -> f32
+// CHECK-WRAP-IN-CLASS: subscript {{.*}}[{{.*}}] : (!emitc.array<1xf32>, !emitc.size_t) -> !emitc.lvalue<f32>
+// CHECK-WRAP-IN-CLASS: assign {{.*}} : f32 to {{.*}} : <f32>
+// CHECK-WRAP-IN-CLASS: return
+// CHECK-WRAP-IN-CLASS: }
+// CHECK-WRAP-IN-CLASS: }
+// CHECK-WRAP-IN-CLASS: }
diff --git a/mlir/test/Dialect/EmitC/types.mlir b/mlir/test/Dialect/EmitC/types.mlir
index d4dd94457f39b..ce1e03a83e5d1 100644
--- a/mlir/test/Dialect/EmitC/types.mlir
+++ b/mlir/test/Dialect/EmitC/types.mlir
@@ -1,6 +1,6 @@
-// RUN: mlir-opt -verify-diagnostics -allow-unregistered-dialect %s | FileCheck %s
-// check parser
-// RUN: mlir-opt -verify-diagnostics -allow-unregistered-dialect %s | mlir-opt -verify-diagnostics --allow-unregistered-dialect | FileCheck %s
+// RUN: mlir-opt %s -allow-unregistered-dialect | FileCheck %s
+// Check parser
+// RUN: mlir-opt %s -allow-unregistered-dialect | mlir-opt -allow-unregistered-dialect | FileCheck %s
// CHECK-LABEL: func @array_types(
func.func @array_types(
diff --git a/mlir/test/Dialect/EmitC/wrap_emitc_func_in_class.mlir b/mlir/test/Dialect/EmitC/wrap_emitc_func_in_class.mlir
deleted file mode 100644
index 029fa78a3f528..0000000000000
--- a/mlir/test/Dialect/EmitC/wrap_emitc_func_in_class.mlir
+++ /dev/null
@@ -1,40 +0,0 @@
-// RUN: mlir-opt --wrap-emitc-func-in-class %s | FileCheck %s
-
-module attributes { } {
- emitc.func @model(%arg0: !emitc.array<1xf32> {emitc.name_hint = "another_feature"},
- %arg1: !emitc.array<1xf32> {emitc.name_hint = "some_feature"},
- %arg2: !emitc.array<1xf32> {emitc.name_hint = "output_0"}) attributes { } {
- %0 = "emitc.constant"() <{value = 0 : index}> : () -> !emitc.size_t
- %1 = subscript %arg1[%0] : (!emitc.array<1xf32>, !emitc.size_t) -> !emitc.lvalue<f32>
- %2 = load %1 : <f32>
- %3 = subscript %arg0[%0] : (!emitc.array<1xf32>, !emitc.size_t) -> !emitc.lvalue<f32>
- %4 = load %3 : <f32>
- %5 = add %2, %4 : (f32, f32) -> f32
- %6 = subscript %arg2[%0] : (!emitc.array<1xf32>, !emitc.size_t) -> !emitc.lvalue<f32>
- assign %5 : f32 to %6 : <f32>
- return
- }
-}
-
-
-// CHECK: module {
-// CHECK-NEXT: emitc.class @modelClass {
-// CHECK-NEXT: emitc.field @fieldName0 : !emitc.array<1xf32> {emitc.name_hint = "another_feature"}
-// CHECK-NEXT: emitc.field @fieldName1 : !emitc.array<1xf32> {emitc.name_hint = "some_feature"}
-// CHECK-NEXT: emitc.field @fieldName2 : !emitc.array<1xf32> {emitc.name_hint = "output_0"}
-// CHECK-NEXT: emitc.func @execute() {
-// CHECK-NEXT: get_field @fieldName0 : !emitc.array<1xf32>
-// CHECK-NEXT: get_field @fieldName1 : !emitc.array<1xf32>
-// CHECK-NEXT: get_field @fieldName2 : !emitc.array<1xf32>
-// CHECK-NEXT: "emitc.constant"() <{value = 0 : index}> : () -> !emitc.size_t
-// CHECK-NEXT: subscript {{.*}}[{{.*}}] : (!emitc.array<1xf32>, !emitc.size_t) -> !emitc.lvalue<f32>
-// CHECK-NEXT: load {{.*}} : <f32>
-// CHECK-NEXT: subscript {{.*}}[{{.*}}] : (!emitc.array<1xf32>, !emitc.size_t) -> !emitc.lvalue<f32>
-// CHECK-NEXT: load {{.*}} : <f32>
-// CHECK-NEXT: add {{.*}}, {{.*}} : (f32, f32) -> f32
-// CHECK-NEXT: subscript {{.*}}[{{.*}}] : (!emitc.array<1xf32>, !emitc.size_t) -> !emitc.lvalue<f32>
-// CHECK-NEXT: assign {{.*}} : f32 to {{.*}} : <f32>
-// CHECK-NEXT: return
-// CHECK-NEXT: }
-// CHECK-NEXT: }
-// CHECK-NEXT: }
diff --git a/mlir/test/Dialect/EmitC/wrap_emitc_func_in_class_noAttr.mlir b/mlir/test/Dialect/EmitC/wrap_emitc_func_in_class_noAttr.mlir
deleted file mode 100644
index 92ed20c4b14e3..0000000000000
--- a/mlir/test/Dialect/EmitC/wrap_emitc_func_in_class_noAttr.mlir
+++ /dev/null
@@ -1,17 +0,0 @@
-// RUN: mlir-opt --wrap-emitc-func-in-class %s | FileCheck %s
-
-emitc.func @foo(%arg0 : !emitc.array<1xf32>) {
- emitc.call_opaque "bar" (%arg0) : (!emitc.array<1xf32>) -> ()
- emitc.return
-}
-
-// CHECK: module {
-// CHECK-NEXT: emitc.class @fooClass {
-// CHECK-NEXT: emitc.field @fieldName0 : !emitc.array<1xf32>
-// CHECK-NEXT: emitc.func @execute() {
-// CHECK-NEXT: %0 = get_field @fieldName0 : !emitc.array<1xf32>
-// CHECK-NEXT: call_opaque "bar"(%0) : (!emitc.array<1xf32>) -> ()
-// CHECK-NEXT: return
-// CHECK-NEXT: }
-// CHECK-NEXT: }
-// CHECK-NEXT: }
diff --git a/mlir/test/Target/Cpp/class.mlir b/mlir/test/Target/Cpp/class.mlir
new file mode 100644
index 0000000000000..6e777ea183268
--- /dev/null
+++ b/mlir/test/Target/Cpp/class.mlir
@@ -0,0 +1,78 @@
+// RUN: mlir-translate -mlir-to-cpp %s | FileCheck %s
+
+emitc.class @modelClass {
+ emitc.field @fieldName0 : !emitc.array<1xf32>
+ emitc.field @fieldName1 : !emitc.array<1xf32>
+ emitc.func @execute() {
+ %0 = "emitc.constant"() <{value = 0 : index}> : () -> !emitc.size_t
+ %1 = get_field @fieldName0 : !emitc.array<1xf32>
+ %2 = get_field @fieldName1 : !emitc.array<1xf32>
+ %3 = subscript %1[%0] : (!emitc.array<1xf32>, !emitc.size_t) -> !emitc.lvalue<f32>
+ return
+ }
+}
+
+// CHECK-LABEL: class modelClass {
+// CHECK-NEXT: public:
+// CHECK-NEXT: float fieldName0[1];
+// CHECK-NEXT: float fieldName1[1];
+// CHECK-NEXT: void execute() {
+// CHECK-NEXT: size_t v1 = 0;
+// CHECK-NEXT: return;
+// CHECK-NEXT: }
+// CHECK-NEXT: };
+
+emitc.class final @finalClass {
+ emitc.field @fieldName0 : !emitc.array<1xf32>
+ emitc.field @fieldName1 : !emitc.array<1xf32>
+ emitc.func @execute() {
+ %0 = "emitc.constant"() <{value = 0 : index}> : () -> !emitc.size_t
+ %1 = get_field @fieldName0 : !emitc.array<1xf32>
+ %2 = get_field @fieldName1 : !emitc.array<1xf32>
+ %3 = subscript %1[%0] : (!emitc.array<1xf32>, !emitc.size_t) -> !emitc.lvalue<f32>
+ return
+ }
+}
+
+// CHECK-LABEL: class finalClass final {
+// CHECK-NEXT: public:
+// CHECK-NEXT: float fieldName0[1];
+// CHECK-NEXT: float fieldName1[1];
+// CHECK-NEXT: void execute() {
+// CHECK-NEXT: size_t v1 = 0;
+// CHECK-NEXT: return;
+// CHECK-NEXT: }
+// CHECK-NEXT: };
+
+emitc.class @mainClass {
+ emitc.field @fieldName0 : !emitc.array<2xf32> = dense<0.0> {attrs = {emitc.name_hint = "another_feature"}}
+ emitc.func @get_fieldName0() {
+ %0 = emitc.get_field @fieldName0 : !emitc.array<2xf32>
+ return
+ }
+}
+
+// CHECK-LABEL: class mainClass {
+// CHECK-NEXT: public:
+// CHECK-NEXT: float fieldName0[2] = {0.0e+00f, 0.0e+00f};
+// CHECK-NEXT: void get_fieldName0() {
+// CHECK-NEXT: return;
+// CHECK-NEXT: }
+// CHECK-NEXT: };
+
+emitc.class @reflectionClass {
+ emitc.field @reflectionMap : !emitc.opaque<"const std::map<std::string, std::string>"> = #emitc.opaque<"{ { \22another_feature\22, \22fieldName0\22 } }">
+ emitc.func @get_reflectionMap() {
+ %0 = emitc.get_field @reflectionMap : !emitc.opaque<"const std::map<std::string, std::string>">
+ return
+ }
+}
+
+// CHECK-LABEL: class reflectionClass {
+// CHECK-NEXT: public:
+// CHECK-NEXT: const std::map<std::string, std::string> reflectionMap = { { "another_feature", "fieldName0" } };
+// CHECK-NEXT: void get_reflectionMap() {
+// CHECK-NEXT: return;
+// CHECK-NEXT: }
+// CHECK-NEXT: };
+
diff --git a/mlir/test/mlir-translate/emitc_classops.mlir b/mlir/test/mlir-translate/emitc_classops.mlir
deleted file mode 100644
index d880f9b16dfc6..0000000000000
--- a/mlir/test/mlir-translate/emitc_classops.mlir
+++ /dev/null
@@ -1,78 +0,0 @@
-// RUN: mlir-translate --mlir-to-cpp %s | FileCheck %s
-
-emitc.class @modelClass {
- emitc.field @fieldName0 : !emitc.array<1xf32>
- emitc.field @fieldName1 : !emitc.array<1xf32>
- emitc.func @execute() {
- %0 = "emitc.constant"() <{value = 0 : index}> : () -> !emitc.size_t
- %1 = get_field @fieldName0 : !emitc.array<1xf32>
- %2 = get_field @fieldName1 : !emitc.array<1xf32>
- %3 = subscript %1[%0] : (!emitc.array<1xf32>, !emitc.size_t) -> !emitc.lvalue<f32>
- return
- }
-}
-
-// CHECK-LABEL: class modelClass {
-// CHECK-NEXT: public:
-// CHECK-NEXT: float fieldName0[1];
-// CHECK-NEXT: float fieldName1[1];
-// CHECK-NEXT: void execute() {
-// CHECK-NEXT: size_t v1 = 0;
-// CHECK-NEXT: return;
-// CHECK-NEXT: }
-// CHECK-NEXT: };
-
-emitc.class final @finalClass {
- emitc.field @fieldName0 : !emitc.array<1xf32>
- emitc.field @fieldName1 : !emitc.array<1xf32>
- emitc.func @execute() {
- %0 = "emitc.constant"() <{value = 0 : index}> : () -> !emitc.size_t
- %1 = get_field @fieldName0 : !emitc.array<1xf32>
- %2 = get_field @fieldName1 : !emitc.array<1xf32>
- %3 = subscript %1[%0] : (!emitc.array<1xf32>, !emitc.size_t) -> !emitc.lvalue<f32>
- return
- }
-}
-
-// CHECK-LABEL: class finalClass final {
-// CHECK-NEXT: public:
-// CHECK-NEXT: float fieldName0[1];
-// CHECK-NEXT: float fieldName1[1];
-// CHECK-NEXT: void execute() {
-// CHECK-NEXT: size_t v1 = 0;
-// CHECK-NEXT: return;
-// CHECK-NEXT: }
-// CHECK-NEXT: };
-
-emitc.class @mainClass {
- emitc.field @fieldName0 : !emitc.array<2xf32> = dense<0.0> {attrs = {emitc.name_hint = "another_feature"}}
- emitc.func @get_fieldName0() {
- %0 = emitc.get_field @fieldName0 : !emitc.array<2xf32>
- return
- }
-}
-
-// CHECK-LABEL: class mainClass {
-// CHECK-NEXT: public:
-// CHECK-NEXT: float fieldName0[2] = {0.0e+00f, 0.0e+00f};
-// CHECK-NEXT: void get_fieldName0() {
-// CHECK-NEXT: return;
-// CHECK-NEXT: }
-// CHECK-NEXT: };
-
-emitc.class @reflectionClass {
- emitc.field @reflectionMap : !emitc.opaque<"const std::map<std::string, std::string>"> = #emitc.opaque<"{ { \22another_feature\22, \22fieldName0\22 } }">
- emitc.func @get_reflectionMap() {
- %0 = emitc.get_field @reflectionMap : !emitc.opaque<"const std::map<std::string, std::string>">
- return
- }
-}
-
-// CHECK-LABEL: class reflectionClass {
-// CHECK-NEXT: public:
-// CHECK-NEXT: const std::map<std::string, std::string> reflectionMap = { { "another_feature", "fieldName0" } };
-// CHECK-NEXT: void get_reflectionMap() {
-// CHECK-NEXT: return;
-// CHECK-NEXT: }
-// CHECK-NEXT: };
-
More information about the Mlir-commits
mailing list