[mlir][emitc] Add a cast op
authorMarius Brehler <marius.brehler@iml.fraunhofer.de>
Mon, 11 Apr 2022 14:14:51 +0000 (14:14 +0000)
committerMarius Brehler <marius.brehler@iml.fraunhofer.de>
Thu, 28 Apr 2022 15:50:59 +0000 (15:50 +0000)
This adds a cast operation that allows to perform an explicit type
conversion. The cast op is emitted as a C-style cast. It can be applied
to integer, float, index and EmitC types.

Reviewed By: jpienaar

Differential Revision: https://reviews.llvm.org/D123514

mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
mlir/lib/Dialect/EmitC/IR/CMakeLists.txt
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/cast.mlir [new file with mode: 0644]

index e67ac05..6ddf10e 100644 (file)
@@ -16,6 +16,7 @@
 include "mlir/Dialect/EmitC/IR/EmitCAttributes.td"
 include "mlir/Dialect/EmitC/IR/EmitCTypes.td"
 
+include "mlir/Interfaces/CastInterfaces.td"
 include "mlir/Interfaces/SideEffectInterfaces.td"
 
 //===----------------------------------------------------------------------===//
@@ -88,6 +89,33 @@ def EmitC_CallOp : EmitC_Op<"call", []> {
   let hasVerifier = 1;
 }
 
+def EmitC_CastOp : EmitC_Op<"cast", [
+    DeclareOpInterfaceMethods<CastOpInterface>,
+    SameOperandsAndResultShape
+  ]> {
+  let summary = "Cast operation";
+  let description = [{
+    The `cast` operation performs an explicit type conversion and is emitted
+    as a C-style cast expression. It can be applied to integer, float, index
+    and EmitC types.
+
+    Example:
+
+    ```mlir
+    // Cast from `int32_t` to `float`
+    %0 = emitc.cast %arg0: i32 to f32
+
+    // Cast from `void` to `int32_t` pointer
+    %1 = emitc.cast %arg1 :
+        !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<i32>
+    ```
+  }];
+
+  let arguments = (ins AnyType:$source);
+  let results = (outs AnyType:$dest);
+  let assemblyFormat = "$source attr-dict `:` type($source) `to` type($dest)";
+}
+
 def EmitC_ConstantOp : EmitC_Op<"constant", [ConstantLike]> {
   let summary = "Constant operation";
   let description = [{
index 6283441..1eaa8ce 100644 (file)
@@ -9,6 +9,7 @@ add_mlir_dialect_library(MLIREmitC
   MLIREmitCAttributesIncGen
 
   LINK_LIBS PUBLIC
+  MLIRCastInterfaces
   MLIRIR
   MLIRSideEffectInterfaces
   )
index a1e6db2..a310495 100644 (file)
@@ -63,6 +63,19 @@ LogicalResult ApplyOp::verify() {
 }
 
 //===----------------------------------------------------------------------===//
+// CastOp
+//===----------------------------------------------------------------------===//
+
+bool CastOp::areCastCompatible(TypeRange inputs, TypeRange outputs) {
+  Type input = inputs.front(), output = outputs.front();
+
+  return ((input.isa<IntegerType, FloatType, IndexType, emitc::OpaqueType,
+                     emitc::PointerType>()) &&
+          (output.isa<IntegerType, FloatType, IndexType, emitc::OpaqueType,
+                      emitc::PointerType>()));
+}
+
+//===----------------------------------------------------------------------===//
 // CallOp
 //===----------------------------------------------------------------------===//
 
index dc3336f..4eaa686 100644 (file)
@@ -383,6 +383,21 @@ static LogicalResult printOperation(CppEmitter &emitter,
   return success();
 }
 
+static LogicalResult printOperation(CppEmitter &emitter, emitc::CastOp castOp) {
+  raw_ostream &os = emitter.ostream();
+  Operation &op = *castOp.getOperation();
+
+  if (failed(emitter.emitAssignPrefix(op)))
+    return failure();
+  os << "(";
+  if (failed(emitter.emitType(op.getLoc(), op.getResult(0).getType())))
+    return failure();
+  os << ") ";
+  os << emitter.getOrCreateName(castOp.getOperand());
+
+  return success();
+}
+
 static LogicalResult printOperation(CppEmitter &emitter,
                                     emitc::IncludeOp includeOp) {
   raw_ostream &os = emitter.ostream();
@@ -918,7 +933,7 @@ LogicalResult CppEmitter::emitOperation(Operation &op, bool trailingSemicolon) {
           .Case<cf::BranchOp, cf::CondBranchOp>(
               [&](auto op) { return printOperation(*this, op); })
           // EmitC ops.
-          .Case<emitc::ApplyOp, emitc::CallOp, emitc::ConstantOp,
+          .Case<emitc::ApplyOp, emitc::CallOp, emitc::CastOp, emitc::ConstantOp,
                 emitc::IncludeOp, emitc::VariableOp>(
               [&](auto op) { return printOperation(*this, op); })
           // Func ops.
index f6e4701..64753b8 100644 (file)
@@ -93,3 +93,11 @@ func.func @var_attribute_return_type_2() {
     %c0 = "emitc.variable"(){value = "nullptr" : !emitc.ptr<i64>} : () -> !emitc.ptr<i32>
     return
 }
+
+// -----
+
+func.func @cast_tensor(%arg : tensor<f32>) {
+    // expected-error @+1 {{'emitc.cast' op operand type 'tensor<f32>' and result type 'tensor<f32>' are cast incompatible}}
+    %1 = emitc.cast %arg: tensor<f32> to tensor<f32>
+    return
+}
index cce9c28..08b2845 100644 (file)
@@ -12,6 +12,11 @@ func.func @f(%arg0: i32, %f: !emitc.opaque<"int32_t">) {
   return
 }
 
+func.func @cast(%arg0: i32) {
+  %1 = emitc.cast %arg0: i32 to f32
+  return
+}
+
 func.func @c() {
   %1 = "emitc.constant"(){value = 42 : i32} : () -> i32
   return
diff --git a/mlir/test/Target/Cpp/cast.mlir b/mlir/test/Target/Cpp/cast.mlir
new file mode 100644 (file)
index 0000000..7254f84
--- /dev/null
@@ -0,0 +1,30 @@
+// RUN: mlir-translate -mlir-to-cpp %s | FileCheck %s
+// CHECK-LABEL: void cast
+func.func @cast(%arg0 : i32) {
+  // CHECK-NEXT: uint32_t [[V1:[^ ]*]] = (uint32_t) [[V0:[^ ]*]]
+  %1 = emitc.cast %arg0: i32 to ui32
+
+  // CHECK-NEXT: int64_t [[V4:[^ ]*]] = (int64_t) [[V0:[^ ]*]]
+  %2 = emitc.cast %arg0: i32 to i64
+  // CHECK-NEXT: int64_t [[V5:[^ ]*]] = (uint64_t) [[V0:[^ ]*]]
+  %3 = emitc.cast %arg0: i32 to ui64
+
+  // CHECK-NEXT: float [[V4:[^ ]*]] = (float) [[V0:[^ ]*]]
+  %4 = emitc.cast %arg0: i32 to f32
+  // CHECK-NEXT: double [[V5:[^ ]*]] = (double) [[V0:[^ ]*]]
+  %5 = emitc.cast %arg0: i32 to f64
+
+  // CHECK-NEXT: bool [[V6:[^ ]*]] = (bool) [[V0:[^ ]*]]
+  %6 = emitc.cast %arg0: i32 to i1
+
+  // CHECK-NEXT: mytype [[V7:[^ ]*]] = (mytype) [[V0:[^ ]*]]
+  %7 = emitc.cast %arg0: i32 to !emitc.opaque<"mytype">
+  return
+}
+
+// CHECK-LABEL: void cast_ptr
+func.func @cast_ptr(%arg0 : !emitc.ptr<!emitc.opaque<"void">>) {
+  // CHECK-NEXT: int32_t* [[V1:[^ ]*]] = (int32_t*) [[V0:[^ ]*]]
+  %1 = emitc.cast %arg0 : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<i32>
+  return
+}