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 @@ -250,6 +250,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; @@ -262,7 +285,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; @@ -275,7 +322,7 @@ } }; -// convert to LLVM IR dialect `unreachable` +/// `fir.unreachable` --> `llvm.unreachable` struct UnreachableOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -507,9 +554,9 @@ pattern.insert< AddrOfOpConversion, CallOpConversion, ExtractValueOpConversion, HasValueOpConversion, GlobalOpConversion, InsertOnRangeOpConversion, - InsertValueOpConversion, SelectOpConversion, SelectRankOpConversion, - UndefOpConversion, UnreachableOpConversion, ZeroOpConversion>( - typeConverter); + InsertValueOpConversion, LoadOpConversion, SelectOpConversion, + SelectRankOpConversion, StoreOpConversion, UndefOpConversion, + UnreachableOpConversion, ZeroOpConversion>(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 @@ -376,3 +376,57 @@ // CHECK-NEXT: %0 = llvm.call @dummy_return_val() : () -> i32 // CHECK-NEXT: llvm.return %0 : i32 // CHECK-NEXT: } + +// ----- + +// 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: }