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 @@ -42,6 +42,88 @@ return lowerTy().convertType(ty); } + mlir::LLVM::ConstantOp + genConstantOffset(mlir::Location loc, + mlir::ConversionPatternRewriter &rewriter, + int offset) const { + auto ity = lowerTy().offsetType(); + auto cattr = rewriter.getI32IntegerAttr(offset); + return rewriter.create(loc, ity, cattr); + } + + /// Construct code sequence to get the rank from a box. + mlir::Value getRankFromBox(mlir::Location loc, mlir::Value box, + mlir::Type resultTy, + mlir::ConversionPatternRewriter &rewriter) const { + mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0); + mlir::LLVM::ConstantOp cRank = + genConstantOffset(loc, rewriter, kRankPosInBox); + auto pty = mlir::LLVM::LLVMPointerType::get(resultTy); + auto p = rewriter.create( + loc, pty, mlir::ValueRange{box, c0, cRank}); + return rewriter.create(loc, resultTy, p); + } + + /// Method to construct code sequence to get the triple for dimension `dim` + /// from a box. + SmallVector + getDimsFromBox(mlir::Location loc, ArrayRef retTys, + mlir::Value box, mlir::Value dim, + mlir::ConversionPatternRewriter &rewriter) const { + mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0); + mlir::LLVM::ConstantOp cDims = + genConstantOffset(loc, rewriter, kDimsPosInBox); + mlir::LLVM::LoadOp l0 = + loadFromOffset(loc, box, c0, cDims, dim, 0, retTys[0], rewriter); + mlir::LLVM::LoadOp l1 = + loadFromOffset(loc, box, c0, cDims, dim, 1, retTys[1], rewriter); + mlir::LLVM::LoadOp l2 = + loadFromOffset(loc, box, c0, cDims, dim, 2, retTys[2], rewriter); + return {l0.getResult(), l1.getResult(), l2.getResult()}; + } + + mlir::LLVM::LoadOp + loadFromOffset(mlir::Location loc, mlir::Value a, mlir::LLVM::ConstantOp c0, + mlir::LLVM::ConstantOp cDims, mlir::Value dim, int off, + mlir::Type ty, + mlir::ConversionPatternRewriter &rewriter) const { + auto pty = mlir::LLVM::LLVMPointerType::get(ty); + mlir::LLVM::ConstantOp c = genConstantOffset(loc, rewriter, off); + mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, a, c0, cDims, dim, c); + return rewriter.create(loc, ty, p); + } + + /// Read base address from a fir.box. Returned address has type ty. + mlir::Value + loadBaseAddrFromBox(mlir::Location loc, mlir::Type ty, mlir::Value box, + mlir::ConversionPatternRewriter &rewriter) const { + mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0); + mlir::LLVM::ConstantOp cAddr = + genConstantOffset(loc, rewriter, kAddrPosInBox); + auto pty = mlir::LLVM::LLVMPointerType::get(ty); + mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, box, c0, cAddr); + return rewriter.create(loc, ty, p); + } + + mlir::Value + loadElementSizeFromBox(mlir::Location loc, mlir::Type ty, mlir::Value box, + mlir::ConversionPatternRewriter &rewriter) const { + mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0); + mlir::LLVM::ConstantOp cElemLen = + genConstantOffset(loc, rewriter, kElemLenPosInBox); + auto pty = mlir::LLVM::LLVMPointerType::get(ty); + mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, box, c0, cElemLen); + return rewriter.create(loc, ty, p); + } + + template + mlir::LLVM::GEPOp genGEP(mlir::Location loc, mlir::Type ty, + mlir::ConversionPatternRewriter &rewriter, + mlir::Value base, ARGS... args) const { + SmallVector cv{args...}; + return rewriter.create(loc, ty, base, cv); + } + fir::LLVMTypeConverter &lowerTy() const { return *static_cast(this->getTypeConverter()); } @@ -80,6 +162,84 @@ } }; +/// Lower `fir.box_addr` to the sequence of operations to extract the first +/// element of the box. +struct BoxAddrOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + mlir::LogicalResult + matchAndRewrite(fir::BoxAddrOp boxaddr, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const override { + mlir::Value a = adaptor.getOperands()[0]; + auto loc = boxaddr.getLoc(); + mlir::Type ty = convertType(boxaddr.getType()); + if (auto argty = boxaddr.val().getType().dyn_cast()) { + rewriter.replaceOp(boxaddr, loadBaseAddrFromBox(loc, ty, a, rewriter)); + } else { + auto c0attr = rewriter.getI32IntegerAttr(0); + auto c0 = mlir::ArrayAttr::get(boxaddr.getContext(), c0attr); + rewriter.replaceOpWithNewOp(boxaddr, ty, a, + c0); + } + return success(); + } +}; + +/// Lower `fir.box_dims` to a sequence of operations to extract the requested +/// dimension infomartion from the boxed value. +/// Result in a triple set of GEPs and loads. +struct BoxDimsOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + mlir::LogicalResult + matchAndRewrite(fir::BoxDimsOp boxdims, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const override { + SmallVector resultTypes = { + convertType(boxdims.getResult(0).getType()), + convertType(boxdims.getResult(1).getType()), + convertType(boxdims.getResult(2).getType()), + }; + auto results = + getDimsFromBox(boxdims.getLoc(), resultTypes, adaptor.getOperands()[0], + adaptor.getOperands()[1], rewriter); + rewriter.replaceOp(boxdims, results); + return success(); + } +}; + +/// Lower `fir.box_elesize` to a sequence of operations ro extract the size of +/// an element in the boxed value. +struct BoxEleSizeOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + mlir::LogicalResult + matchAndRewrite(fir::BoxEleSizeOp boxelesz, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const override { + mlir::Value a = adaptor.getOperands()[0]; + auto loc = boxelesz.getLoc(); + auto ty = convertType(boxelesz.getType()); + rewriter.replaceOp(boxelesz, loadElementSizeFromBox(loc, ty, a, rewriter)); + return success(); + } +}; + +/// Lower `fir.box_rank` to the sequence of operation to extract the rank from +/// the box. +struct BoxRankOpConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + mlir::LogicalResult + matchAndRewrite(fir::BoxRankOp boxrank, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const override { + mlir::Value a = adaptor.getOperands()[0]; + auto loc = boxrank.getLoc(); + mlir::Type ty = convertType(boxrank.getType()); + auto result = getRankFromBox(loc, a, ty, rewriter); + rewriter.replaceOp(boxrank, result); + return success(); + } +}; + // `fir.call` -> `llvm.call` struct CallOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -835,14 +995,16 @@ auto *context = getModule().getContext(); fir::LLVMTypeConverter typeConverter{getModule()}; mlir::OwningRewritePatternList pattern(context); - pattern.insert(typeConverter); + pattern.insert< + AddcOpConversion, AddrOfOpConversion, BoxAddrOpConversion, + BoxDimsOpConversion, BoxEleSizeOpConversion, BoxRankOpConversion, + CallOpConversion, ConvertOpConversion, DivcOpConversion, + ExtractValueOpConversion, HasValueOpConversion, GlobalOpConversion, + InsertOnRangeOpConversion, InsertValueOpConversion, LoadOpConversion, + NegcOpConversion, MulcOpConversion, SelectOpConversion, + SelectRankOpConversion, StoreOpConversion, SubcOpConversion, + UndefOpConversion, UnreachableOpConversion, ZeroOpConversion>( + typeConverter); mlir::populateStdToLLVMConversionPatterns(typeConverter, pattern); mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter, pattern); diff --git a/flang/lib/Optimizer/CodeGen/TypeConverter.h b/flang/lib/Optimizer/CodeGen/TypeConverter.h --- a/flang/lib/Optimizer/CodeGen/TypeConverter.h +++ b/flang/lib/Optimizer/CodeGen/TypeConverter.h @@ -79,6 +79,10 @@ }); } + // i32 is used here because LLVM wants i32 constants when indexing into struct + // types. Indexing into other aggregate types is more flexible. + mlir::Type offsetType() { return mlir::IntegerType::get(&getContext(), 32); } + // fir.type --> llvm<"%name = { ty... }"> mlir::Type convertRecordType(fir::RecordType derived) { auto name = derived.getName(); 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 @@ -686,3 +686,80 @@ // CHECK-SAME: (%{{.*}}: !llvm.ptr>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, array<1 x array<3 x i{{.*}}>>)>>) { // CHECK-NEXT: llvm.return // CHECK-NEXT: } + +// ----- + +// Test `fir.box_rank` conversion. + +func @extract_rank(%arg0: !fir.box>) -> i32 { + %0 = fir.box_rank %arg0 : (!fir.box>) -> i32 + return %0 : i32 +} + +// CHECK-LABEL: llvm.func @extract_rank( +// CHECK-SAME: %[[ARG0:.*]]: !llvm.ptr, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})>>) -> i32 +// CHECK: %[[C0:.*]] = llvm.mlir.constant(0 : i32) : i32 +// CHECK: %[[CRANK:.*]] = llvm.mlir.constant(3 : i32) : i32 +// CHECK: %[[GEP:.*]] = llvm.getelementptr %[[ARG0]][%[[C0]], %[[CRANK]]] : (!llvm.ptr, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})>>, i32, i32) -> !llvm.ptr +// CHECK: %[[RANK:.*]] = llvm.load %[[GEP]] : !llvm.ptr +// CHECK: llvm.return %[[RANK]] : i32 + +// ----- + +// Test `fir.box_addr` conversion. + +func @extract_addr(%arg0: !fir.box>) -> !fir.ref { + %0 = fir.box_addr %arg0 : (!fir.box>) -> !fir.ref + return %0 : !fir.ref +} + +// CHECK-LABEL: llvm.func @extract_addr( +// CHECK-SAME: %[[ARG0:.*]]: !llvm.ptr, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})>>) -> !llvm.ptr +// CHECK: %[[C0:.*]] = llvm.mlir.constant(0 : i32) : i32 +// CHECK: %[[CADDR:.*]] = llvm.mlir.constant(0 : i32) : i32 +// CHECK: %[[GEP:.*]] = llvm.getelementptr %[[ARG0]][%[[C0]], %[[CADDR]]] : (!llvm.ptr, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})>>, i32, i32) -> !llvm.ptr> +// CHECK: %[[ADDR:.*]] = llvm.load %[[GEP]] : !llvm.ptr> +// CHECK: llvm.return %[[ADDR]] : !llvm.ptr + +// ----- + +// Test `fir.box_dims` conversion. + +func @extract_dims(%arg0: !fir.box>) -> index { + %c1 = arith.constant 0 : i32 + %0:3 = fir.box_dims %arg0, %c1 : (!fir.box>, i32) -> (index, index, index) + return %0 : index +} + +// CHECK-LABEL: llvm.func @extract_dims( +// CHECK-SAME: %[[ARG0:.*]]: !llvm.ptr, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})>>) -> i64 +// CHECK: %[[C0_1:.*]] = llvm.mlir.constant(0 : i32) : i32 +// CHECK: %[[C0:.*]] = llvm.mlir.constant(0 : i32) : i32 +// CHECK: %[[CDIMS:.*]] = llvm.mlir.constant(7 : i32) : i32 +// CHECK: %[[C0_2:.*]] = llvm.mlir.constant(0 : i32) : i32 +// CHECK: %[[GEP0:.*]] = llvm.getelementptr %[[ARG0]][%[[C0]], %[[CDIMS]], %[[C0_1]], %[[C0_2]]] : (!llvm.ptr, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})>>, i32, i32, i32, i32) -> !llvm.ptr +// CHECK: %[[LOAD0:.*]] = llvm.load %[[GEP0]] : !llvm.ptr +// CHECK: %[[C1:.*]] = llvm.mlir.constant(1 : i32) : i32 +// CHECK: %[[GEP1:.*]] = llvm.getelementptr %[[ARG0]][%[[C0]], %[[CDIMS]], %[[C0_1]], %[[C1]]] : (!llvm.ptr, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})>>, i32, i32, i32, i32) -> !llvm.ptr +// CHECK: %[[LOAD1:.*]] = llvm.load %[[GEP1]] : !llvm.ptr +// CHECK: %[[C2:.*]] = llvm.mlir.constant(2 : i32) : i32 +// CHECK: %[[GEP2:.*]] = llvm.getelementptr %[[ARG0]][%[[C0]], %[[CDIMS]], %[[C0_1]], %[[C2]]] : (!llvm.ptr, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})>>, i32, i32, i32, i32) -> !llvm.ptr +// CHECK: %[[LOAD2:.*]] = llvm.load %[[GEP2]] : !llvm.ptr +// CHECK: llvm.return %[[LOAD0]] : i64 + +// ----- + +// Test `fir.box_elesize` conversion. + +func @extract_elesize(%arg0: !fir.box) -> i32 { + %0 = fir.box_elesize %arg0 : (!fir.box) -> i32 + return %0 : i32 +} + +// CHECK-LABEL: llvm.func @extract_elesize( +// CHECK-SAME: %[[ARG0:.*]]: !llvm.ptr, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})>>) -> i32 +// CHECK: %[[C0:.*]] = llvm.mlir.constant(0 : i32) : i32 +// CHECK: %[[CELESIZE:.*]] = llvm.mlir.constant(1 : i32) : i32 +// CHECK: %[[GEP:.*]] = llvm.getelementptr %[[ARG0]][%[[C0]], %[[CELESIZE]]] : (!llvm.ptr, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})>>, i32, i32) -> !llvm.ptr +// CHECK: %[[ELE_SIZE:.*]] = llvm.load %[[GEP]] : !llvm.ptr +// CHECK: llvm.return %[[ELE_SIZE]] : i32