[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
This commit is contained in:
Marius Brehler 2022-04-11 14:14:51 +00:00
parent 0d70bc990b
commit 84fe39a45b
7 changed files with 101 additions and 1 deletions

View 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 = [{

View File

@ -9,6 +9,7 @@ add_mlir_dialect_library(MLIREmitC
MLIREmitCAttributesIncGen
LINK_LIBS PUBLIC
MLIRCastInterfaces
MLIRIR
MLIRSideEffectInterfaces
)

View File

@ -62,6 +62,19 @@ LogicalResult ApplyOp::verify() {
return success();
}
//===----------------------------------------------------------------------===//
// 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
//===----------------------------------------------------------------------===//

View 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.

View 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
}

View 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

View File

@ -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
}