diff --git a/mlir/include/mlir/IR/BuiltinOps.h b/mlir/include/mlir/IR/BuiltinOps.h index c695a005283b0b..c0163b148f3cc7 100644 --- a/mlir/include/mlir/IR/BuiltinOps.h +++ b/mlir/include/mlir/IR/BuiltinOps.h @@ -17,6 +17,8 @@ #include "mlir/IR/OwningOpRef.h" #include "mlir/IR/SymbolTable.h" #include "mlir/Interfaces/CallInterfaces.h" +#include "mlir/Interfaces/CastInterfaces.h" +#include "mlir/Interfaces/SideEffectInterfaces.h" #include "llvm/Support/PointerLikeTypeTraits.h" //===----------------------------------------------------------------------===// diff --git a/mlir/include/mlir/IR/BuiltinOps.td b/mlir/include/mlir/IR/BuiltinOps.td index 86de251094cdc3..2c4d47d61cbe21 100644 --- a/mlir/include/mlir/IR/BuiltinOps.td +++ b/mlir/include/mlir/IR/BuiltinOps.td @@ -17,6 +17,8 @@ include "mlir/IR/BuiltinDialect.td" include "mlir/IR/SymbolInterfaces.td" include "mlir/Interfaces/CallInterfaces.td" +include "mlir/Interfaces/CastInterfaces.td" +include "mlir/Interfaces/SideEffectInterfaces.td" // Base class for Builtin dialect ops. class Builtin_Op traits = []> : @@ -220,4 +222,53 @@ def ModuleTerminatorOp : Builtin_Op<"module_terminator", [ let assemblyFormat = "attr-dict"; } +//===----------------------------------------------------------------------===// +// UnrealizedConversionCastOp +//===----------------------------------------------------------------------===// + +def UnrealizedConversionCastOp : Builtin_Op<"unrealized_conversion_cast", [ + DeclareOpInterfaceMethods, NoSideEffect + ]> { + let summary = "An unrealized conversion from one set of types to another"; + let description = [{ + An `unrealized_conversion_cast` operation represents an unrealized + conversion from one set of types to another, that is used to enable the + inter-mixing of different type systems. This operation should not be + attributed any special representational or execution semantics, and is + generally only intended to be used to satisfy the temporary intermixing of + type systems during the conversion of one type system to another. + + This operation may produce results of arity 1-N, and accept as input + operands of arity 0-N. + + Example: + + ```mlir + // An unrealized 0-1 conversion. These types of conversions are useful in + // cases where a type is removed from the type system, but not all uses have + // been converted. For example, imagine we have a tuple type that is + // expanded to its element types. If only some uses of an empty tuple type + // instance are converted we still need an instance of the tuple type, but + // have no inputs to the unrealized conversion. + %result = unrealized_conversion_cast to !bar.tuple_type<> + + // An unrealized 1-1 conversion. + %result1 = unrealized_conversion_cast %operand : !foo.type to !bar.lowered_type + + // An unrealized 1-N conversion. + %results2:2 = unrealized_conversion_cast %tuple_operand : !foo.tuple_type to !foo.type, !foo.type + + // An unrealized N-1 conversion. + %result3 = unrealized_conversion_cast %operand, %operand : !foo.type, !foo.type to !bar.tuple_type + ``` + }]; + + let arguments = (ins Variadic:$inputs); + let results = (outs Variadic:$outputs); + let assemblyFormat = [{ + ($inputs^ `:` type($inputs))? `to` type($outputs) attr-dict + }]; + let hasFolder = 1; +} + #endif // BUILTIN_OPS diff --git a/mlir/lib/IR/BuiltinDialect.cpp b/mlir/lib/IR/BuiltinDialect.cpp index 2cd90f3d0fde4b..138321e734e282 100644 --- a/mlir/lib/IR/BuiltinDialect.cpp +++ b/mlir/lib/IR/BuiltinDialect.cpp @@ -18,6 +18,7 @@ #include "mlir/IR/BuiltinTypes.h" #include "mlir/IR/FunctionImplementation.h" #include "mlir/IR/OpImplementation.h" +#include "mlir/IR/PatternMatch.h" #include "llvm/ADT/MapVector.h" using namespace mlir; @@ -236,6 +237,38 @@ static LogicalResult verify(ModuleOp op) { return success(); } +//===----------------------------------------------------------------------===// +// UnrealizedConversionCastOp +//===----------------------------------------------------------------------===// + +LogicalResult +UnrealizedConversionCastOp::fold(ArrayRef attrOperands, + SmallVectorImpl &foldResults) { + OperandRange operands = inputs(); + if (operands.empty()) + return failure(); + + // Check that the input is a cast with results that all feed into this + // operation, and operand types that directly match the result types of this + // operation. + ResultRange results = outputs(); + Value firstInput = operands.front(); + auto inputOp = firstInput.getDefiningOp(); + if (!inputOp || inputOp.getResults() != operands || + inputOp.getOperandTypes() != results.getTypes()) + return failure(); + + // If everything matches up, we can fold the passthrough. + foldResults.append(inputOp->operand_begin(), inputOp->operand_end()); + return success(); +} + +bool UnrealizedConversionCastOp::areCastCompatible(TypeRange inputs, + TypeRange outputs) { + // `UnrealizedConversionCastOp` is agnostic of the input/output types. + return true; +} + //===----------------------------------------------------------------------===// // TableGen'd op method definitions //===----------------------------------------------------------------------===// diff --git a/mlir/lib/IR/CMakeLists.txt b/mlir/lib/IR/CMakeLists.txt index e4b3b914132305..f9ab7033eb48f7 100644 --- a/mlir/lib/IR/CMakeLists.txt +++ b/mlir/lib/IR/CMakeLists.txt @@ -37,8 +37,10 @@ add_mlir_library(MLIRIR MLIRBuiltinOpsIncGen MLIRBuiltinTypesIncGen MLIRCallInterfacesIncGen + MLIRCastInterfacesIncGen MLIROpAsmInterfaceIncGen MLIRRegionKindInterfaceIncGen + MLIRSideEffectInterfacesIncGen MLIRSymbolInterfacesIncGen LINK_LIBS PUBLIC diff --git a/mlir/test/Dialect/Builtin/canonicalize.mlir b/mlir/test/Dialect/Builtin/canonicalize.mlir new file mode 100644 index 00000000000000..316684428f2df5 --- /dev/null +++ b/mlir/test/Dialect/Builtin/canonicalize.mlir @@ -0,0 +1,25 @@ +// RUN: mlir-opt %s -canonicalize | FileCheck %s + +//===----------------------------------------------------------------------===// +// UnrealizedConversionCastOp +//===----------------------------------------------------------------------===// + +// Test folding conversion casts feeding into other casts. +// CHECK-LABEL: func @multiple_conversion_casts +// CHECK-SAME: %[[ARG0:.*]]: i32, %[[ARG1:.*]]: +func @multiple_conversion_casts(%arg0: i32, %arg1: i32) -> (i32, i32) { + // CHECK-NOT: unrealized_conversion_cast + // CHECK: return %[[ARG0]], %[[ARG1]] + %inputs:2 = unrealized_conversion_cast %arg0, %arg1 : i32, i32 to i64, i64 + %outputs:2 = unrealized_conversion_cast %inputs#0, %inputs#1 : i64, i64 to i32, i32 + return %outputs#0, %outputs#1 : i32, i32 +} + +// CHECK-LABEL: func @multiple_conversion_casts +func @multiple_conversion_casts_failure(%arg0: i32, %arg1: i32, %arg2: i64) -> (i32, i32) { + // CHECK: unrealized_conversion_cast + // CHECK: unrealized_conversion_cast + %inputs:2 = unrealized_conversion_cast %arg0, %arg1 : i32, i32 to i64, i64 + %outputs:2 = unrealized_conversion_cast %arg2, %inputs#1 : i64, i64 to i32, i32 + return %outputs#0, %outputs#1 : i32, i32 +} diff --git a/mlir/test/Dialect/Builtin/invalid.mlir b/mlir/test/Dialect/Builtin/invalid.mlir new file mode 100644 index 00000000000000..e7b08407d8a880 --- /dev/null +++ b/mlir/test/Dialect/Builtin/invalid.mlir @@ -0,0 +1,11 @@ +// RUN: mlir-opt %s -split-input-file -verify-diagnostics + +//===----------------------------------------------------------------------===// +// UnrealizedConversionCastOp +//===----------------------------------------------------------------------===// + +// expected-error@+1 {{expected at least one result for cast operation}} +"unrealized_conversion_cast"() : () -> () + +// ----- + diff --git a/mlir/test/Dialect/Builtin/ops.mlir b/mlir/test/Dialect/Builtin/ops.mlir new file mode 100644 index 00000000000000..e1ef6f196bd5e0 --- /dev/null +++ b/mlir/test/Dialect/Builtin/ops.mlir @@ -0,0 +1,20 @@ +// RUN: mlir-opt %s -allow-unregistered-dialect | mlir-opt -allow-unregistered-dialect + +//===----------------------------------------------------------------------===// +// UnrealizedConversionCastOp +//===----------------------------------------------------------------------===// + +%operand = "foo.op"() : () -> !foo.type +%tuple_operand = "foo.op"() : () -> !foo.tuple_type + +// An unrealized 0-1 conversion. +%result = unrealized_conversion_cast to !bar.tuple_type<> + +// An unrealized 1-1 conversion. +%result1 = unrealized_conversion_cast %operand : !foo.type to !bar.lowered_type + +// An unrealized 1-N conversion. +%results2:2 = unrealized_conversion_cast %tuple_operand : !foo.tuple_type to !foo.type, !foo.type + +// An unrealized N-1 conversion. +%result3 = unrealized_conversion_cast %operand, %operand : !foo.type, !foo.type to !bar.tuple_type