[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