From 84fe39a45b7340d468378f58836c8559fda3959b Mon Sep 17 00:00:00 2001 From: Marius Brehler Date: Mon, 11 Apr 2022 14:14:51 +0000 Subject: [PATCH] [mlir][emitc] Add a cast op 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 | 28 +++++++++++++++++++++++++++ mlir/lib/Dialect/EmitC/IR/CMakeLists.txt | 1 + mlir/lib/Dialect/EmitC/IR/EmitC.cpp | 13 +++++++++++++ mlir/lib/Target/Cpp/TranslateToCpp.cpp | 17 +++++++++++++++- mlir/test/Dialect/EmitC/invalid_ops.mlir | 8 ++++++++ mlir/test/Dialect/EmitC/ops.mlir | 5 +++++ mlir/test/Target/Cpp/cast.mlir | 30 +++++++++++++++++++++++++++++ 7 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 mlir/test/Target/Cpp/cast.mlir diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td index e67ac05..6ddf10e 100644 --- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td +++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td @@ -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, + 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> to !emitc.ptr + ``` + }]; + + 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 = [{ diff --git a/mlir/lib/Dialect/EmitC/IR/CMakeLists.txt b/mlir/lib/Dialect/EmitC/IR/CMakeLists.txt index 6283441..1eaa8ce 100644 --- a/mlir/lib/Dialect/EmitC/IR/CMakeLists.txt +++ b/mlir/lib/Dialect/EmitC/IR/CMakeLists.txt @@ -9,6 +9,7 @@ add_mlir_dialect_library(MLIREmitC MLIREmitCAttributesIncGen LINK_LIBS PUBLIC + MLIRCastInterfaces MLIRIR MLIRSideEffectInterfaces ) diff --git a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp index a1e6db2..a310495 100644 --- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp +++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp @@ -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()) && + (output.isa())); +} + +//===----------------------------------------------------------------------===// // CallOp //===----------------------------------------------------------------------===// diff --git a/mlir/lib/Target/Cpp/TranslateToCpp.cpp b/mlir/lib/Target/Cpp/TranslateToCpp.cpp index dc3336f..4eaa686 100644 --- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp +++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp @@ -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( [&](auto op) { return printOperation(*this, op); }) // EmitC ops. - .Case( [&](auto op) { return printOperation(*this, op); }) // Func ops. diff --git a/mlir/test/Dialect/EmitC/invalid_ops.mlir b/mlir/test/Dialect/EmitC/invalid_ops.mlir index f6e4701..64753b8 100644 --- a/mlir/test/Dialect/EmitC/invalid_ops.mlir +++ b/mlir/test/Dialect/EmitC/invalid_ops.mlir @@ -93,3 +93,11 @@ func.func @var_attribute_return_type_2() { %c0 = "emitc.variable"(){value = "nullptr" : !emitc.ptr} : () -> !emitc.ptr return } + +// ----- + +func.func @cast_tensor(%arg : tensor) { + // expected-error @+1 {{'emitc.cast' op operand type 'tensor' and result type 'tensor' are cast incompatible}} + %1 = emitc.cast %arg: tensor to tensor + return +} diff --git a/mlir/test/Dialect/EmitC/ops.mlir b/mlir/test/Dialect/EmitC/ops.mlir index cce9c28..08b2845 100644 --- a/mlir/test/Dialect/EmitC/ops.mlir +++ b/mlir/test/Dialect/EmitC/ops.mlir @@ -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 index 0000000..7254f84 --- /dev/null +++ b/mlir/test/Target/Cpp/cast.mlir @@ -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>) { + // CHECK-NEXT: int32_t* [[V1:[^ ]*]] = (int32_t*) [[V0:[^ ]*]] + %1 = emitc.cast %arg0 : !emitc.ptr> to !emitc.ptr + return +} -- 2.7.4