[flang][CodeGen] Transform `IsPresentOpConversion` and `AbsentOpConversion`

This patch extends the `FIRToLLVMLowering` pass in Flang by adding
hooks to transform `fir.is_present` and `fir.absent` to the LLVM dialect
of MLIR.

This is part of the upstreaming effort from the `fir-dev` branch in [1].

[1] https://github.com/flang-compiler/f18-llvm-project

Differential Revision: https://reviews.llvm.org/D113395

Originally written by:
Co-authored-by: Jean Perier <jperier@nvidia.com>
Co-authored-by: Eric Schweitz <eschweitz@nvidia.com>
This commit is contained in:
Andrzej Warzynski 2021-11-08 11:35:48 +00:00
parent 96d027b503
commit 420ad7ce7d
2 changed files with 158 additions and 11 deletions

View File

@ -206,6 +206,34 @@ public:
mlir::ConversionPatternRewriter &rewriter) const = 0;
};
/// Create value signaling an absent optional argument in a call, e.g.
/// `fir.absent !fir.ref<i64>` --> `llvm.mlir.null : !llvm.ptr<i64>`
struct AbsentOpConversion : public FIROpConversion<fir::AbsentOp> {
using FIROpConversion::FIROpConversion;
mlir::LogicalResult
matchAndRewrite(fir::AbsentOp absent, OpAdaptor,
mlir::ConversionPatternRewriter &rewriter) const override {
mlir::Type ty = convertType(absent.getType());
mlir::Location loc = absent.getLoc();
if (absent.getType().isa<fir::BoxCharType>()) {
auto structTy = ty.cast<mlir::LLVM::LLVMStructType>();
assert(!structTy.isOpaque() && !structTy.getBody().empty());
auto undefStruct = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
auto nullField =
rewriter.create<mlir::LLVM::NullOp>(loc, structTy.getBody()[0]);
mlir::MLIRContext *ctx = absent.getContext();
auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
absent, ty, undefStruct, nullField, c0);
} else {
rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(absent, ty);
}
return success();
}
};
// Lower `fir.address_of` operation to `llvm.address_of` operation.
struct AddrOfOpConversion : public FIROpConversion<fir::AddrOfOp> {
using FIROpConversion::FIROpConversion;
@ -1326,6 +1354,40 @@ struct NegcOpConversion : public FIROpConversion<fir::NegcOp> {
}
};
/// `fir.is_present` -->
/// ```
/// %0 = llvm.mlir.constant(0 : i64)
/// %1 = llvm.ptrtoint %0
/// %2 = llvm.icmp "ne" %1, %0 : i64
/// ```
struct IsPresentOpConversion : public FIROpConversion<fir::IsPresentOp> {
using FIROpConversion::FIROpConversion;
mlir::LogicalResult
matchAndRewrite(fir::IsPresentOp isPresent, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const override {
mlir::Type idxTy = lowerTy().indexType();
mlir::Location loc = isPresent.getLoc();
auto ptr = adaptor.getOperands()[0];
if (isPresent.val().getType().isa<fir::BoxCharType>()) {
auto structTy = ptr.getType().cast<mlir::LLVM::LLVMStructType>();
assert(!structTy.isOpaque() && !structTy.getBody().empty());
mlir::Type ty = structTy.getBody()[0];
mlir::MLIRContext *ctx = isPresent.getContext();
auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
ptr = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, ptr, c0);
}
mlir::LLVM::ConstantOp c0 =
genConstantIndex(isPresent.getLoc(), idxTy, rewriter, 0);
auto addr = rewriter.create<mlir::LLVM::PtrToIntOp>(loc, idxTy, ptr);
rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
isPresent, mlir::LLVM::ICmpPredicate::ne, addr, c0);
return success();
}
};
} // namespace
namespace {
@ -1349,17 +1411,18 @@ public:
fir::LLVMTypeConverter typeConverter{getModule()};
mlir::OwningRewritePatternList pattern(context);
pattern.insert<
AddcOpConversion, AddrOfOpConversion, AllocaOpConversion,
BoxAddrOpConversion, BoxDimsOpConversion, BoxEleSizeOpConversion,
BoxIsAllocOpConversion, BoxIsArrayOpConversion, BoxIsPtrOpConversion,
BoxRankOpConversion, CallOpConversion, ConvertOpConversion,
DispatchOpConversion, DispatchTableOpConversion, DTEntryOpConversion,
DivcOpConversion, ExtractValueOpConversion, HasValueOpConversion,
GlobalOpConversion, InsertOnRangeOpConversion, InsertValueOpConversion,
LoadOpConversion, NegcOpConversion, MulcOpConversion,
SelectCaseOpConversion, SelectOpConversion, SelectRankOpConversion,
StoreOpConversion, SubcOpConversion, UndefOpConversion,
UnreachableOpConversion, ZeroOpConversion>(typeConverter);
AbsentOpConversion, AddcOpConversion, AddrOfOpConversion,
AllocaOpConversion, BoxAddrOpConversion, BoxDimsOpConversion,
BoxEleSizeOpConversion, BoxIsAllocOpConversion, BoxIsArrayOpConversion,
BoxIsPtrOpConversion, BoxRankOpConversion, CallOpConversion,
ConvertOpConversion, DispatchOpConversion, DispatchTableOpConversion,
DTEntryOpConversion, DivcOpConversion, ExtractValueOpConversion,
HasValueOpConversion, GlobalOpConversion, InsertOnRangeOpConversion,
InsertValueOpConversion, IsPresentOpConversion, LoadOpConversion,
NegcOpConversion, MulcOpConversion, SelectCaseOpConversion,
SelectOpConversion, SelectRankOpConversion, StoreOpConversion,
SubcOpConversion, UndefOpConversion, UnreachableOpConversion,
ZeroOpConversion>(typeConverter);
mlir::populateStdToLLVMConversionPatterns(typeConverter, pattern);
mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter,
pattern);

