[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