From 8ea361b26adbad56aa8c0b04d2b1113fe3e20fd7 Mon Sep 17 00:00:00 2001 From: Kelvin Li Date: Wed, 16 Jul 2025 14:24:43 -0400 Subject: [PATCH 1/2] [flang] handle allocation of zero-sized objects --- flang/lib/Optimizer/CodeGen/CodeGen.cpp | 16 +++++++++-- flang/test/Lower/zero-size2.f90 | 37 +++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 flang/test/Lower/zero-size2.f90 diff --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp index ecc04a6c9a2be..06686005bf2c9 100644 --- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp +++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp @@ -1119,9 +1119,19 @@ struct AllocMemOpConversion : public fir::FIROpConversion { mlir::Value size = genTypeSizeInBytes(loc, ity, rewriter, llvmObjectTy); if (auto scaleSize = genAllocationScaleSize(heap, ity, rewriter)) size = rewriter.create(loc, ity, size, scaleSize); - for (mlir::Value opnd : adaptor.getOperands()) - size = rewriter.create( - loc, ity, size, integerCast(loc, rewriter, ity, opnd)); + for (mlir::Value opnd : adaptor.getOperands()) { + auto arg = integerCast(loc, rewriter, ity, opnd); + auto val = fir::getIntIfConstant(arg); + if (val && *val == 0) { + // 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. + size = genConstantIndex(loc, ity, rewriter, 1); + break; + } else { + size = rewriter.create(loc, ity, size, arg); + } + } auto mallocTyWidth = lowerTy().getIndexTypeBitwidth(); auto mallocTy = mlir::IntegerType::get(rewriter.getContext(), mallocTyWidth); diff --git a/flang/test/Lower/zero-size2.f90 b/flang/test/Lower/zero-size2.f90 new file mode 100644 index 0000000000000..efaf57a6ac59f --- /dev/null +++ b/flang/test/Lower/zero-size2.f90 @@ -0,0 +1,37 @@ +! RUN: %flang_fc1 -emit-llvm -o - %s | FileCheck %s + +subroutine sub1() + integer, allocatable :: arr(:) + allocate(arr(0)) +! CHECK-LABEL: @sub1_ +! CHECK: %[[p:.*]] = call ptr @malloc(i64 1) +end + +subroutine sub2() + real, allocatable :: arr(:,:) + allocate(arr(10,0)) +! CHECK-LABEL: @sub2_ +! CHECK: %[[p:.*]] = call ptr @malloc(i64 1) +end + +subroutine sub3(i) + integer :: i + real, allocatable :: arr(:,:) + allocate(arr(i,0)) +! CHECK-LABEL: @sub3_ +! CHECK: %[[p:.*]] = call ptr @malloc(i64 1) +end + +subroutine sub4() + character(:), allocatable :: c + allocate(character(0)::c) +! CHECK-LABEL: @sub4_ +! CHECK: %[[p:.*]] = call ptr @malloc(i64 1) +end + +subroutine sub5() + character(:), allocatable :: c(:) + allocate(character(5)::c(0)) +! CHECK-LABEL: @sub5_ +! CHECK: %[[p:.*]] = call ptr @malloc(i64 1) +end From 2bc33b58555946431edb0579f378a0fb36306d29 Mon Sep 17 00:00:00 2001 From: Kelvin Li Date: Wed, 16 Jul 2025 23:40:42 -0400 Subject: [PATCH 2/2] update changes to address review comments --- flang/lib/Optimizer/CodeGen/CodeGen.cpp | 26 +++++++------- flang/test/Fir/alloc-32.fir | 4 ++- flang/test/Fir/alloc.fir | 48 ++++++++++++++++++------- flang/test/Fir/arrexp.fir | 4 ++- flang/test/Fir/convert-to-llvm.fir | 32 ++++++++++++----- flang/test/Lower/forall/character-1.f90 | 4 ++- flang/test/Lower/zero-size2.f90 | 37 ------------------- 7 files changed, 82 insertions(+), 73 deletions(-) delete mode 100644 flang/test/Lower/zero-size2.f90 diff --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp index 06686005bf2c9..04389bdf44684 100644 --- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp +++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp @@ -1119,19 +1119,19 @@ struct AllocMemOpConversion : public fir::FIROpConversion { mlir::Value size = genTypeSizeInBytes(loc, ity, rewriter, llvmObjectTy); if (auto scaleSize = genAllocationScaleSize(heap, ity, rewriter)) size = rewriter.create(loc, ity, size, scaleSize); - for (mlir::Value opnd : adaptor.getOperands()) { - auto arg = integerCast(loc, rewriter, ity, opnd); - auto val = fir::getIntIfConstant(arg); - if (val && *val == 0) { - // 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. - size = genConstantIndex(loc, ity, rewriter, 1); - break; - } else { - size = rewriter.create(loc, ity, size, arg); - } - } + for (mlir::Value opnd : adaptor.getOperands()) + size = rewriter.create( + 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( + loc, mlir::LLVM::ICmpPredicate::sgt, size, zero); + size = rewriter.create(loc, cmp, size, one); + auto mallocTyWidth = lowerTy().getIndexTypeBitwidth(); auto mallocTy = mlir::IntegerType::get(rewriter.getContext(), mallocTyWidth); diff --git a/flang/test/Fir/alloc-32.fir b/flang/test/Fir/alloc-32.fir index 3eefc3225fac7..a3cbf200c24fc 100644 --- a/flang/test/Fir/alloc-32.fir +++ b/flang/test/Fir/alloc-32.fir @@ -20,7 +20,9 @@ func.func @allocmem_scalar_nonchar() -> !fir.heap { // 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> { %1 = fir.allocmem !fir.char<1,?>(%l : i32) diff --git a/flang/test/Fir/alloc.fir b/flang/test/Fir/alloc.fir index 5b4930bb9cb34..8da8b828c18b9 100644 --- a/flang/test/Fir/alloc.fir +++ b/flang/test/Fir/alloc.fir @@ -87,7 +87,9 @@ func.func @alloca_scalar_dynchar_kind(%l : i32) -> !fir.ref> { // 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> { %1 = fir.allocmem !fir.char<1,?>(%l : i32) return %1 : !fir.heap> @@ -97,7 +99,9 @@ func.func @allocmem_scalar_dynchar(%l : i32) -> !fir.heap> { // 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>{ %1 = fir.allocmem !fir.char<2,?>(%l : i32) return %1 : !fir.heap> @@ -152,7 +156,9 @@ func.func @allocmem_array_of_char() -> !fir.heap> // 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>> { %1 = fir.allocmem !fir.array<3x3x!fir.char<1,?>>(%l : i32) return %1 : !fir.heap>> @@ -180,7 +186,9 @@ func.func @alloca_dynarray_of_nonchar2(%e: index) -> !fir.ref !fir.heap> { %1 = fir.allocmem !fir.array<3x?xi32>, %e return %1 : !fir.heap> @@ -190,7 +198,9 @@ func.func @allocmem_dynarray_of_nonchar(%e: index) -> !fir.heap !fir.heap> { %1 = fir.allocmem !fir.array, %e, %e return %1 : !fir.heap> @@ -218,7 +228,9 @@ func.func @alloca_dynarray_of_char2(%e : index) -> !fir.ref !fir.heap>> { %1 = fir.allocmem !fir.array<3x?x!fir.char<2,10>>, %e return %1 : !fir.heap>> @@ -228,7 +240,9 @@ func.func @allocmem_dynarray_of_char(%e : index) -> !fir.heap !fir.heap>> { %1 = fir.allocmem !fir.array>, %e, %e return %1 : !fir.heap>> @@ -261,7 +275,9 @@ func.func @alloca_dynarray_of_dynchar2(%l: i32, %e : index) -> !fir.ref !fir.heap>> { %1 = fir.allocmem !fir.array<3x?x!fir.char<2,?>>(%l : i32), %e return %1 : !fir.heap>> @@ -273,7 +289,9 @@ func.func @allocmem_dynarray_of_dynchar(%l: i32, %e : index) -> !fir.heap !fir.heap>> { %1 = fir.allocmem !fir.array>(%l : i32), %e, %e return %1 : !fir.heap>> @@ -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> { %a = fir.allocmem !fir.array<4x?x3x?x5xi32>, %0, %1 return %a : !fir.heap> @@ -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>> { %1 = fir.allocmem !fir.array<3x?x4x!fir.char<2,10>>, %e return %1 : !fir.heap>> @@ -331,7 +353,9 @@ func.func @allocmem_array_with_holes_char(%e: index) -> !fir.heap !fir.heap>> { %1 = fir.allocmem !fir.array<3x?x4x!fir.char<2,?>>(%arg0 : index), %arg1 return %1 : !fir.heap>> diff --git a/flang/test/Fir/arrexp.fir b/flang/test/Fir/arrexp.fir index 6c7f71f6f1f9c..e8ec8ac79e0c2 100644 --- a/flang/test/Fir/arrexp.fir +++ b/flang/test/Fir/arrexp.fir @@ -146,7 +146,9 @@ func.func @f6(%arg0: !fir.box>, %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.slice<1>) -> !fir.array %3 = fir.slice %c1, %c9, %c1 : (index, index, index) -> !fir.slice<1> diff --git a/flang/test/Fir/convert-to-llvm.fir b/flang/test/Fir/convert-to-llvm.fir index 0e2bfe48a807d..50a98466f0d4b 100644 --- a/flang/test/Fir/convert-to-llvm.fir +++ b/flang/test/Fir/convert-to-llvm.fir @@ -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: } diff --git a/flang/test/Lower/forall/character-1.f90 b/flang/test/Lower/forall/character-1.f90 index 1e4bb73350871..d1e12a8dbdfec 100644 --- a/flang/test/Lower/forall/character-1.f90 +++ b/flang/test/Lower/forall/character-1.f90 @@ -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]]) diff --git a/flang/test/Lower/zero-size2.f90 b/flang/test/Lower/zero-size2.f90 deleted file mode 100644 index efaf57a6ac59f..0000000000000 --- a/flang/test/Lower/zero-size2.f90 +++ /dev/null @@ -1,37 +0,0 @@ -! RUN: %flang_fc1 -emit-llvm -o - %s | FileCheck %s - -subroutine sub1() - integer, allocatable :: arr(:) - allocate(arr(0)) -! CHECK-LABEL: @sub1_ -! CHECK: %[[p:.*]] = call ptr @malloc(i64 1) -end - -subroutine sub2() - real, allocatable :: arr(:,:) - allocate(arr(10,0)) -! CHECK-LABEL: @sub2_ -! CHECK: %[[p:.*]] = call ptr @malloc(i64 1) -end - -subroutine sub3(i) - integer :: i - real, allocatable :: arr(:,:) - allocate(arr(i,0)) -! CHECK-LABEL: @sub3_ -! CHECK: %[[p:.*]] = call ptr @malloc(i64 1) -end - -subroutine sub4() - character(:), allocatable :: c - allocate(character(0)::c) -! CHECK-LABEL: @sub4_ -! CHECK: %[[p:.*]] = call ptr @malloc(i64 1) -end - -subroutine sub5() - character(:), allocatable :: c(:) - allocate(character(5)::c(0)) -! CHECK-LABEL: @sub5_ -! CHECK: %[[p:.*]] = call ptr @malloc(i64 1) -end