View File

@ -1113,3 +1113,87 @@ func @select_case_logical(%arg0: !fir.ref<!fir.logical<4>>) {
// CHECK: llvm.br ^bb5
// CHECK-LABEL: ^bb5:
// CHECK: llvm.return
// -----
// Test `fir.is_present`
func @test_is_present_i64(%arg0: !fir.ref<i64>) -> () {
%0 = fir.is_present %arg0 : (!fir.ref<i64>) -> i1
return
}
// CHECK-LABEL: @test_is_present_i64
// CHECK-SAME: (%[[arg:.*]]: !llvm.ptr<i64>)
// CHECK-NEXT: %[[constant:.*]] = llvm.mlir.constant(0 : i64) : i64
// CHECK-NEXT: %[[ptr:.*]] = llvm.ptrtoint %[[arg]] : !llvm.ptr<i64> to i64
// CHECK-NEXT: %{{.*}} = llvm.icmp "ne" %[[ptr]], %[[constant]] : i64
// CHECK-NEXT: llvm.return
// CHECK-NEXT: }
func @test_is_present_box(%arg0: !fir.box<!fir.ref<i64>>) -> () {
%0 = fir.is_present %arg0 : (!fir.box<!fir.ref<i64>>) -> i1
return
}
// CHECK-LABEL: @test_is_present_box
// CHECK-SAME: (%[[arg:.*]]: !llvm.ptr<struct<(ptr<i64>, i64, i32, i8, i8, i8, i8)>>)
// CHECK-NEXT: %[[constant:.*]] = llvm.mlir.constant(0 : i64) : i64
// CHECK-NEXT: %[[ptr:.*]] = llvm.ptrtoint %[[arg]] : !llvm.ptr<struct<(ptr<i64>, i64, i32, i8, i8, i8, i8)>> to i64
// CHECK-NEXT: %{{.*}} = llvm.icmp "ne" %[[ptr]], %[[constant]] : i64
// CHECK-NEXT: llvm.return
// CHECK-NEXT: }
// -----
// Test `fir.absent`
func @test_absent_i64() -> () {
%0 = fir.absent !fir.ref<i64>
return
}
// CHECK-LABEL: @test_absent_i64
// CHECK-NEXT: %{{.*}} = llvm.mlir.null : !llvm.ptr<i64>
// CHECK-NEXT: llvm.return
// CHECK-NEXT: }
func @test_absent_box() -> () {
%0 = fir.absent !fir.box<!fir.array<?xf32>>
return
}
// CHECK-LABEL: @test_absent_box
// CHECK-NEXT: %{{.*}} = llvm.mlir.null : !llvm.ptr<struct<(ptr<f32>, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>)>>
// CHECK-NEXT: llvm.return
// CHECK-NEXT: }
// -----
// This is a bit more comprehensive test for `fir.is_present` and `fir.absent`
// when used together
func @is_present(%arg0: !fir.ref<i64>) -> i1 {
%0 = fir.is_present %arg0 : (!fir.ref<i64>) -> i1
return %0 : i1
}
// CHECK-LABEL: @is_present
// CHECK-SAME: (%[[arg:.*]]: !llvm.ptr<i64>) -> i1
// CHECK-NEXT: %[[constant:.*]] = llvm.mlir.constant(0 : i64) : i64
// CHECK-NEXT: %[[ptr:.*]] = llvm.ptrtoint %[[arg]] : !llvm.ptr<i64> to i64
// CHECK-NEXT: %[[ret_val:.*]] = llvm.icmp "ne" %[[ptr]], %[[constant]] : i64
// CHECK-NEXT: llvm.return %[[ret_val]] : i1
// CHECK-NEXT: }
func @absent() -> i1 {
%0 = fir.absent !fir.ref<i64>
%1 = fir.call @is_present(%0) : (!fir.ref<i64>) -> i1
return %1 : i1
}
// CHECK-LABEL: @absent
// CHECK-SAME: () -> i1
// CHECK-NEXT: %[[ptr:.*]] = llvm.mlir.null : !llvm.ptr<i64>
// CHECK-NEXT: %[[ret_val:.*]] = llvm.call @is_present(%[[ptr]]) : (!llvm.ptr<i64>) -> i1
// CHECK-NEXT: llvm.return %[[ret_val]] : i1