forked from OSchip/llvm-project
[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:
parent
96d027b503
commit
420ad7ce7d
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue