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 @@ -206,6 +206,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; @@ -1326,6 +1354,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 { @@ -1349,17 +1411,18 @@ 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); 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 @@ -1113,3 +1113,87 @@ // CHECK: llvm.br ^bb5 // CHECK-LABEL: ^bb5: // CHECK: llvm.return + +// ----- + +// 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