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 @@ -29,6 +29,14 @@ // fir::LLVMTypeConverter for converting to LLVM IR dialect types. #include "TypeConverter.h" +static mlir::LLVM::ConstantOp +genConstantIndex(mlir::Location loc, mlir::Type ity, + mlir::ConversionPatternRewriter &rewriter, + std::int64_t offset) { + auto cattr = rewriter.getI64IntegerAttr(offset); + return rewriter.create(loc, ity, cattr); +} + namespace { /// FIR conversion pattern template template @@ -66,6 +74,34 @@ mlir::ConversionPatternRewriter &rewriter) const = 0; }; +/// Create value signaling an absent optional argument in a call, e.g. +/// `fir.absent !fir.ref` --> `llvm.mlir.null : !llvm.ptr` +struct AbsentOpConversion : public FIROpConversion { + 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()) { + auto structTy = ty.cast(); + assert(!structTy.isOpaque() && !structTy.getBody().empty()); + auto undefStruct = rewriter.create(loc, ty); + auto nullField = + rewriter.create(loc, structTy.getBody()[0]); + mlir::MLIRContext *ctx = absent.getContext(); + auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); + rewriter.replaceOpWithNewOp( + absent, ty, undefStruct, nullField, c0); + } else { + rewriter.replaceOpWithNewOp(absent, ty); + } + return success(); + } +}; + // Lower `fir.address_of` operation to `llvm.address_of` operation. struct AddrOfOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -813,6 +849,40 @@ } }; +/// `fir.is_present` --> +/// ``` +/// %0 = llvm.mlir.constant(0 : i64) +/// %1 = llvm.ptrtoint %0 +/// %2 = llvm.icmp "ne" %1, %0 : i64 +/// ``` +struct IsPresentOpConversion : public FIROpConversion { + 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()) { + auto structTy = ptr.getType().cast(); + 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(loc, ty, ptr, c0); + } + mlir::LLVM::ConstantOp c0 = + genConstantIndex(isPresent.getLoc(), idxTy, rewriter, 0); + auto addr = rewriter.create(loc, idxTy, ptr); + rewriter.replaceOpWithNewOp( + isPresent, mlir::LLVM::ICmpPredicate::ne, addr, c0); + + return success(); + } +}; } // namespace namespace { @@ -835,12 +905,13 @@ auto *context = getModule().getContext(); fir::LLVMTypeConverter typeConverter{getModule()}; mlir::OwningRewritePatternList pattern(context); - pattern.insert(typeConverter); mlir::populateStdToLLVMConversionPatterns(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 @@ -63,6 +63,9 @@ }); } + // i64 can be used to index into aggregates like arrays + mlir::Type indexType() { return mlir::IntegerType::get(&getContext(), 64); } + // 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 @@ -329,6 +329,7 @@ // CHECK: llvm.return // ----- + // Test `fir.call` -> `llvm.call` conversion for functions that take no arguments // and return nothing @@ -686,3 +687,87 @@ // 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.is_present` + +func @test_is_present_i64(%arg0: !fir.ref) -> () { + %0 = fir.is_present %arg0 : (!fir.ref) -> i1 + return +} + +// CHECK-LABEL: @test_is_present_i64 +// CHECK-SAME: (%[[arg:.*]]: !llvm.ptr) +// CHECK-NEXT: %[[constant:.*]] = llvm.mlir.constant(0 : i64) : i64 +// CHECK-NEXT: %[[ptr:.*]] = llvm.ptrtoint %[[arg]] : !llvm.ptr to i64 +// CHECK-NEXT: %{{.*}} = llvm.icmp "ne" %[[ptr]], %[[constant]] : i64 +// CHECK-NEXT: llvm.return +// CHECK-NEXT: } + +func @test_is_present_box(%arg0: !fir.box>) -> () { + %0 = fir.is_present %arg0 : (!fir.box>) -> i1 + return +} + +// CHECK-LABEL: @test_is_present_box +// CHECK-SAME: (%[[arg:.*]]: !llvm.ptr, i64, i32, i8, i8, i8, i8)>>) +// CHECK-NEXT: %[[constant:.*]] = llvm.mlir.constant(0 : i64) : i64 +// CHECK-NEXT: %[[ptr:.*]] = llvm.ptrtoint %[[arg]] : !llvm.ptr, 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 + return +} + +// CHECK-LABEL: @test_absent_i64 +// CHECK-NEXT: %{{.*}} = llvm.mlir.null : !llvm.ptr +// CHECK-NEXT: llvm.return +// CHECK-NEXT: } + +func @test_absent_box() -> () { + %0 = fir.absent !fir.box> + return +} +// CHECK-LABEL: @test_absent_box +// CHECK-NEXT: %{{.*}} = llvm.mlir.null : !llvm.ptr, 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) -> i1 { + %0 = fir.is_present %arg0 : (!fir.ref) -> i1 + return %0 : i1 +} + +// CHECK-LABEL: @is_present +// CHECK-SAME: (%[[arg:.*]]: !llvm.ptr) -> i1 +// CHECK-NEXT: %[[constant:.*]] = llvm.mlir.constant(0 : i64) : i64 +// CHECK-NEXT: %[[ptr:.*]] = llvm.ptrtoint %[[arg]] : !llvm.ptr 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 + %1 = fir.call @is_present(%0) : (!fir.ref) -> i1 + return %1 : i1 +} + +// CHECK-LABEL: @absent +// CHECK-SAME: () -> i1 +// CHECK-NEXT: %[[ptr:.*]] = llvm.mlir.null : !llvm.ptr +// CHECK-NEXT: %[[ret_val:.*]] = llvm.call @is_present(%[[ptr]]) : (!llvm.ptr) -> i1 +// CHECK-NEXT: llvm.return %[[ret_val]] : i1