diff --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp --- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp +++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp @@ -366,6 +366,29 @@ } }; +/// `fir.load` --> `llvm.load` +struct LoadOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + mlir::LogicalResult + matchAndRewrite(fir::LoadOp load, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const override { + // fir.box is a special case because it is considered as an ssa values in + // fir, but it is lowered as a pointer to a descriptor. So fir.ref + // and fir.box end up being the same llvm types and loading a + // fir.ref is actually a no op in LLVM. + if (load.getType().isa()) { + rewriter.replaceOp(load, adaptor.getOperands()[0]); + } else { + mlir::Type ty = convertType(load.getType()); + ArrayRef at = load->getAttrs(); + rewriter.replaceOpWithNewOp( + load, ty, adaptor.getOperands(), at); + } + return success(); + } +}; + /// conversion of fir::SelectRankOp to an if-then-else ladder struct SelectRankOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -378,7 +401,31 @@ } }; -// convert to LLVM IR dialect `undef` +/// `fir.store` --> `llvm.store` +struct StoreOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + mlir::LogicalResult + matchAndRewrite(fir::StoreOp store, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const override { + if (store.value().getType().isa()) { + // fir.box value is actually in memory, load it first before storing it. + mlir::Location loc = store.getLoc(); + mlir::Type boxPtrTy = adaptor.getOperands()[0].getType(); + auto val = rewriter.create( + loc, boxPtrTy.cast().getElementType(), + adaptor.getOperands()[0]); + rewriter.replaceOpWithNewOp( + store, val, adaptor.getOperands()[1]); + } else { + rewriter.replaceOpWithNewOp( + store, adaptor.getOperands()[0], adaptor.getOperands()[1]); + } + return success(); + } +}; + +/// convert to LLVM IR dialect `undef` struct UndefOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -391,7 +438,7 @@ } }; -// convert to LLVM IR dialect `unreachable` +/// `fir.unreachable` --> `llvm.unreachable` struct UnreachableOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -788,14 +835,14 @@ auto *context = getModule().getContext(); fir::LLVMTypeConverter typeConverter{getModule()}; mlir::OwningRewritePatternList pattern(context); - pattern - .insert(typeConverter); + pattern.insert(typeConverter); mlir::populateStdToLLVMConversionPatterns(typeConverter, pattern); mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter, pattern); diff --git a/flang/test/Fir/convert-to-llvm.fir b/flang/test/Fir/convert-to-llvm.fir --- a/flang/test/Fir/convert-to-llvm.fir +++ b/flang/test/Fir/convert-to-llvm.fir @@ -632,3 +632,57 @@ // CHECK: %[[STRUCT1:.*]] = llvm.insertvalue %[[CONVERTX]], %[[STRUCT0]][0 : i32] : !llvm.struct<(f16, f16)> // CHECK: %[[STRUCT2:.*]] = llvm.insertvalue %[[CONVERTY]], %[[STRUCT1]][1 : i32] : !llvm.struct<(f16, f16)> // CHECK: llvm.return %[[STRUCT2]] : !llvm.struct<(f16, f16)> + +// ----- + +// Test `fir.store` --> `llvm.store` conversion + +func @test_store_index(%val_to_store : index, %addr : !fir.ref) { + fir.store %val_to_store to %addr : !fir.ref + return +} + +// CHECK-LABEL: llvm.func @test_store_index +// CHECK-SAME: (%[[arg0:.*]]: i64, %[[arg1:.*]]: !llvm.ptr) { +// CHECK-NEXT: llvm.store %[[arg0]], %[[arg1]] : !llvm.ptr +// CHECK-NEXT: llvm.return +// CHECK-NEXT: } + +func @test_store_box(%array : !fir.ref>>, %box : !fir.box>) { + fir.store %box to %array : !fir.ref>> + return +} + +// CHECK-LABEL: llvm.func @test_store_box +// CHECK-SAME: (%[[arg0:.*]]: !llvm.ptr, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, array<2 x array<3 x i{{.*}}>>)>>, +// CHECK-SAME: %[[arg1:.*]]: !llvm.ptr, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, array<2 x array<3 x i{{.*}}>>)>>) { +// CHECK-NEXT: %[[box_to_store:.*]] = llvm.load %arg1 : !llvm.ptr, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, array<2 x array<3 x i{{.*}}>>)>> +// CHECK-NEXT: llvm.store %[[box_to_store]], %[[arg0]] : !llvm.ptr, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, array<2 x array<3 x i{{.*}}>>)>> +// CHECK-NEXT: llvm.return +// CHECK-NEXT: } + +// ----- + +// Test `fir.load` --> `llvm.load` conversion + +func @test_load_index(%addr : !fir.ref) { + %0 = fir.load %addr : !fir.ref + return +} + +// CHECK-LABEL: llvm.func @test_load_index( +// CHECK-SAME: %[[arg1:.*]]: !llvm.ptr) { +// CHECK-NEXT: %0 = llvm.load %[[arg1]] : !llvm.ptr +// CHECK-NEXT: llvm.return +// CHECK-NEXT: } + +func @test_load_box(%addr : !fir.ref>>) { + %0 = fir.load %addr : !fir.ref>> + return +} + +// Loading a `fir.ref> is a no-op +// CHECK-LABEL: llvm.func @test_load_box +// CHECK-SAME: (%{{.*}}: !llvm.ptr>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, array<1 x array<3 x i{{.*}}>>)>>) { +// CHECK-NEXT: llvm.return +// CHECK-NEXT: }