[flang] Implement conversion of compatible derived types (#111165)
With some restrictions, BIND(C) derived types can be converted to compatible BIND(C) derived types. Semantics already support this, but ConvertOp was missing the conversion of such types. Fixes https://github.com/llvm/llvm-project/issues/107783
This commit is contained in:
@@ -479,7 +479,10 @@ mlir::Value fir::factory::createConvert(mlir::OpBuilder &builder,
|
||||
mlir::Location loc, mlir::Type toTy,
|
||||
mlir::Value val) {
|
||||
if (val.getType() != toTy) {
|
||||
assert(!fir::isa_derived(toTy));
|
||||
assert((!fir::isa_derived(toTy) ||
|
||||
mlir::cast<fir::RecordType>(val.getType()).getTypeList() ==
|
||||
mlir::cast<fir::RecordType>(toTy).getTypeList()) &&
|
||||
"incompatible record types");
|
||||
return builder.create<fir::ConvertOp>(loc, toTy, val);
|
||||
}
|
||||
return val;
|
||||
|
||||
@@ -660,6 +660,31 @@ struct ConvertOpConversion : public fir::FIROpConversion<fir::ConvertOp> {
|
||||
auto loc = convert.getLoc();
|
||||
auto i1Type = mlir::IntegerType::get(convert.getContext(), 1);
|
||||
|
||||
if (mlir::isa<fir::RecordType>(toFirTy)) {
|
||||
// Convert to compatible BIND(C) record type.
|
||||
// Double check that the record types are compatible (it should have
|
||||
// already been checked by the verifier).
|
||||
assert(mlir::cast<fir::RecordType>(fromFirTy).getTypeList() ==
|
||||
mlir::cast<fir::RecordType>(toFirTy).getTypeList() &&
|
||||
"incompatible record types");
|
||||
|
||||
auto toStTy = mlir::cast<mlir::LLVM::LLVMStructType>(toTy);
|
||||
mlir::Value val = rewriter.create<mlir::LLVM::UndefOp>(loc, toStTy);
|
||||
auto indexTypeMap = toStTy.getSubelementIndexMap();
|
||||
assert(indexTypeMap.has_value() && "invalid record type");
|
||||
|
||||
for (auto [attr, type] : indexTypeMap.value()) {
|
||||
int64_t index = mlir::cast<mlir::IntegerAttr>(attr).getInt();
|
||||
auto extVal =
|
||||
rewriter.create<mlir::LLVM::ExtractValueOp>(loc, op0, index);
|
||||
val =
|
||||
rewriter.create<mlir::LLVM::InsertValueOp>(loc, val, extVal, index);
|
||||
}
|
||||
|
||||
rewriter.replaceOp(convert, val);
|
||||
return mlir::success();
|
||||
}
|
||||
|
||||
if (mlir::isa<fir::LogicalType>(fromFirTy) ||
|
||||
mlir::isa<fir::LogicalType>(toFirTy)) {
|
||||
// By specification fir::LogicalType value may be any number,
|
||||
|
||||
@@ -1410,6 +1410,15 @@ bool fir::ConvertOp::areVectorsCompatible(mlir::Type inTy, mlir::Type outTy) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool areRecordsCompatible(mlir::Type inTy, mlir::Type outTy) {
|
||||
// Both records must have the same field types.
|
||||
// Trust frontend semantics for in-depth checks, such as if both records
|
||||
// have the BIND(C) attribute.
|
||||
auto inRecTy = mlir::dyn_cast<fir::RecordType>(inTy);
|
||||
auto outRecTy = mlir::dyn_cast<fir::RecordType>(outTy);
|
||||
return inRecTy && outRecTy && inRecTy.getTypeList() == outRecTy.getTypeList();
|
||||
}
|
||||
|
||||
bool fir::ConvertOp::canBeConverted(mlir::Type inType, mlir::Type outType) {
|
||||
if (inType == outType)
|
||||
return true;
|
||||
@@ -1428,7 +1437,8 @@ bool fir::ConvertOp::canBeConverted(mlir::Type inType, mlir::Type outType) {
|
||||
(fir::isBoxedRecordType(inType) && fir::isPolymorphicType(outType)) ||
|
||||
(fir::isPolymorphicType(inType) && fir::isPolymorphicType(outType)) ||
|
||||
(fir::isPolymorphicType(inType) && mlir::isa<BoxType>(outType)) ||
|
||||
areVectorsCompatible(inType, outType);
|
||||
areVectorsCompatible(inType, outType) ||
|
||||
areRecordsCompatible(inType, outType);
|
||||
}
|
||||
|
||||
llvm::LogicalResult fir::ConvertOp::verify() {
|
||||
|
||||
@@ -816,6 +816,31 @@ func.func @convert_complex16(%arg0 : complex<f128>) -> complex<f16> {
|
||||
|
||||
// -----
|
||||
|
||||
// Test `fir.convert` operation conversion between compatible fir.record types.
|
||||
|
||||
func.func @convert_record(%arg0 : !fir.type<_QMmod1Trec{i:i32,f:f64,c:!llvm.struct<(f32, f32)>,cstr:!fir.array<4x!fir.char<1>>}>) ->
|
||||
!fir.type<_QMmod2Trec{i:i32,f:f64,c:!llvm.struct<(f32, f32)>,cstr:!fir.array<4x!fir.char<1>>}> {
|
||||
%0 = fir.convert %arg0 : (!fir.type<_QMmod1Trec{i:i32,f:f64,c:!llvm.struct<(f32, f32)>,cstr:!fir.array<4x!fir.char<1>>}>) ->
|
||||
!fir.type<_QMmod2Trec{i:i32,f:f64,c:!llvm.struct<(f32, f32)>,cstr:!fir.array<4x!fir.char<1>>}>
|
||||
return %0 : !fir.type<_QMmod2Trec{i:i32,f:f64,c:!llvm.struct<(f32, f32)>,cstr:!fir.array<4x!fir.char<1>>}>
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @convert_record(
|
||||
// CHECK-SAME: %[[ARG0:.*]]: [[MOD1_REC:!llvm.struct<"_QMmod1Trec", \(i32, f64, struct<\(f32, f32\)>, array<4 x array<1 x i8>>\)>]]) ->
|
||||
// CHECK-SAME: [[MOD2_REC:!llvm.struct<"_QMmod2Trec", \(i32, f64, struct<\(f32, f32\)>, array<4 x array<1 x i8>>\)>]]
|
||||
// CHECK: %{{.*}} = llvm.mlir.undef : [[MOD2_REC]]
|
||||
// CHECK-DAG: %[[I:.*]] = llvm.extractvalue %[[ARG0]][0] : [[MOD1_REC]]
|
||||
// CHECK-DAG: %{{.*}} = llvm.insertvalue %[[I]], %{{.*}}[0] : [[MOD2_REC]]
|
||||
// CHECK-DAG: %[[F:.*]] = llvm.extractvalue %[[ARG0]][1] : [[MOD1_REC]]
|
||||
// CHECK-DAG: %{{.*}} = llvm.insertvalue %[[F]], %{{.*}}[1] : [[MOD2_REC]]
|
||||
// CHECK-DAG: %[[C:.*]] = llvm.extractvalue %[[ARG0]][2] : [[MOD1_REC]]
|
||||
// CHECK-DAG: %{{.*}} = llvm.insertvalue %[[C]], %{{.*}}[2] : [[MOD2_REC]]
|
||||
// CHECK-DAG: %[[CSTR:.*]] = llvm.extractvalue %[[ARG0]][3] : [[MOD1_REC]]
|
||||
// CHECK-DAG: %{{.*}} = llvm.insertvalue %[[CSTR]], %{{.*}}[3] : [[MOD2_REC]]
|
||||
// CHECK: llvm.return %{{.*}} : [[MOD2_REC]]
|
||||
|
||||
// -----
|
||||
|
||||
// Test `fir.store` --> `llvm.store` conversion
|
||||
|
||||
func.func @test_store_index(%val_to_store : index, %addr : !fir.ref<index>) {
|
||||
|
||||
@@ -965,6 +965,14 @@ func.func @fp_to_logical(%arg0: f32) -> !fir.logical<4> {
|
||||
|
||||
// -----
|
||||
|
||||
func.func @rec_to_rec(%arg0: !fir.type<t1{i:i32, f:f32}>) -> !fir.type<t2{f:f32, i:i32}> {
|
||||
// expected-error@+1{{'fir.convert' op invalid type conversion}}
|
||||
%0 = fir.convert %arg0 : (!fir.type<t1{i:i32, f:f32}>) -> !fir.type<t2{f:f32, i:i32}>
|
||||
return %0 : !fir.type<t2{f:f32, i:i32}>
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
func.func @bad_box_offset(%not_a_box : !fir.ref<i32>) {
|
||||
// expected-error@+1{{'fir.box_offset' op box_ref operand must have !fir.ref<!fir.box<T>> type}}
|
||||
%addr1 = fir.box_offset %not_a_box base_addr : (!fir.ref<i32>) -> !fir.llvm_ptr<!fir.ref<i32>>
|
||||
|
||||
Reference in New Issue
Block a user