[Mlir-commits] [mlir] 1fa1251 - [mlir][emitc] Add a variable op
Marius Brehler
llvmlistbot at llvm.org
Thu Feb 24 07:28:32 PST 2022
Author: Marius Brehler
Date: 2022-02-24T15:25:21Z
New Revision: 1fa125111607e2e00e3ce23f69fbc0ce9bb2a207
URL: https://github.com/llvm/llvm-project/commit/1fa125111607e2e00e3ce23f69fbc0ce9bb2a207
DIFF: https://github.com/llvm/llvm-project/commit/1fa125111607e2e00e3ce23f69fbc0ce9bb2a207.diff
LOG: [mlir][emitc] Add a variable op
This adds a variable op, emitted as C/C++ locale variable, which can be
used if the `emitc.constant` op is not sufficient.
As an example, the canonicalization pass would transform
```mlir
%0 = "emitc.constant"() {value = 0 : i32} : () -> i32
%1 = "emitc.constant"() {value = 0 : i32} : () -> i32
%2 = emitc.apply "&"(%0) : (i32) -> !emitc.ptr<i32>
%3 = emitc.apply "&"(%1) : (i32) -> !emitc.ptr<i32>
emitc.call "write"(%2, %3) : (!emitc.ptr<i32>, !emitc.ptr<i32>) -> ()
```
into
```mlir
%0 = "emitc.constant"() {value = 0 : i32} : () -> i32
%1 = emitc.apply "&"(%0) : (i32) -> !emitc.ptr<i32>
%2 = emitc.apply "&"(%0) : (i32) -> !emitc.ptr<i32>
emitc.call "write"(%1, %2) : (!emitc.ptr<i32>, !emitc.ptr<i32>) -> ()
```
resulting in pointer aliasing, as %1 and %2 point to the same address.
In such a case, the `emitc.variable` operation can be used instead.
Reviewed By: jpienaar
Differential Revision: https://reviews.llvm.org/D120098
Added:
mlir/test/Target/Cpp/variable.mlir
Modified:
mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
mlir/lib/Dialect/EmitC/IR/EmitC.cpp
mlir/lib/Target/Cpp/TranslateToCpp.cpp
mlir/test/Dialect/EmitC/invalid_ops.mlir
mlir/test/Dialect/EmitC/ops.mlir
mlir/test/Target/Cpp/const.mlir
Removed:
################################################################################
diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
index 7135cd9f12c62..e67ac05ebaf20 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
@@ -44,6 +44,7 @@ def EmitC_ApplyOp : EmitC_Op<"apply", []> {
```
}];
+ // TODO: Disallow to apply the operator & (address of) to emitc.constant operations.
let arguments = (ins
Arg<StrAttr, "the operator to apply">:$applicableOperator,
AnyType:$operand
@@ -94,7 +95,8 @@ def EmitC_ConstantOp : EmitC_Op<"constant", [ConstantLike]> {
specified by an attribute. This can be used to form simple integer and
floating point constants, as well as more exotic things like tensor
constants. The `constant` operation also supports the EmitC opaque
- attribute and the EmitC opaque type.
+ attribute and the EmitC opaque type. Since folding is supported,
+ it should not be used with pointers.
Example:
@@ -102,10 +104,10 @@ def EmitC_ConstantOp : EmitC_Op<"constant", [ConstantLike]> {
// Integer constant
%0 = "emitc.constant"(){value = 42 : i32} : () -> i32
- // Constant emitted as `int32_t* = NULL;`
+ // Constant emitted as `char = CHAR_MIN;`
%1 = "emitc.constant"()
- {value = #emitc.opaque<"NULL"> : !emitc.opaque<"int32_t*">}
- : () -> !emitc.opaque<"int32_t*">
+ {value = #emitc.opaque<"CHAR_MIN"> : !emitc.opaque<"char">}
+ : () -> !emitc.opaque<"char">
```
}];
@@ -113,6 +115,7 @@ def EmitC_ConstantOp : EmitC_Op<"constant", [ConstantLike]> {
let results = (outs AnyType);
let hasFolder = 1;
+ // TODO: Disallow empty constants.
let hasVerifier = 1;
}
@@ -146,4 +149,45 @@ def EmitC_IncludeOp
let hasCustomAssemblyFormat = 1;
}
+def EmitC_VariableOp : EmitC_Op<"variable", []> {
+ let summary = "Variable operation";
+ let description = [{
+ The `variable` operation produces an SSA value equal to some value
+ specified by an attribute. This can be used to form simple integer and
+ floating point variables, as well as more exotic things like tensor
+ variables. The `variable` operation also supports the EmitC opaque
+ attribute and the EmitC opaque type. If further supports the EmitC
+ pointer type, whereas folding is not supported.
+ The `variable` is emitted as a C/C++ local variable.
+
+ Example:
+
+ ```mlir
+ // Integer variable
+ %0 = "emitc.variable"(){value = 42 : i32} : () -> i32
+
+ // Variable emitted as `int32_t* = NULL;`
+ %1 = "emitc.variable"()
+ {value = #emitc.opaque<"NULL"> : !emitc.opaque<"int32_t*">}
+ : () -> !emitc.opaque<"int32_t*">
+ ```
+
+ Since folding is not supported, it can be used with pointers.
+ As an example, it is valid to create pointers to `variable` operations
+ by using `apply` operations and pass these to a `call` operation.
+ ```mlir
+ %0 = "emitc.variable"() {value = 0 : i32} : () -> i32
+ %1 = "emitc.variable"() {value = 0 : i32} : () -> i32
+ %2 = emitc.apply "&"(%0) : (i32) -> !emitc.ptr<i32>
+ %3 = emitc.apply "&"(%1) : (i32) -> !emitc.ptr<i32>
+ emitc.call "write"(%2, %3) : (!emitc.ptr<i32>, !emitc.ptr<i32>) -> ()
+ ```
+ }];
+
+ let arguments = (ins AnyAttr:$value);
+ let results = (outs AnyType);
+
+ let hasVerifier = 1;
+}
+
#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 6a101fc1eb96e..06e5a6b536417 100644
--- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
+++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
@@ -152,6 +152,20 @@ ParseResult IncludeOp::parse(OpAsmParser &parser, OperationState &result) {
return success();
}
+//===----------------------------------------------------------------------===//
+// VariableOp
+//===----------------------------------------------------------------------===//
+
+/// The variable op requires that the attribute's type matches the return type.
+LogicalResult emitc::VariableOp::verify() {
+ Attribute value = valueAttr();
+ Type type = getType();
+ if (!value.getType().isa<NoneType>() && type != value.getType())
+ return emitOpError() << "requires attribute's type (" << value.getType()
+ << ") to match op's return type (" << type << ")";
+ return success();
+}
+
//===----------------------------------------------------------------------===//
// TableGen'd op method definitions
//===----------------------------------------------------------------------===//
diff --git a/mlir/lib/Target/Cpp/TranslateToCpp.cpp b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
index eabda3234395d..69b6cbfee64bf 100644
--- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp
+++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
@@ -221,6 +221,14 @@ static LogicalResult printOperation(CppEmitter &emitter,
return printConstantOp(emitter, operation, value);
}
+static LogicalResult printOperation(CppEmitter &emitter,
+ emitc::VariableOp variableOp) {
+ Operation *operation = variableOp.getOperation();
+ Attribute value = variableOp.value();
+
+ return printConstantOp(emitter, operation, value);
+}
+
static LogicalResult printOperation(CppEmitter &emitter,
arith::ConstantOp constantOp) {
Operation *operation = constantOp.getOperation();
@@ -903,7 +911,7 @@ LogicalResult CppEmitter::emitOperation(Operation &op, bool trailingSemicolon) {
llvm::TypeSwitch<Operation *, LogicalResult>(&op)
// EmitC ops.
.Case<emitc::ApplyOp, emitc::CallOp, emitc::ConstantOp,
- emitc::IncludeOp>(
+ emitc::IncludeOp, emitc::VariableOp>(
[&](auto op) { return printOperation(*this, op); })
// SCF ops.
.Case<scf::ForOp, scf::IfOp, scf::YieldOp>(
diff --git a/mlir/test/Dialect/EmitC/invalid_ops.mlir b/mlir/test/Dialect/EmitC/invalid_ops.mlir
index e86664627c368..9ff1f8a0ad850 100644
--- a/mlir/test/Dialect/EmitC/invalid_ops.mlir
+++ b/mlir/test/Dialect/EmitC/invalid_ops.mlir
@@ -9,8 +9,8 @@ func @const_attribute_return_type_1() {
// -----
func @const_attribute_return_type_2() {
- // expected-error @+1 {{'emitc.constant' op requires attribute's type ('!emitc.opaque<"int32_t*">') to match op's return type ('!emitc.opaque<"int32_t">')}}
- %c0 = "emitc.constant"(){value = "nullptr" : !emitc.opaque<"int32_t*">} : () -> !emitc.opaque<"int32_t">
+ // expected-error @+1 {{'emitc.constant' op requires attribute's type ('!emitc.opaque<"char">') to match op's return type ('!emitc.opaque<"mychar">')}}
+ %c0 = "emitc.constant"(){value = "CHAR_MIN" : !emitc.opaque<"char">} : () -> !emitc.opaque<"mychar">
return
}
@@ -77,3 +77,19 @@ func @illegal_operator(%arg : i32) {
%2 = emitc.apply "+"(%arg) : (i32) -> !emitc.opaque<"int32_t*">
return
}
+
+// -----
+
+func @var_attribute_return_type_1() {
+ // expected-error @+1 {{'emitc.variable' op requires attribute's type ('i64') to match op's return type ('i32')}}
+ %c0 = "emitc.variable"(){value = 42: i64} : () -> i32
+ return
+}
+
+// -----
+
+func @var_attribute_return_type_2() {
+ // expected-error @+1 {{'emitc.variable' op requires attribute's type ('!emitc.opaque<"int32_t*">') to match op's return type ('!emitc.opaque<"int32_t">')}}
+ %c0 = "emitc.variable"(){value = "nullptr" : !emitc.opaque<"int32_t*">} : () -> !emitc.opaque<"int32_t">
+ return
+}
diff --git a/mlir/test/Dialect/EmitC/ops.mlir b/mlir/test/Dialect/EmitC/ops.mlir
index 25d6f2adf90af..f6526cbaa0251 100644
--- a/mlir/test/Dialect/EmitC/ops.mlir
+++ b/mlir/test/Dialect/EmitC/ops.mlir
@@ -12,7 +12,7 @@ func @f(%arg0: i32, %f: !emitc.opaque<"int32_t">) {
return
}
-func @c(%arg0: i32) {
+func @c() {
%1 = "emitc.constant"(){value = 42 : i32} : () -> i32
return
}
diff --git a/mlir/test/Target/Cpp/const.mlir b/mlir/test/Target/Cpp/const.mlir
index c9c24d3f15580..1cc5336d9a992 100644
--- a/mlir/test/Target/Cpp/const.mlir
+++ b/mlir/test/Target/Cpp/const.mlir
@@ -1,15 +1,13 @@
// RUN: mlir-translate -mlir-to-cpp %s | FileCheck %s -check-prefix=CPP-DEFAULT
// RUN: mlir-translate -mlir-to-cpp -declare-variables-at-top %s | FileCheck %s -check-prefix=CPP-DECLTOP
-
func @emitc_constant() {
%c0 = "emitc.constant"(){value = #emitc.opaque<""> : i32} : () -> i32
%c1 = "emitc.constant"(){value = 42 : i32} : () -> i32
%c2 = "emitc.constant"(){value = -1 : i32} : () -> i32
%c3 = "emitc.constant"(){value = -1 : si8} : () -> si8
%c4 = "emitc.constant"(){value = 255 : ui8} : () -> ui8
- %c5 = "emitc.constant"(){value = #emitc.opaque<""> : !emitc.opaque<"int32_t*">} : () -> !emitc.opaque<"int32_t*">
- %c6 = "emitc.constant"(){value = #emitc.opaque<"NULL"> : !emitc.opaque<"int32_t*">} : () -> !emitc.opaque<"int32_t*">
+ %c5 = "emitc.constant"(){value = #emitc.opaque<"CHAR_MIN"> : !emitc.opaque<"char">} : () -> !emitc.opaque<"char">
return
}
// CPP-DEFAULT: void emitc_constant() {
@@ -18,8 +16,7 @@ func @emitc_constant() {
// CPP-DEFAULT-NEXT: int32_t [[V2:[^ ]*]] = -1;
// CPP-DEFAULT-NEXT: int8_t [[V3:[^ ]*]] = -1;
// CPP-DEFAULT-NEXT: uint8_t [[V4:[^ ]*]] = 255;
-// CPP-DEFAULT-NEXT: int32_t* [[V5:[^ ]*]];
-// CPP-DEFAULT-NEXT: int32_t* [[V6:[^ ]*]] = NULL;
+// CPP-DEFAULT-NEXT: char [[V5:[^ ]*]] = CHAR_MIN;
// CPP-DECLTOP: void emitc_constant() {
// CPP-DECLTOP-NEXT: int32_t [[V0:[^ ]*]];
@@ -27,12 +24,10 @@ func @emitc_constant() {
// CPP-DECLTOP-NEXT: int32_t [[V2:[^ ]*]];
// CPP-DECLTOP-NEXT: int8_t [[V3:[^ ]*]];
// CPP-DECLTOP-NEXT: uint8_t [[V4:[^ ]*]];
-// CPP-DECLTOP-NEXT: int32_t* [[V5:[^ ]*]];
-// CPP-DECLTOP-NEXT: int32_t* [[V6:[^ ]*]];
+// CPP-DECLTOP-NEXT: char [[V5:[^ ]*]];
// CPP-DECLTOP-NEXT: ;
// CPP-DECLTOP-NEXT: [[V1]] = 42;
// CPP-DECLTOP-NEXT: [[V2]] = -1;
// CPP-DECLTOP-NEXT: [[V3]] = -1;
// CPP-DECLTOP-NEXT: [[V4]] = 255;
-// CPP-DECLTOP-NEXT: ;
-// CPP-DECLTOP-NEXT: [[V6]] = NULL;
+// CPP-DECLTOP-NEXT: [[V5]] = CHAR_MIN;
diff --git a/mlir/test/Target/Cpp/variable.mlir b/mlir/test/Target/Cpp/variable.mlir
new file mode 100644
index 0000000000000..d5db23713761a
--- /dev/null
+++ b/mlir/test/Target/Cpp/variable.mlir
@@ -0,0 +1,37 @@
+// RUN: mlir-translate -mlir-to-cpp %s | FileCheck %s -check-prefix=CPP-DEFAULT
+// RUN: mlir-translate -mlir-to-cpp -declare-variables-at-top %s | FileCheck %s -check-prefix=CPP-DECLTOP
+
+func @emitc_variable() {
+ %c0 = "emitc.variable"(){value = #emitc.opaque<""> : i32} : () -> i32
+ %c1 = "emitc.variable"(){value = 42 : i32} : () -> i32
+ %c2 = "emitc.variable"(){value = -1 : i32} : () -> i32
+ %c3 = "emitc.variable"(){value = -1 : si8} : () -> si8
+ %c4 = "emitc.variable"(){value = 255 : ui8} : () -> ui8
+ %c5 = "emitc.variable"(){value = #emitc.opaque<""> : !emitc.ptr<i32>} : () -> !emitc.ptr<i32>
+ %c6 = "emitc.variable"(){value = #emitc.opaque<"NULL"> : !emitc.ptr<i32>} : () -> !emitc.ptr<i32>
+ return
+}
+// CPP-DEFAULT: void emitc_variable() {
+// CPP-DEFAULT-NEXT: int32_t [[V0:[^ ]*]];
+// CPP-DEFAULT-NEXT: int32_t [[V1:[^ ]*]] = 42;
+// CPP-DEFAULT-NEXT: int32_t [[V2:[^ ]*]] = -1;
+// CPP-DEFAULT-NEXT: int8_t [[V3:[^ ]*]] = -1;
+// CPP-DEFAULT-NEXT: uint8_t [[V4:[^ ]*]] = 255;
+// CPP-DEFAULT-NEXT: int32_t* [[V5:[^ ]*]];
+// CPP-DEFAULT-NEXT: int32_t* [[V6:[^ ]*]] = NULL;
+
+// CPP-DECLTOP: void emitc_variable() {
+// CPP-DECLTOP-NEXT: int32_t [[V0:[^ ]*]];
+// CPP-DECLTOP-NEXT: int32_t [[V1:[^ ]*]];
+// CPP-DECLTOP-NEXT: int32_t [[V2:[^ ]*]];
+// CPP-DECLTOP-NEXT: int8_t [[V3:[^ ]*]];
+// CPP-DECLTOP-NEXT: uint8_t [[V4:[^ ]*]];
+// CPP-DECLTOP-NEXT: int32_t* [[V5:[^ ]*]];
+// CPP-DECLTOP-NEXT: int32_t* [[V6:[^ ]*]];
+// CPP-DECLTOP-NEXT: ;
+// CPP-DECLTOP-NEXT: [[V1]] = 42;
+// CPP-DECLTOP-NEXT: [[V2]] = -1;
+// CPP-DECLTOP-NEXT: [[V3]] = -1;
+// CPP-DECLTOP-NEXT: [[V4]] = 255;
+// CPP-DECLTOP-NEXT: ;
+// CPP-DECLTOP-NEXT: [[V6]] = NULL;
More information about the Mlir-commits
mailing list