Skip to content

Commit 52e1d52

Browse files
authored
Merge pull request #531 from Xilinx/niklas.opaque_conversion
Added test cases for arith-to-emitc conversions using opaque types. To generate input opaque types for testing, the ArithToEmitC pass was edited to convert f80 and i80 types, both unsupported in emitc, to opaque types. Adapted arith-to-emitc conversions to allow opaque types.
1 parent 4ab068e commit 52e1d52

File tree

6 files changed

+167
-47
lines changed

6 files changed

+167
-47
lines changed

mlir/include/mlir/Dialect/EmitC/IR/EmitC.h

+4
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ bool isSupportedFloatType(mlir::Type type);
5050
/// EmitC.
5151
bool isFloatOrOpaqueType(mlir::Type type);
5252

53+
/// Determines whether \p type is a valid integer or opaque type in
54+
/// EmitC.
55+
bool isIntegerOrOpaqueType(mlir::Type type);
56+
5357
/// Determines whether \p type is a emitc.size_t/ssize_t type.
5458
bool isPointerWideType(mlir::Type type);
5559

mlir/lib/Conversion/ArithToEmitC/ArithToEmitC.cpp

+54-30
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ Type adaptIntegralTypeSignedness(Type ty, bool needsUnsigned) {
7373

7474
/// Insert a cast operation to type \p ty if \p val does not have this type.
7575
Value adaptValueType(Value val, ConversionPatternRewriter &rewriter, Type ty) {
76+
assert(emitc::isSupportedEmitCType(val.getType()));
7677
return rewriter.createOrFold<emitc::CastOp>(val.getLoc(), ty, val);
7778
}
7879

@@ -279,7 +280,8 @@ class CmpIOpConversion : public OpConversionPattern<arith::CmpIOp> {
279280
ConversionPatternRewriter &rewriter) const override {
280281

281282
Type type = adaptor.getLhs().getType();
282-
if (!type || !(isa<IntegerType>(type) || emitc::isPointerWideType(type))) {
283+
if (!type || !(emitc::isIntegerOrOpaqueType(type) ||
284+
emitc::isPointerWideType(type))) {
283285
return rewriter.notifyMatchFailure(
284286
op, "expected integer or size_t/ssize_t/ptrdiff_t type");
285287
}
@@ -334,7 +336,7 @@ class CastConversion : public OpConversionPattern<ArithOp> {
334336
ConversionPatternRewriter &rewriter) const override {
335337

336338
Type opReturnType = this->getTypeConverter()->convertType(op.getType());
337-
if (!opReturnType || !(isa<IntegerType>(opReturnType) ||
339+
if (!opReturnType || !(emitc::isIntegerOrOpaqueType(opReturnType) ||
338340
emitc::isPointerWideType(opReturnType)))
339341
return rewriter.notifyMatchFailure(
340342
op, "expected integer or size_t/ssize_t/ptrdiff_t result type");
@@ -345,7 +347,7 @@ class CastConversion : public OpConversionPattern<ArithOp> {
345347
}
346348

347349
Type operandType = adaptor.getIn().getType();
348-
if (!operandType || !(isa<IntegerType>(operandType) ||
350+
if (!operandType || !(emitc::isIntegerOrOpaqueType(operandType) ||
349351
emitc::isPointerWideType(operandType)))
350352
return rewriter.notifyMatchFailure(
351353
op, "expected integer or size_t/ssize_t/ptrdiff_t operand type");
@@ -439,16 +441,17 @@ class BinaryUIOpConversion final : public OpConversionPattern<ArithOp> {
439441
if (!newRetTy)
440442
return rewriter.notifyMatchFailure(uiBinOp,
441443
"converting result type failed");
442-
if (!isa<IntegerType>(newRetTy)) {
444+
445+
if (!emitc::isIntegerOrOpaqueType(newRetTy)) {
443446
return rewriter.notifyMatchFailure(uiBinOp, "expected integer type");
444447
}
445448
Type unsignedType =
446449
adaptIntegralTypeSignedness(newRetTy, /*needsUnsigned=*/true);
447450
if (!unsignedType)
448451
return rewriter.notifyMatchFailure(uiBinOp,
449452
"converting result type failed");
450-
Value lhsAdapted = adaptValueType(uiBinOp.getLhs(), rewriter, unsignedType);
451-
Value rhsAdapted = adaptValueType(uiBinOp.getRhs(), rewriter, unsignedType);
453+
Value lhsAdapted = adaptValueType(adaptor.getLhs(), rewriter, unsignedType);
454+
Value rhsAdapted = adaptValueType(adaptor.getRhs(), rewriter, unsignedType);
452455

453456
auto newDivOp =
454457
rewriter.create<EmitCOp>(uiBinOp.getLoc(), unsignedType,
@@ -469,7 +472,8 @@ class IntegerOpConversion final : public OpConversionPattern<ArithOp> {
469472
ConversionPatternRewriter &rewriter) const override {
470473

471474
Type type = this->getTypeConverter()->convertType(op.getType());
472-
if (!type || !(isa<IntegerType>(type) || emitc::isPointerWideType(type))) {
475+
if (!type || !(emitc::isIntegerOrOpaqueType(type) ||
476+
emitc::isPointerWideType(type))) {
473477
return rewriter.notifyMatchFailure(
474478
op, "expected integer or size_t/ssize_t/ptrdiff_t type");
475479
}
@@ -512,7 +516,7 @@ class BitwiseOpConversion : public OpConversionPattern<ArithOp> {
512516
ConversionPatternRewriter &rewriter) const override {
513517

514518
Type type = this->getTypeConverter()->convertType(op.getType());
515-
if (!isa_and_nonnull<IntegerType>(type)) {
519+
if (!type || !emitc::isIntegerOrOpaqueType(type)) {
516520
return rewriter.notifyMatchFailure(
517521
op,
518522
"expected integer type, vector/tensor support not yet implemented");
@@ -552,7 +556,9 @@ class ShiftOpConversion : public OpConversionPattern<ArithOp> {
552556
ConversionPatternRewriter &rewriter) const override {
553557

554558
Type type = this->getTypeConverter()->convertType(op.getType());
555-
if (!type || !(isa<IntegerType>(type) || emitc::isPointerWideType(type))) {
559+
bool retIsOpaque = isa_and_nonnull<emitc::OpaqueType>(type);
560+
if (!type || (!retIsOpaque && !(isa<IntegerType>(type) ||
561+
emitc::isPointerWideType(type)))) {
556562
return rewriter.notifyMatchFailure(
557563
op, "expected integer or size_t/ssize_t/ptrdiff_t type");
558564
}
@@ -578,21 +584,33 @@ class ShiftOpConversion : public OpConversionPattern<ArithOp> {
578584
op.getLoc(), rhsType, "sizeof", ArrayRef<Value>{eight});
579585
width = rewriter.create<emitc::MulOp>(op.getLoc(), rhsType, eight,
580586
sizeOfCall.getResult(0));
581-
} else {
587+
} else if (!retIsOpaque) {
582588
width = rewriter.create<emitc::ConstantOp>(
583589
op.getLoc(), rhsType,
584590
rewriter.getIntegerAttr(rhsType, type.getIntOrFloatBitWidth()));
591+
} else {
592+
width = rewriter.create<emitc::ConstantOp>(
593+
op.getLoc(), rhsType,
594+
emitc::OpaqueAttr::get(rhsType.getContext(),
595+
"opaque_shift_bitwidth"));
585596
}
586597

587598
Value excessCheck = rewriter.create<emitc::CmpOp>(
588599
op.getLoc(), rewriter.getI1Type(), emitc::CmpPredicate::lt, rhs, width);
589600

590601
// Any concrete value is a valid refinement of poison.
591-
Value poison = rewriter.create<emitc::ConstantOp>(
592-
op.getLoc(), arithmeticType,
593-
(isa<IntegerType>(arithmeticType)
594-
? rewriter.getIntegerAttr(arithmeticType, 0)
595-
: rewriter.getIndexAttr(0)));
602+
Value poison;
603+
if (retIsOpaque) {
604+
poison = rewriter.create<emitc::ConstantOp>(
605+
op.getLoc(), arithmeticType,
606+
emitc::OpaqueAttr::get(rhsType.getContext(), "opaque_shift_poison"));
607+
} else {
608+
poison = rewriter.create<emitc::ConstantOp>(
609+
op.getLoc(), arithmeticType,
610+
(isa<IntegerType>(arithmeticType)
611+
? rewriter.getIntegerAttr(arithmeticType, 0)
612+
: rewriter.getIndexAttr(0)));
613+
}
596614

597615
emitc::ExpressionOp ternary = rewriter.create<emitc::ExpressionOp>(
598616
op.getLoc(), arithmeticType, /*do_not_inline=*/false);
@@ -669,19 +687,23 @@ class FtoICastOpConversion : public OpConversionPattern<CastOp> {
669687
if (!dstType)
670688
return rewriter.notifyMatchFailure(castOp, "type conversion failed");
671689

690+
Type actualResultType = dstType;
691+
672692
// Float-to-i1 casts are not supported: any value with 0 < value < 1 must be
673693
// truncated to 0, whereas a boolean conversion would return true.
674-
if (!emitc::isSupportedIntegerType(dstType) || dstType.isInteger(1))
675-
return rewriter.notifyMatchFailure(castOp,
676-
"unsupported cast destination type");
677-
678-
// Convert to unsigned if it's the "ui" variant
679-
// Signless is interpreted as signed, so no need to cast for "si"
680-
Type actualResultType = dstType;
681-
if (isa<arith::FPToUIOp>(castOp)) {
682-
actualResultType =
683-
rewriter.getIntegerType(dstType.getIntOrFloatBitWidth(),
684-
/*isSigned=*/false);
694+
bool dstIsOpaque = isa<emitc::OpaqueType>(dstType);
695+
if (!dstIsOpaque) {
696+
if (!emitc::isSupportedIntegerType(dstType) || dstType.isInteger(1))
697+
return rewriter.notifyMatchFailure(castOp,
698+
"unsupported cast destination type");
699+
700+
// Convert to unsigned if it's the "ui" variant
701+
// Signless is interpreted as signed, so no need to cast for "si"
702+
if (isa<arith::FPToUIOp>(castOp)) {
703+
actualResultType =
704+
rewriter.getIntegerType(dstType.getIntOrFloatBitWidth(),
705+
/*isSigned=*/false);
706+
}
685707
}
686708

687709
Value result = rewriter.create<emitc::CastOp>(
@@ -708,7 +730,9 @@ class ItoFCastOpConversion : public OpConversionPattern<CastOp> {
708730
ConversionPatternRewriter &rewriter) const override {
709731
// Vectors in particular are not supported
710732
Type operandType = adaptor.getIn().getType();
711-
if (!emitc::isSupportedIntegerType(operandType))
733+
bool opIsOpaque = isa<emitc::OpaqueType>(operandType);
734+
735+
if (!(opIsOpaque || emitc::isSupportedIntegerType(operandType)))
712736
return rewriter.notifyMatchFailure(castOp,
713737
"unsupported cast source type");
714738

@@ -723,7 +747,7 @@ class ItoFCastOpConversion : public OpConversionPattern<CastOp> {
723747
// Convert to unsigned if it's the "ui" variant
724748
// Signless is interpreted as signed, so no need to cast for "si"
725749
Type actualOperandType = operandType;
726-
if (isa<arith::UIToFPOp>(castOp)) {
750+
if (!opIsOpaque && isa<arith::UIToFPOp>(castOp)) {
727751
actualOperandType =
728752
rewriter.getIntegerType(operandType.getIntOrFloatBitWidth(),
729753
/*isSigned=*/false);
@@ -751,7 +775,7 @@ class FpCastOpConversion : public OpConversionPattern<CastOp> {
751775
ConversionPatternRewriter &rewriter) const override {
752776
// Vectors in particular are not supported.
753777
Type operandType = adaptor.getIn().getType();
754-
if (!emitc::isSupportedFloatType(operandType))
778+
if (!emitc::isFloatOrOpaqueType(operandType))
755779
return rewriter.notifyMatchFailure(castOp,
756780
"unsupported cast source type");
757781
if (auto roundingModeOp =
@@ -765,7 +789,7 @@ class FpCastOpConversion : public OpConversionPattern<CastOp> {
765789
if (!dstType)
766790
return rewriter.notifyMatchFailure(castOp, "type conversion failed");
767791

768-
if (!emitc::isSupportedFloatType(dstType))
792+
if (!emitc::isFloatOrOpaqueType(dstType))
769793
return rewriter.notifyMatchFailure(castOp,
770794
"unsupported cast destination type");
771795

mlir/lib/Conversion/ArithToEmitC/ArithToEmitCPass.cpp

+34-1
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,42 @@ namespace {
3030
struct ConvertArithToEmitC
3131
: public impl::ConvertArithToEmitCBase<ConvertArithToEmitC> {
3232
void runOnOperation() override;
33+
34+
/// Applies conversion to opaque types for f80 and i80 types, both unsupported
35+
/// in emitc. Used to test the pass with opaque types.
36+
void populateOpaqueTypeConversions(TypeConverter &converter);
3337
};
3438
} // namespace
3539

40+
void ConvertArithToEmitC::populateOpaqueTypeConversions(
41+
TypeConverter &converter) {
42+
converter.addConversion([](Type type) -> std::optional<Type> {
43+
if (type.isF80())
44+
return emitc::OpaqueType::get(type.getContext(), "f80");
45+
if (type.isInteger() && type.getIntOrFloatBitWidth() == 80)
46+
return emitc::OpaqueType::get(type.getContext(), "i80");
47+
return type;
48+
});
49+
50+
converter.addTypeAttributeConversion(
51+
[](Type type,
52+
Attribute attrToConvert) -> TypeConverter::AttributeConversionResult {
53+
if (auto floatAttr = llvm::dyn_cast<FloatAttr>(attrToConvert)) {
54+
if (floatAttr.getType().isF80()) {
55+
return emitc::OpaqueAttr::get(type.getContext(), "f80");
56+
}
57+
return {};
58+
}
59+
if (auto intAttr = llvm::dyn_cast<IntegerAttr>(attrToConvert)) {
60+
if (intAttr.getType().isInteger() &&
61+
intAttr.getType().getIntOrFloatBitWidth() == 80) {
62+
return emitc::OpaqueAttr::get(type.getContext(), "i80");
63+
}
64+
}
65+
return {};
66+
});
67+
}
68+
3669
void ConvertArithToEmitC::runOnOperation() {
3770
ConversionTarget target(getContext());
3871

@@ -42,8 +75,8 @@ void ConvertArithToEmitC::runOnOperation() {
4275
RewritePatternSet patterns(&getContext());
4376

4477
TypeConverter typeConverter;
45-
typeConverter.addConversion([](Type type) { return type; });
4678

79+
populateOpaqueTypeConversions(typeConverter);
4780
populateArithToEmitCPatterns(typeConverter, patterns);
4881

4982
if (failed(

mlir/lib/Dialect/EmitC/IR/EmitC.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,10 @@ bool mlir::emitc::isSupportedFloatType(Type type) {
133133
return false;
134134
}
135135

136+
bool mlir::emitc::isIntegerOrOpaqueType(Type type) {
137+
return isa<emitc::OpaqueType>(type) || isSupportedIntegerType(type);
138+
}
139+
136140
bool mlir::emitc::isFloatOrOpaqueType(Type type) {
137141
return isa<emitc::OpaqueType>(type) || isSupportedFloatType(type);
138142
}

mlir/test/Conversion/ArithToEmitC/arith-to-emitc-unsupported.mlir

-16
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,6 @@ func.func @arith_cast_vector(%arg0: vector<5xf32>) -> vector<5xi32> {
3030
return %t: vector<5xi32>
3131
}
3232

33-
// -----
34-
func.func @arith_cast_f80(%arg0: f80) -> i32 {
35-
// expected-error @+1 {{failed to legalize operation 'arith.fptosi'}}
36-
%t = arith.fptosi %arg0 : f80 to i32
37-
return %t: i32
38-
}
39-
4033
// -----
4134

4235
func.func @arith_cast_f128(%arg0: f128) -> i32 {
@@ -45,15 +38,6 @@ func.func @arith_cast_f128(%arg0: f128) -> i32 {
4538
return %t: i32
4639
}
4740

48-
49-
// -----
50-
51-
func.func @arith_cast_to_f80(%arg0: i32) -> f80 {
52-
// expected-error @+1 {{failed to legalize operation 'arith.sitofp'}}
53-
%t = arith.sitofp %arg0 : i32 to f80
54-
return %t: f80
55-
}
56-
5741
// -----
5842

5943
func.func @arith_cast_to_f128(%arg0: i32) -> f128 {

mlir/test/Conversion/ArithToEmitC/arith-to-emitc.mlir

+71
Original file line numberDiff line numberDiff line change
@@ -780,3 +780,74 @@ func.func @arith_truncf(%arg0: f64) -> f16 {
780780

781781
return %truncd1 : f16
782782
}
783+
784+
// -----
785+
786+
func.func @float_opaque_conversion(%arg0: f80, %arg1: f80) {
787+
// CHECK-LABEL: float_opaque_conversion
788+
// CHECK-SAME: (%[[Arg0:[^ ]*]]: f80, %[[Arg1:[^ ]*]]: f80)
789+
790+
// CHECK-DAG: [[arg1_cast:[^ ]*]] = builtin.unrealized_conversion_cast %[[Arg1]] : f80 to !emitc.opaque<"f80">
791+
// CHECK-DAG: [[arg0_cast:[^ ]*]] = builtin.unrealized_conversion_cast %[[Arg0]] : f80 to !emitc.opaque<"f80">
792+
// CHECK: "emitc.constant"() <{value = #emitc.opaque<"f80">}> : () -> !emitc.opaque<"f80">
793+
%10 = arith.constant 0.0 : f80
794+
// CHECK: emitc.add [[arg0_cast]], [[arg1_cast]] : (!emitc.opaque<"f80">, !emitc.opaque<"f80">) -> !emitc.opaque<"f80">
795+
%2 = arith.addf %arg0, %arg1 : f80
796+
// CHECK: [[EQ:[^ ]*]] = emitc.cmp eq, [[arg0_cast]], [[arg1_cast]] : (!emitc.opaque<"f80">, !emitc.opaque<"f80">) -> i1
797+
// CHECK: [[NotNaNArg0:[^ ]*]] = emitc.cmp eq, [[arg0_cast]], [[arg0_cast]] : (!emitc.opaque<"f80">, !emitc.opaque<"f80">) -> i1
798+
// CHECK: [[NotNaNArg1:[^ ]*]] = emitc.cmp eq, [[arg1_cast]], [[arg1_cast]] : (!emitc.opaque<"f80">, !emitc.opaque<"f80">) -> i1
799+
// CHECK: [[Ordered:[^ ]*]] = emitc.logical_and [[NotNaNArg0]], [[NotNaNArg1]] : i1, i1
800+
// CHECK: emitc.logical_and [[Ordered]], [[EQ]] : i1, i1
801+
%11 = arith.cmpf oeq, %arg0, %arg1 : f80
802+
// CHECK: emitc.unary_minus [[arg0_cast]] : (!emitc.opaque<"f80">) -> !emitc.opaque<"f80">
803+
%12 = arith.negf %arg0 : f80
804+
// CHECK: [[V0:[^ ]*]] = emitc.cast [[arg0_cast]] : !emitc.opaque<"f80"> to ui32
805+
// CHECK: [[V1:[^ ]*]] = emitc.cast [[V0]] : ui32 to i32
806+
%7 = arith.fptoui %arg0 : f80 to i32
807+
// CHECK: emitc.cast [[V1]] : i32 to !emitc.opaque<"f80">
808+
%8 = arith.sitofp %7 : i32 to f80
809+
// CHECK: [[trunc:[^ ]*]] = emitc.cast [[arg0_cast]] : !emitc.opaque<"f80"> to f32
810+
%13 = arith.truncf %arg0 : f80 to f32
811+
// CHECK: emitc.cast [[trunc]] : f32 to !emitc.opaque<"f80">
812+
%15 = arith.extf %13 : f32 to f80
813+
return
814+
}
815+
816+
// -----
817+
818+
func.func @int_opaque_conversion(%arg0: i80, %arg1: i80, %arg2: i1) {
819+
// CHECK-LABEL: int_opaque_conversion
820+
// CHECK-SAME: (%[[Arg0:[^ ]*]]: i80, %[[Arg1:[^ ]*]]: i80, %[[Arg2:[^ ]*]]: i1)
821+
822+
// CHECK-DAG: [[arg1_cast:[^ ]*]] = builtin.unrealized_conversion_cast %[[Arg1]] : i80 to !emitc.opaque<"i80">
823+
// CHECK-DAG: [[arg0_cast:[^ ]*]] = builtin.unrealized_conversion_cast %[[Arg0]] : i80 to !emitc.opaque<"i80">
824+
// CHECK: "emitc.constant"() <{value = #emitc.opaque<"i80">}> : () -> !emitc.opaque<"i80">
825+
%10 = arith.constant 0 : i80
826+
// CHECK: emitc.div [[arg0_cast]], [[arg1_cast]] : (!emitc.opaque<"i80">, !emitc.opaque<"i80">) -> !emitc.opaque<"i80">
827+
%3 = arith.divui %arg0, %arg1 : i80
828+
// CHECK: emitc.add [[arg0_cast]], [[arg1_cast]] : (!emitc.opaque<"i80">, !emitc.opaque<"i80">) -> !emitc.opaque<"i80">
829+
%2 = arith.addi %arg0, %arg1 : i80
830+
// CHECK: emitc.bitwise_and [[arg0_cast]], [[arg1_cast]] : (!emitc.opaque<"i80">, !emitc.opaque<"i80">) -> !emitc.opaque<"i80">
831+
%14 = arith.andi %arg0, %arg1 : i80
832+
// CHECK: [[Bitwidth:[^ ]*]] = "emitc.constant"() <{value = #emitc.opaque<"opaque_shift_bitwidth">}> : () -> !emitc.opaque<"i80">
833+
// CHECK: [[LT:[^ ]*]] = emitc.cmp lt, [[arg1_cast]], [[Bitwidth]] : (!emitc.opaque<"i80">, !emitc.opaque<"i80">) -> i1
834+
// CHECK: [[Poison:[^ ]*]] = "emitc.constant"() <{value = #emitc.opaque<"opaque_shift_poison">}> : () -> !emitc.opaque<"i80">
835+
// CHECK: [[Exp:[^ ]*]] = emitc.expression : !emitc.opaque<"i80"> {
836+
// CHECK: [[LShift:[^ ]*]] = emitc.bitwise_left_shift [[arg0_cast]], [[arg1_cast]] : (!emitc.opaque<"i80">, !emitc.opaque<"i80">) -> !emitc.opaque<"i80">
837+
// CHECK: emitc.conditional [[LT]], [[LShift]], [[Poison]] : !emitc.opaque<"i80">
838+
// CHECK: emitc.yield {{.*}} : !emitc.opaque<"i80">
839+
// CHECK: }
840+
%12 = arith.shli %arg0, %arg1 : i80
841+
// CHECK: emitc.cmp eq, [[arg0_cast]], [[arg1_cast]] : (!emitc.opaque<"i80">, !emitc.opaque<"i80">) -> i1
842+
%11 = arith.cmpi eq, %arg0, %arg1 : i80
843+
// CHECK: emitc.conditional %[[Arg2]], [[arg0_cast]], [[arg1_cast]] : !emitc.opaque<"i80">
844+
%13 = arith.select %arg2, %arg0, %arg1 : i80
845+
// CHECK: [[V0:[^ ]*]] = emitc.cast [[arg0_cast]] : !emitc.opaque<"i80"> to ui8
846+
// CHECK: emitc.cast [[V0]] : ui8 to i8
847+
%15 = arith.trunci %arg0 : i80 to i8
848+
// CHECK: [[V1:[^ ]*]] = emitc.cast [[arg0_cast]] : !emitc.opaque<"i80"> to f32
849+
%9 = arith.uitofp %arg0 : i80 to f32
850+
// CHECK: emitc.cast [[V1]] : f32 to !emitc.opaque<"i80">
851+
%6 = arith.fptosi %9 : f32 to i80
852+
return
853+
}

0 commit comments

Comments
 (0)