Skip to content

Commit 78ccffc

Browse files
authored
[flang] Add MALLOC and FREE intrinsics for Cray pointers (#110018)
MALLOC and FREE are extensions provided by gfortran, Intel Fortran and classic flang to allocate memory for Cray pointers. These are used in some legacy codes such as libexodus. All the above compilers accept using MALLOC and FREE with integers as well, despite that this will often signify a bug in user code. We should accept the same as the other compilers for compatibility.
1 parent 725eb6b commit 78ccffc

File tree

12 files changed

+245
-2
lines changed

12 files changed

+245
-2
lines changed

flang/docs/Intrinsics.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -700,7 +700,7 @@ IBCHNG, ISHA, ISHC, ISHL, IXOR
700700
IARG, IARGC, NARGS, NUMARG
701701
BADDRESS, IADDR
702702
CACHESIZE, EOF, FP_CLASS, INT_PTR_KIND, ISNAN, LOC
703-
MALLOC
703+
MALLOC, FREE
704704
```
705705

706706
### Library subroutine
@@ -765,7 +765,7 @@ This phase currently supports all the intrinsic procedures listed above but the
765765
| Coarray intrinsic functions | COSHAPE |
766766
| Object characteristic inquiry functions | ALLOCATED, ASSOCIATED, EXTENDS_TYPE_OF, IS_CONTIGUOUS, PRESENT, RANK, SAME_TYPE, STORAGE_SIZE |
767767
| Type inquiry intrinsic functions | BIT_SIZE, DIGITS, EPSILON, HUGE, KIND, MAXEXPONENT, MINEXPONENT, NEW_LINE, PRECISION, RADIX, RANGE, TINY|
768-
| Non-standard intrinsic functions | AND, OR, XOR, SHIFT, ZEXT, IZEXT, COSD, SIND, TAND, ACOSD, ASIND, ATAND, ATAN2D, COMPL, EQV, NEQV, INT8, JINT, JNINT, KNINT, QCMPLX, DREAL, DFLOAT, QEXT, QFLOAT, QREAL, DNUM, NUM, JNUM, KNUM, QNUM, RNUM, RAN, RANF, ILEN, SIZEOF, MCLOCK, SECNDS, COTAN, IBCHNG, ISHA, ISHC, ISHL, IXOR, IARG, IARGC, NARGS, GETPID, NUMARG, BADDRESS, IADDR, CACHESIZE, EOF, FP_CLASS, INT_PTR_KIND, ISNAN, MALLOC |
768+
| Non-standard intrinsic functions | AND, OR, XOR, SHIFT, ZEXT, IZEXT, COSD, SIND, TAND, ACOSD, ASIND, ATAND, ATAN2D, COMPL, EQV, NEQV, INT8, JINT, JNINT, KNINT, QCMPLX, DREAL, DFLOAT, QEXT, QFLOAT, QREAL, DNUM, NUM, JNUM, KNUM, QNUM, RNUM, RAN, RANF, ILEN, SIZEOF, MCLOCK, SECNDS, COTAN, IBCHNG, ISHA, ISHC, ISHL, IXOR, IARG, IARGC, NARGS, GETPID, NUMARG, BADDRESS, IADDR, CACHESIZE, EOF, FP_CLASS, INT_PTR_KIND, ISNAN, MALLOC, FREE |
769769
| Intrinsic subroutines |MVBITS (elemental), CPU_TIME, DATE_AND_TIME, EVENT_QUERY, EXECUTE_COMMAND_LINE, GET_COMMAND, GET_COMMAND_ARGUMENT, GET_ENVIRONMENT_VARIABLE, MOVE_ALLOC, RANDOM_INIT, RANDOM_NUMBER, RANDOM_SEED, SIGNAL, SLEEP, SYSTEM, SYSTEM_CLOCK |
770770
| Atomic intrinsic subroutines | ATOMIC_ADD |
771771
| Collective intrinsic subroutines | CO_REDUCE |

flang/include/flang/Optimizer/Builder/IntrinsicCall.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ struct IntrinsicLibrary {
249249
mlir::Value genFloor(mlir::Type, llvm::ArrayRef<mlir::Value>);
250250
mlir::Value genFraction(mlir::Type resultType,
251251
mlir::ArrayRef<mlir::Value> args);
252+
void genFree(mlir::ArrayRef<fir::ExtendedValue> args);
252253
fir::ExtendedValue genGetCwd(std::optional<mlir::Type> resultType,
253254
llvm::ArrayRef<fir::ExtendedValue> args);
254255
void genGetCommand(mlir::ArrayRef<fir::ExtendedValue> args);
@@ -315,6 +316,7 @@ struct IntrinsicLibrary {
315316
fir::ExtendedValue genLen(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
316317
fir::ExtendedValue genLenTrim(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
317318
fir::ExtendedValue genLoc(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
319+
mlir::Value genMalloc(mlir::Type, llvm::ArrayRef<mlir::Value>);
318320
template <typename Shift>
319321
mlir::Value genMask(mlir::Type, llvm::ArrayRef<mlir::Value>);
320322
fir::ExtendedValue genMatmul(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);

flang/include/flang/Optimizer/Builder/Runtime/Intrinsics.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ void genDateAndTime(fir::FirOpBuilder &, mlir::Location,
4747
void genEtime(fir::FirOpBuilder &builder, mlir::Location loc,
4848
mlir::Value values, mlir::Value time);
4949

50+
void genFree(fir::FirOpBuilder &builder, mlir::Location loc, mlir::Value ptr);
51+
mlir::Value genMalloc(fir::FirOpBuilder &builder, mlir::Location loc,
52+
mlir::Value size);
53+
5054
void genRandomInit(fir::FirOpBuilder &, mlir::Location, mlir::Value repeatable,
5155
mlir::Value imageDistinct);
5256
void genRandomNumber(fir::FirOpBuilder &, mlir::Location, mlir::Value harvest);

flang/include/flang/Runtime/extensions.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ void FORTRAN_PROCEDURE_NAME(flush)(const int &unit);
2828
// GNU extension subroutine FDATE
2929
void FORTRAN_PROCEDURE_NAME(fdate)(char *string, std::int64_t length);
3030

31+
void RTNAME(Free)(std::intptr_t ptr);
32+
3133
// GNU Fortran 77 compatibility function IARGC.
3234
std::int32_t FORTRAN_PROCEDURE_NAME(iargc)();
3335

@@ -38,6 +40,8 @@ void FORTRAN_PROCEDURE_NAME(getarg)(
3840
// GNU extension subroutine GETLOG(C).
3941
void FORTRAN_PROCEDURE_NAME(getlog)(char *name, std::int64_t length);
4042

43+
std::intptr_t RTNAME(Malloc)(std::size_t size);
44+
4145
// GNU extension function STATUS = SIGNAL(number, handler)
4246
std::int64_t RTNAME(Signal)(std::int64_t number, void (*handler)(int));
4347

flang/lib/Evaluate/intrinsics.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,7 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
620620
{"log10", {{"x", SameReal}}, SameReal},
621621
{"logical", {{"l", AnyLogical}, DefaultingKIND}, KINDLogical},
622622
{"log_gamma", {{"x", SameReal}}, SameReal},
623+
{"malloc", {{"size", AnyInt}}, SubscriptInt},
623624
{"matmul",
624625
{{"matrix_a", AnyLogical, Rank::vector},
625626
{"matrix_b", AnyLogical, Rank::matrix}},
@@ -1409,6 +1410,7 @@ static const IntrinsicInterface intrinsicSubroutine[]{
14091410
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
14101411
{"exit", {{"status", DefaultInt, Rank::scalar, Optionality::optional}}, {},
14111412
Rank::elemental, IntrinsicClass::impureSubroutine},
1413+
{"free", {{"ptr", Addressable}}, {}},
14121414
{"get_command",
14131415
{{"command", DefaultChar, Rank::scalar, Optionality::optional,
14141416
common::Intent::Out},

flang/lib/Optimizer/Builder/IntrinsicCall.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,7 @@ static constexpr IntrinsicHandler handlers[]{
265265
/*isElemental=*/false},
266266
{"floor", &I::genFloor},
267267
{"fraction", &I::genFraction},
268+
{"free", &I::genFree},
268269
{"get_command",
269270
&I::genGetCommand,
270271
{{{"command", asBox, handleDynamicOptional},
@@ -436,6 +437,7 @@ static constexpr IntrinsicHandler handlers[]{
436437
{"lle", &I::genCharacterCompare<mlir::arith::CmpIPredicate::sle>},
437438
{"llt", &I::genCharacterCompare<mlir::arith::CmpIPredicate::slt>},
438439
{"loc", &I::genLoc, {{{"x", asBox}}}, /*isElemental=*/false},
440+
{"malloc", &I::genMalloc},
439441
{"maskl", &I::genMask<mlir::arith::ShLIOp>},
440442
{"maskr", &I::genMask<mlir::arith::ShRUIOp>},
441443
{"matmul",
@@ -3581,6 +3583,12 @@ mlir::Value IntrinsicLibrary::genFraction(mlir::Type resultType,
35813583
fir::runtime::genFraction(builder, loc, fir::getBase(args[0])));
35823584
}
35833585

3586+
void IntrinsicLibrary::genFree(llvm::ArrayRef<fir::ExtendedValue> args) {
3587+
assert(args.size() == 1);
3588+
3589+
fir::runtime::genFree(builder, loc, fir::getBase(args[0]));
3590+
}
3591+
35843592
// GETCWD
35853593
fir::ExtendedValue
35863594
IntrinsicLibrary::genGetCwd(std::optional<mlir::Type> resultType,
@@ -5307,6 +5315,13 @@ IntrinsicLibrary::genLoc(mlir::Type resultType,
53075315
.getResults()[0];
53085316
}
53095317

5318+
mlir::Value IntrinsicLibrary::genMalloc(mlir::Type resultType,
5319+
llvm::ArrayRef<mlir::Value> args) {
5320+
assert(args.size() == 1);
5321+
return builder.createConvert(loc, resultType,
5322+
fir::runtime::genMalloc(builder, loc, args[0]));
5323+
}
5324+
53105325
// MASKL, MASKR
53115326
template <typename Shift>
53125327
mlir::Value IntrinsicLibrary::genMask(mlir::Type resultType,

flang/lib/Optimizer/Builder/Runtime/Intrinsics.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,26 @@ void fir::runtime::genEtime(fir::FirOpBuilder &builder, mlir::Location loc,
120120
builder.create<fir::CallOp>(loc, runtimeFunc, args);
121121
}
122122

123+
void fir::runtime::genFree(fir::FirOpBuilder &builder, mlir::Location loc,
124+
mlir::Value ptr) {
125+
auto runtimeFunc = fir::runtime::getRuntimeFunc<mkRTKey(Free)>(loc, builder);
126+
mlir::Type intPtrTy = builder.getIntPtrType();
127+
128+
builder.create<fir::CallOp>(loc, runtimeFunc,
129+
builder.createConvert(loc, intPtrTy, ptr));
130+
}
131+
132+
mlir::Value fir::runtime::genMalloc(fir::FirOpBuilder &builder,
133+
mlir::Location loc, mlir::Value size) {
134+
auto runtimeFunc =
135+
fir::runtime::getRuntimeFunc<mkRTKey(Malloc)>(loc, builder);
136+
auto argTy = runtimeFunc.getArgumentTypes()[0];
137+
return builder
138+
.create<fir::CallOp>(loc, runtimeFunc,
139+
builder.createConvert(loc, argTy, size))
140+
.getResult(0);
141+
}
142+
123143
void fir::runtime::genRandomInit(fir::FirOpBuilder &builder, mlir::Location loc,
124144
mlir::Value repeatable,
125145
mlir::Value imageDistinct) {

flang/lib/Semantics/check-call.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1600,6 +1600,18 @@ static void CheckMaxMin(const characteristics::Procedure &proc,
16001600
}
16011601
}
16021602

1603+
static void CheckFree(evaluate::ActualArguments &arguments,
1604+
parser::ContextualMessages &messages) {
1605+
if (arguments.size() != 1) {
1606+
messages.Say("FREE expects a single argument"_err_en_US);
1607+
}
1608+
auto arg = arguments[0];
1609+
if (const Symbol * symbol{evaluate::UnwrapWholeSymbolDataRef(arg)};
1610+
!symbol || !symbol->test(Symbol::Flag::CrayPointer)) {
1611+
messages.Say("FREE should only be used with Cray pointers"_warn_en_US);
1612+
}
1613+
}
1614+
16031615
// MOVE_ALLOC (F'2023 16.9.147)
16041616
static void CheckMove_Alloc(evaluate::ActualArguments &arguments,
16051617
parser::ContextualMessages &messages) {
@@ -1885,6 +1897,8 @@ static void CheckSpecificIntrinsic(const characteristics::Procedure &proc,
18851897
CheckReduce(arguments, context.foldingContext());
18861898
} else if (intrinsic.name == "transfer") {
18871899
CheckTransfer(arguments, context, scope);
1900+
} else if (intrinsic.name == "free") {
1901+
CheckFree(arguments, context.foldingContext().messages());
18881902
}
18891903
}
18901904

flang/runtime/extensions.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ void FORTRAN_PROCEDURE_NAME(fdate)(char *arg, std::int64_t length) {
9696
CopyAndPad(arg, str, length, 24);
9797
}
9898

99+
std::intptr_t RTNAME(Malloc)(std::size_t size) {
100+
return reinterpret_cast<std::intptr_t>(std::malloc(size));
101+
}
102+
99103
// RESULT = IARGC()
100104
std::int32_t FORTRAN_PROCEDURE_NAME(iargc)() { return RTNAME(ArgumentCount)(); }
101105

@@ -124,6 +128,10 @@ void FORTRAN_PROCEDURE_NAME(getlog)(char *arg, std::int64_t length) {
124128
#endif
125129
}
126130

131+
void RTNAME(Free)(std::intptr_t ptr) {
132+
std::free(reinterpret_cast<void *>(ptr));
133+
}
134+
127135
std::int64_t RTNAME(Signal)(std::int64_t number, void (*handler)(int)) {
128136
// using auto for portability:
129137
// on Windows, this is a void *

flang/test/Lower/Intrinsics/free.f90

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
! RUN: bbc -emit-hlfir %s -o - | FileCheck %s
2+
! RUN: %flang_fc1 -emit-hlfir %s -o - | FileCheck %s
3+
4+
! CHECK-LABEL: func.func @_QPfree_ptr() {
5+
subroutine free_ptr()
6+
integer :: x
7+
pointer (ptr_x, x)
8+
! CHECK: %[[X:.*]] = fir.alloca !fir.box<!fir.ptr<i32>>
9+
! CHECK: %[[X_PTR:.*]] = fir.alloca i64 {bindc_name = "ptr_x", uniq_name = "_QFfree_ptrEptr_x"}
10+
! CHECK: %[[X_PTR_DECL:.*]]:2 = hlfir.declare %[[X_PTR]] {uniq_name = "_QFfree_ptrEptr_x"} : (!fir.ref<i64>) -> (!fir.ref<i64>, !fir.ref<i64>)
11+
! CHECK: %[[X_DECL:.*]]:2 = hlfir.declare %[[X]] {fortran_attrs = #fir.var_attrs<pointer>, uniq_name = "_QFfree_ptrEx"} : (!fir.ref<!fir.box<!fir.ptr<i32>>>) -> (!fir.ref<!fir.box<!fir.ptr<i32>>>, !fir.ref<!fir.box<!fir.ptr<i32>>>)
12+
! CHECK: %[[X_LD:.*]] = fir.load %[[X_PTR_DECL]]#0 : !fir.ref<i64>
13+
! CHECK: %[[VOID:.*]] = fir.call @_FortranAFree(%[[X_LD]]) fastmath<contract> : (i64) -> none
14+
! CHECK: return
15+
call free(ptr_x)
16+
end subroutine
17+
18+
! gfortran allows free to be used on integers, so we accept it with a warning.
19+
20+
! CHECK-LABEL: func.func @_QPfree_i8() {
21+
subroutine free_i8
22+
integer (kind=1) :: x
23+
! CHECK: %[[X:.*]] = fir.alloca i8 {bindc_name = "x", uniq_name = "_QFfree_i8Ex"}
24+
! CHECK: %[[X_DECL:.*]]:2 = hlfir.declare %[[X]] {uniq_name = "_QFfree_i8Ex"} : (!fir.ref<i8>) -> (!fir.ref<i8>, !fir.ref<i8>)
25+
! CHECK: %[[X_LD:.*]] = fir.load %[[X_DECL]]#0 : !fir.ref<i8>
26+
! CHECK: %[[X_I64:.*]] = fir.convert %[[X_LD]] : (i8) -> i64
27+
! CHECK: %[[VOID:.*]] = fir.call @_FortranAFree(%[[X_I64]]) fastmath<contract> : (i64) -> none
28+
! CHECK: return
29+
call free(x)
30+
end subroutine
31+
32+
33+
! CHECK-LABEL: func.func @_QPfree_i16() {
34+
subroutine free_i16
35+
integer (kind=2) :: x
36+
! CHECK: %[[X:.*]] = fir.alloca i16 {bindc_name = "x", uniq_name = "_QFfree_i16Ex"}
37+
! CHECK: %[[X_DECL:.*]]:2 = hlfir.declare %[[X]] {uniq_name = "_QFfree_i16Ex"} : (!fir.ref<i16>) -> (!fir.ref<i16>, !fir.ref<i16>)
38+
! CHECK: %[[X_LD:.*]] = fir.load %[[X_DECL]]#0 : !fir.ref<i16>
39+
! CHECK: %[[X_I64:.*]] = fir.convert %[[X_LD]] : (i16) -> i64
40+
! CHECK: %[[VOID:.*]] = fir.call @_FortranAFree(%[[X_I64]]) fastmath<contract> : (i64) -> none
41+
! CHECK: return
42+
call free(x)
43+
end subroutine
44+
45+
! CHECK-LABEL: func.func @_QPfree_i32() {
46+
subroutine free_i32
47+
integer (kind=4) :: x
48+
! CHECK: %[[X:.*]] = fir.alloca i32 {bindc_name = "x", uniq_name = "_QFfree_i32Ex"}
49+
! CHECK: %[[X_DECL:.*]]:2 = hlfir.declare %[[X]] {uniq_name = "_QFfree_i32Ex"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
50+
! CHECK: %[[X_LD:.*]] = fir.load %[[X_DECL]]#0 : !fir.ref<i32>
51+
! CHECK: %[[X_I64:.*]] = fir.convert %[[X_LD]] : (i32) -> i64
52+
! CHECK: %[[VOID:.*]] = fir.call @_FortranAFree(%[[X_I64]]) fastmath<contract> : (i64) -> none
53+
! CHECK: return
54+
call free(x)
55+
end subroutine
56+
57+
! CHECK-LABEL: func.func @_QPfree_i64() {
58+
subroutine free_i64
59+
integer (kind=8) :: x
60+
! CHECK: %[[X:.*]] = fir.alloca i64 {bindc_name = "x", uniq_name = "_QFfree_i64Ex"}
61+
! CHECK: %[[X_DECL:.*]]:2 = hlfir.declare %[[X]] {uniq_name = "_QFfree_i64Ex"} : (!fir.ref<i64>) -> (!fir.ref<i64>, !fir.ref<i64>)
62+
! CHECK: %[[X_LD:.*]] = fir.load %[[X_DECL]]#0 : !fir.ref<i64>
63+
! CHECK: %[[VOID:.*]] = fir.call @_FortranAFree(%[[X_LD]]) fastmath<contract> : (i64) -> none
64+
! CHECK: return
65+
call free(x)
66+
end subroutine

0 commit comments

Comments
 (0)