[flang] handle allocation of zero-sized objects (#149165)

This PR handles the allocation of zero-sized objects for different
implementations. One byte is allocated for the zero-sized objects.
This commit is contained in:
Kelvin Li 2025-07-17 23:52:48 -04:00 committed by GitHub
parent 52a9c493e6
commit df56b1a2cf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 79 additions and 23 deletions

View File

@ -1123,6 +1123,16 @@ struct AllocMemOpConversion : public fir::FIROpConversion<fir::AllocMemOp> {
for (mlir::Value opnd : adaptor.getOperands())
size = rewriter.create<mlir::LLVM::MulOp>(
loc, ity, size, integerCast(loc, rewriter, ity, opnd));
// As the return value of malloc(0) is implementation defined, allocate one
// byte to ensure the allocation status being true. This behavior aligns to
// what the runtime has.
mlir::Value zero = genConstantIndex(loc, ity, rewriter, 0);
mlir::Value one = genConstantIndex(loc, ity, rewriter, 1);
mlir::Value cmp = rewriter.create<mlir::LLVM::ICmpOp>(
loc, mlir::LLVM::ICmpPredicate::sgt, size, zero);
size = rewriter.create<mlir::LLVM::SelectOp>(loc, cmp, size, one);
auto mallocTyWidth = lowerTy().getIndexTypeBitwidth();
auto mallocTy =
mlir::IntegerType::get(rewriter.getContext(), mallocTyWidth);

View File

@ -20,7 +20,9 @@ func.func @allocmem_scalar_nonchar() -> !fir.heap<i32> {
// CHECK-SAME: i32 %[[len:.*]])
// CHECK: %[[mul1:.*]] = sext i32 %[[len]] to i64
// CHECK: %[[mul2:.*]] = mul i64 1, %[[mul1]]
// CHECK: %[[trunc:.*]] = trunc i64 %[[mul2]] to i32
// CHECK: %[[cmp:.*]] = icmp sgt i64 %[[mul2]], 0
// CHECK: %[[sz:.*]] = select i1 %[[cmp]], i64 %[[mul2]], i64 1
// CHECK: %[[trunc:.*]] = trunc i64 %[[sz]] to i32
// CHECK: call ptr @malloc(i32 %[[trunc]])
func.func @allocmem_scalar_dynchar(%l : i32) -> !fir.heap<!fir.char<1,?>> {
%1 = fir.allocmem !fir.char<1,?>(%l : i32)

View File

@ -87,7 +87,9 @@ func.func @alloca_scalar_dynchar_kind(%l : i32) -> !fir.ref<!fir.char<2,?>> {
// CHECK-SAME: i32 %[[len:.*]])
// CHECK: %[[mul1:.*]] = sext i32 %[[len]] to i64
// CHECK: %[[mul2:.*]] = mul i64 1, %[[mul1]]
// CHECK: call ptr @malloc(i64 %[[mul2]])
// CHECK: %[[cmp:.*]] = icmp sgt i64 %[[mul2]], 0
// CHECK: %[[size:.*]] = select i1 %[[cmp]], i64 %[[mul2]], i64 1
// CHECK: call ptr @malloc(i64 %[[size]])
func.func @allocmem_scalar_dynchar(%l : i32) -> !fir.heap<!fir.char<1,?>> {
%1 = fir.allocmem !fir.char<1,?>(%l : i32)
return %1 : !fir.heap<!fir.char<1,?>>
@ -97,7 +99,9 @@ func.func @allocmem_scalar_dynchar(%l : i32) -> !fir.heap<!fir.char<1,?>> {
// CHECK-SAME: i32 %[[len:.*]])
// CHECK: %[[mul1:.*]] = sext i32 %[[len]] to i64
// CHECK: %[[mul2:.*]] = mul i64 2, %[[mul1]]
// CHECK: call ptr @malloc(i64 %[[mul2]])
// CHECK: %[[cmp:.*]] = icmp sgt i64 %[[mul2]], 0
// CHECK: %[[size:.*]] = select i1 %[[cmp]], i64 %[[mul2]], i64 1
// CHECK: call ptr @malloc(i64 %[[size]])
func.func @allocmem_scalar_dynchar_kind(%l : i32) -> !fir.heap<!fir.char<2,?>>{
%1 = fir.allocmem !fir.char<2,?>(%l : i32)
return %1 : !fir.heap<!fir.char<2,?>>
@ -152,7 +156,9 @@ func.func @allocmem_array_of_char() -> !fir.heap<!fir.array<3x3x!fir.char<1,10>>
// CHECK-SAME: i32 %[[len:.*]])
// CHECK: %[[mul1:.*]] = sext i32 %[[len]] to i64
// CHECK: %[[mul2:.*]] = mul i64 9, %[[mul1]]
// CHECK: call ptr @malloc(i64 %[[mul2]])
// CHECK: %[[cmp:.*]] = icmp sgt i64 %[[mul2]], 0
// CHECK: %[[size:.*]] = select i1 %[[cmp]], i64 %[[mul2]], i64 1
// CHECK: call ptr @malloc(i64 %[[size]])
func.func @allocmem_array_of_dynchar(%l: i32) -> !fir.heap<!fir.array<3x3x!fir.char<1,?>>> {
%1 = fir.allocmem !fir.array<3x3x!fir.char<1,?>>(%l : i32)
return %1 : !fir.heap<!fir.array<3x3x!fir.char<1,?>>>
@ -180,7 +186,9 @@ func.func @alloca_dynarray_of_nonchar2(%e: index) -> !fir.ref<!fir.array<?x?xi32
// CHECK-LABEL: define ptr @allocmem_dynarray_of_nonchar(
// CHECK-SAME: i64 %[[extent:.*]])
// CHECK: %[[prod1:.*]] = mul i64 12, %[[extent]]
// CHECK: call ptr @malloc(i64 %[[prod1]])
// CHECK: %[[cmp:.*]] = icmp sgt i64 %[[prod1]], 0
// CHECK: %[[size:.*]] = select i1 %[[cmp]], i64 %[[prod1]], i64 1
// CHECK: call ptr @malloc(i64 %[[size]])
func.func @allocmem_dynarray_of_nonchar(%e: index) -> !fir.heap<!fir.array<3x?xi32>> {
%1 = fir.allocmem !fir.array<3x?xi32>, %e
return %1 : !fir.heap<!fir.array<3x?xi32>>
@ -190,7 +198,9 @@ func.func @allocmem_dynarray_of_nonchar(%e: index) -> !fir.heap<!fir.array<3x?xi
// CHECK-SAME: i64 %[[extent:.*]])
// CHECK: %[[prod1:.*]] = mul i64 4, %[[extent]]
// CHECK: %[[prod2:.*]] = mul i64 %[[prod1]], %[[extent]]
// CHECK: call ptr @malloc(i64 %[[prod2]])
// CHECK: %[[cmp:.*]] = icmp sgt i64 %[[prod2]], 0
// CHECK: %[[size:.*]] = select i1 %[[cmp]], i64 %[[prod2]], i64 1
// CHECK: call ptr @malloc(i64 %[[size]])
func.func @allocmem_dynarray_of_nonchar2(%e: index) -> !fir.heap<!fir.array<?x?xi32>> {
%1 = fir.allocmem !fir.array<?x?xi32>, %e, %e
return %1 : !fir.heap<!fir.array<?x?xi32>>
@ -218,7 +228,9 @@ func.func @alloca_dynarray_of_char2(%e : index) -> !fir.ref<!fir.array<?x?x!fir.
// CHECK-LABEL: define ptr @allocmem_dynarray_of_char(
// CHECK-SAME: i64 %[[extent:.*]])
// CHECK: %[[prod1:.*]] = mul i64 60, %[[extent]]
// CHECK: call ptr @malloc(i64 %[[prod1]])
// CHECK: %[[cmp:.*]] = icmp sgt i64 %[[prod1]], 0
// CHECK: %[[size:.*]] = select i1 %[[cmp]], i64 %[[prod1]], i64 1
// CHECK: call ptr @malloc(i64 %[[size]])
func.func @allocmem_dynarray_of_char(%e : index) -> !fir.heap<!fir.array<3x?x!fir.char<2,10>>> {
%1 = fir.allocmem !fir.array<3x?x!fir.char<2,10>>, %e
return %1 : !fir.heap<!fir.array<3x?x!fir.char<2,10>>>
@ -228,7 +240,9 @@ func.func @allocmem_dynarray_of_char(%e : index) -> !fir.heap<!fir.array<3x?x!fi
// CHECK-SAME: i64 %[[extent:.*]])
// CHECK: %[[prod1:.*]] = mul i64 20, %[[extent]]
// CHECK: %[[prod2:.*]] = mul i64 %[[prod1]], %[[extent]]
// CHECK: call ptr @malloc(i64 %[[prod2]])
// CHECK: %[[cmp:.*]] = icmp sgt i64 %[[prod2]], 0
// CHECK: %[[size:.*]] = select i1 %[[cmp]], i64 %[[mul2]], i64 1
// CHECK: call ptr @malloc(i64 %[[size]])
func.func @allocmem_dynarray_of_char2(%e : index) -> !fir.heap<!fir.array<?x?x!fir.char<2,10>>> {
%1 = fir.allocmem !fir.array<?x?x!fir.char<2,10>>, %e, %e
return %1 : !fir.heap<!fir.array<?x?x!fir.char<2,10>>>
@ -261,7 +275,9 @@ func.func @alloca_dynarray_of_dynchar2(%l: i32, %e : index) -> !fir.ref<!fir.arr
// CHECK: %[[prod1:.*]] = sext i32 %[[len]] to i64
// CHECK: %[[prod2:.*]] = mul i64 6, %[[prod1]]
// CHECK: %[[prod3:.*]] = mul i64 %[[prod2]], %[[extent]]
// CHECK: call ptr @malloc(i64 %[[prod3]])
// CHECK: %[[cmp:.*]] = icmp sgt i64 %[[prod3]], 0
// CHECK: %[[size:.*]] = select i1 %[[cmp]], i64 %[[prod3]], i64 1
// CHECK: call ptr @malloc(i64 %[[size]])
func.func @allocmem_dynarray_of_dynchar(%l: i32, %e : index) -> !fir.heap<!fir.array<3x?x!fir.char<2,?>>> {
%1 = fir.allocmem !fir.array<3x?x!fir.char<2,?>>(%l : i32), %e
return %1 : !fir.heap<!fir.array<3x?x!fir.char<2,?>>>
@ -273,7 +289,9 @@ func.func @allocmem_dynarray_of_dynchar(%l: i32, %e : index) -> !fir.heap<!fir.a
// CHECK: %[[prod1:.*]] = mul i64 2, %[[a]]
// CHECK: %[[prod2:.*]] = mul i64 %[[prod1]], %[[extent]]
// CHECK: %[[prod3:.*]] = mul i64 %[[prod2]], %[[extent]]
// CHECK: call ptr @malloc(i64 %[[prod3]])
// CHECK: %[[cmp:.*]] = icmp sgt i64 %[[prod3]], 0
// CHECK: %[[size:.*]] = select i1 %[[cmp]], i64 %[[prod3]], i64 1
// CHECK: call ptr @malloc(i64 %[[size]])
func.func @allocmem_dynarray_of_dynchar2(%l: i32, %e : index) -> !fir.heap<!fir.array<?x?x!fir.char<2,?>>> {
%1 = fir.allocmem !fir.array<?x?x!fir.char<2,?>>(%l : i32), %e, %e
return %1 : !fir.heap<!fir.array<?x?x!fir.char<2,?>>>
@ -312,7 +330,9 @@ func.func @alloca_array_with_holes_dynchar(%arg0: index, %arg1: index) -> !fir.r
// CHECK-SAME: i64 %[[e1:.*]], i64 %[[e2:.*]])
// CHECK: %[[a:.*]] = mul i64 240, %[[e1]]
// CHECK: %[[b:.*]] = mul i64 %3, %[[e2]]
// CHECK: call ptr @malloc(i64 %[[b]])
// CHECK: %[[cmp:.*]] = icmp sgt i64 %[[b]], 0
// CHECK: %[[size:.*]] = select i1 %[[cmp]], i64 %[[b]], i64 1
// CHECK: call ptr @malloc(i64 %[[size]])
func.func @allocmem_array_with_holes_nonchar(%0 : index, %1 : index) -> !fir.heap<!fir.array<4x?x3x?x5xi32>> {
%a = fir.allocmem !fir.array<4x?x3x?x5xi32>, %0, %1
return %a : !fir.heap<!fir.array<4x?x3x?x5xi32>>
@ -321,7 +341,9 @@ func.func @allocmem_array_with_holes_nonchar(%0 : index, %1 : index) -> !fir.hea
// CHECK-LABEL: define ptr @allocmem_array_with_holes_char(
// CHECK-SAME: i64 %[[e:.*]])
// CHECK: %[[mul:.*]] = mul i64 240, %[[e]]
// CHECK: call ptr @malloc(i64 %[[mul]])
// CHECK: %[[cmp:.*]] = icmp sgt i64 %[[mul]], 0
// CHECK: %[[size:.*]] = select i1 %[[cmp]], i64 %[[mul]], i64 1
// CHECK: call ptr @malloc(i64 %[[size]])
func.func @allocmem_array_with_holes_char(%e: index) -> !fir.heap<!fir.array<3x?x4x!fir.char<2,10>>> {
%1 = fir.allocmem !fir.array<3x?x4x!fir.char<2,10>>, %e
return %1 : !fir.heap<!fir.array<3x?x4x!fir.char<2,10>>>
@ -331,7 +353,9 @@ func.func @allocmem_array_with_holes_char(%e: index) -> !fir.heap<!fir.array<3x?
// CHECK-SAME: i64 %[[len:.*]], i64 %[[extent:.*]])
// CHECK: %[[a:.*]] = mul i64 24, %[[len]]
// CHECK: %[[b:.*]] = mul i64 %[[a]], %[[extent]]
// CHECK: call ptr @malloc(i64 %[[b]])
// CHECK: %[[cmp:.*]] = icmp sgt i64 %[[b]], 0
// CHECK: %[[size:.*]] = select i1 %[[cmp]], i64 %[[b]], i64 1
// CHECK: call ptr @malloc(i64 %[[size]])
func.func @allocmem_array_with_holes_dynchar(%arg0: index, %arg1: index) -> !fir.heap<!fir.array<3x?x4x!fir.char<2,?>>> {
%1 = fir.allocmem !fir.array<3x?x4x!fir.char<2,?>>(%arg0 : index), %arg1
return %1 : !fir.heap<!fir.array<3x?x4x!fir.char<2,?>>>

View File

@ -146,7 +146,9 @@ func.func @f6(%arg0: !fir.box<!fir.array<?xf32>>, %arg1: f32) {
// CHECK: %[[EXT_GEP:.*]] = getelementptr {{.*}} %[[A]], i32 0, i32 7, i64 0, i32 1
// CHECK: %[[EXTENT:.*]] = load i64, ptr %[[EXT_GEP]]
// CHECK: %[[SIZE:.*]] = mul i64 4, %[[EXTENT]]
// CHECK: %[[MALLOC:.*]] = call ptr @malloc(i64 %[[SIZE]])
// CHECK: %[[CMP:.*]] = icmp sgt i64 %[[SIZE]], 0
// CHECK: %[[SZ:.*]] = select i1 %[[CMP]], i64 %[[SIZE]], i64 1
// CHECK: %[[MALLOC:.*]] = call ptr @malloc(i64 %[[SZ]])
%1 = fir.slice %c2, %c10, %c1 : (index, index, index) -> !fir.slice<1>
%2 = fir.array_load %arg0 [%1] : (!fir.box<!fir.array<?xf32>>, !fir.slice<1>) -> !fir.array<?xf32>
%3 = fir.slice %c1, %c9, %c1 : (index, index, index) -> !fir.slice<1>

View File

@ -216,10 +216,14 @@ func.func @test_alloc_and_freemem_one() {
}
// CHECK-LABEL: llvm.func @test_alloc_and_freemem_one() {
// CHECK: %[[N:.*]] = llvm.mlir.constant(4 : i64) : i64
// CHECK-NEXT: llvm.call @malloc(%[[N]])
// CHECK: llvm.call @free(%{{.*}})
// CHECK-NEXT: llvm.return
// CHECK-DAG: %[[N:.*]] = llvm.mlir.constant(4 : i64) : i64
// CHECK-DAG: %[[ZERO:.*]] = llvm.mlir.constant(0 : i64) : i64
// CHECK-DAG: %[[ONE:.*]] = llvm.mlir.constant(1 : i64) : i64
// CHECK-NEXT: %[[CMP:.*]] = llvm.icmp "sgt" %[[N]], %[[ZERO]] : i64
// CHECK-NEXT: %[[SZ:.*]] = llvm.select %[[CMP]], %[[N]], %[[ONE]] : i1, i64
// CHECK-NEXT: llvm.call @malloc(%[[SZ]])
// CHECK: llvm.call @free(%{{.*}})
// CHECK-NEXT: llvm.return
// -----
// Verify that fir.allocmem is transformed to a call to malloc
@ -233,8 +237,12 @@ func.func @test_alloc_and_freemem_several() {
}
// CHECK-LABEL: llvm.func @test_alloc_and_freemem_several() {
// CHECK: %[[N:.*]] = llvm.mlir.constant(400 : i64) : i64
// CHECK: [[MALLOC:%.*]] = llvm.call @malloc(%[[N]])
// CHECK-DAG: %[[N:.*]] = llvm.mlir.constant(400 : i64) : i64
// CHECK-DAG: %[[ZERO:.*]] = llvm.mlir.constant(0 : i64) : i64
// CHECK-DAG: %[[ONE:.*]] = llvm.mlir.constant(1 : i64) : i64
// CHECK-NEXT: %[[CMP:.*]] = llvm.icmp "sgt" %[[N]], %[[ZERO]] : i64
// CHECK-NEXT: %[[SZ:.*]] = llvm.select %[[CMP]], %[[N]], %[[ONE]] : i1, i64
// CHECK: [[MALLOC:%.*]] = llvm.call @malloc(%[[SZ]])
// CHECK: llvm.call @free([[MALLOC]])
// CHECK: llvm.return
@ -250,7 +258,11 @@ func.func @test_with_shape(%ncols: index, %nrows: index) {
// CHECK: %[[FOUR:.*]] = llvm.mlir.constant(4 : i64) : i64
// CHECK: %[[DIM1_SIZE:.*]] = llvm.mul %[[FOUR]], %[[NCOLS]] : i64
// CHECK: %[[TOTAL_SIZE:.*]] = llvm.mul %[[DIM1_SIZE]], %[[NROWS]] : i64
// CHECK: %[[MEM:.*]] = llvm.call @malloc(%[[TOTAL_SIZE]])
// CHECK: %[[ZERO:.*]] = llvm.mlir.constant(0 : i64) : i64
// CHECK: %[[ONE:.*]] = llvm.mlir.constant(1 : i64) : i64
// CHECK: %[[CMP:.*]] = llvm.icmp "sgt" %[[TOTAL_SIZE]], %[[ZERO]] : i64
// CHECK: %[[SZ:.*]] = llvm.select %[[CMP]], %[[TOTAL_SIZE]], %[[ONE]] : i1, i64
// CHECK: %[[MEM:.*]] = llvm.call @malloc(%[[SZ]])
// CHECK: llvm.call @free(%[[MEM]]) : (!llvm.ptr) -> ()
// CHECK: llvm.return
// CHECK: }
@ -266,7 +278,11 @@ func.func @test_string_with_shape(%len: index, %nelems: index) {
// CHECK: %[[ONE:.*]] = llvm.mlir.constant(1 : i64) : i64
// CHECK: %[[LEN_SIZE:.*]] = llvm.mul %[[ONE]], %[[LEN]] : i64
// CHECK: %[[TOTAL_SIZE:.*]] = llvm.mul %[[LEN_SIZE]], %[[NELEMS]] : i64
// CHECK: %[[MEM:.*]] = llvm.call @malloc(%[[TOTAL_SIZE]])
// CHECK: %[[ZERO:.*]] = llvm.mlir.constant(0 : i64) : i64
// CHECK: %[[ONEA:.*]] = llvm.mlir.constant(1 : i64) : i64
// CHECK: %[[CMP:.*]] = llvm.icmp "sgt" %[[TOTAL_SIZE]], %[[ZERO]] : i64
// CHECK: %[[SZ:.*]] = llvm.select %[[CMP]], %[[TOTAL_SIZE]], %[[ONEA]] : i1, i64
// CHECK: %[[MEM:.*]] = llvm.call @malloc(%[[SZ]])
// CHECK: llvm.call @free(%[[MEM]]) : (!llvm.ptr) -> ()
// CHECK: llvm.return
// CHECK: }

View File

@ -29,7 +29,9 @@ end program test
! CHECK: %[[esval:.*]] = load i64, ptr %[[elesize]]
! CHECK: %[[mul:.*]] = mul i64 1, %[[esval]]
! CHECK: %[[mul2:.*]] = mul i64 %[[mul]], %[[extval]]
! CHECK: %[[buff:.*]] = call ptr @malloc(i64 %[[mul2]])
! CHECK: %[[cmp:.*]] = icmp sgt i64 %[[mul2]], 0
! CHECK: %[[size:.*]] = select i1 %[[cmp]], i64 %[[mul2]], i64 1
! CHECK: %[[buff:.*]] = call ptr @malloc(i64 %[[size]])
! CHECK: %[[to:.*]] = getelementptr i8, ptr %[[buff]], i64 %
! CHECK: call void @llvm.memmove.p0.p0.i64(ptr %[[to]], ptr %{{.*}}, i64 %{{.*}}, i1 false)
! CHECK: call void @free(ptr %[[buff]])