[mlir] Add a new builtin `unrealized_conversion_cast` operation

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 was discussed in the following RFC(and ODM):

https://llvm.discourse.group/t/open-meeting-1-14-dialect-conversion-and-type-conversion-the-question-of-cast-operations/

Differential Revision: https://reviews.llvm.org/D94832
This commit is contained in:
River Riddle 2021-01-20 16:17:26 -08:00
parent 6ccf2d62b4
commit c78219f644
7 changed files with 144 additions and 0 deletions

View File

@ -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"
//===----------------------------------------------------------------------===//

View File

@ -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<string mnemonic, list<OpTrait> traits = []> :
@ -220,4 +222,53 @@ def ModuleTerminatorOp : Builtin_Op<"module_terminator", [
let assemblyFormat = "attr-dict";
}
//===----------------------------------------------------------------------===//
// UnrealizedConversionCastOp
//===----------------------------------------------------------------------===//
def UnrealizedConversionCastOp : Builtin_Op<"unrealized_conversion_cast", [
DeclareOpInterfaceMethods<CastOpInterface>, 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<!foo.type, !foo.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<!foo.type, !foo.type>
```
}];
let arguments = (ins Variadic<AnyType>:$inputs);
let results = (outs Variadic<AnyType>:$outputs);
let assemblyFormat = [{
($inputs^ `:` type($inputs))? `to` type($outputs) attr-dict
}];
let hasFolder = 1;
}
#endif // BUILTIN_OPS

View File

@ -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<Attribute> attrOperands,
SmallVectorImpl<OpFoldResult> &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<UnrealizedConversionCastOp>();
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
//===----------------------------------------------------------------------===//

View File

@ -37,8 +37,10 @@ add_mlir_library(MLIRIR
MLIRBuiltinOpsIncGen
MLIRBuiltinTypesIncGen
MLIRCallInterfacesIncGen
MLIRCastInterfacesIncGen
MLIROpAsmInterfaceIncGen
MLIRRegionKindInterfaceIncGen
MLIRSideEffectInterfacesIncGen
MLIRSymbolInterfacesIncGen
LINK_LIBS PUBLIC

View File

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

View File

@ -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"() : () -> ()
// -----

View File

@ -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<!foo.type, !foo.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<!foo.type, !foo.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<!foo.type, !foo.type>