Skip to content

Commit afa52de

Browse files
committed
[flang][Runtime] Add SIGNAL intrinisic (#79337)
The intrinsic is defined as a GNU extension here: https://gcc.gnu.org/onlinedocs/gfortran/SIGNAL.html And as an IBM extension here: https://www.ibm.com/docs/en/xffbg/121.141?topic=procedures-signali-proc-extension The IBM version provides a compatible subset of the functionality offered by the GNU version. This patch supports most of the GNU features, but not calling SIGNAL as a function. We don't currently support intrinsics being both subroutines AND functions and this changed seemed too large to be justified by a non-standard intrinsic. I cannot point to open source code Fortran using this intrinsic. This is needed for a proprietary code base.
1 parent 73874f7 commit afa52de

File tree

10 files changed

+185
-2
lines changed

10 files changed

+185
-2
lines changed

flang/docs/Intrinsics.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -757,7 +757,7 @@ This phase currently supports all the intrinsic procedures listed above but the
757757
| Object characteristic inquiry functions | ALLOCATED, ASSOCIATED, EXTENDS_TYPE_OF, IS_CONTIGUOUS, PRESENT, RANK, SAME_TYPE, STORAGE_SIZE |
758758
| Type inquiry intrinsic functions | BIT_SIZE, DIGITS, EPSILON, HUGE, KIND, MAXEXPONENT, MINEXPONENT, NEW_LINE, PRECISION, RADIX, RANGE, TINY|
759759
| 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 |
760-
| 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, SLEEP, SYSTEM_CLOCK |
760+
| 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_CLOCK |
761761
| Atomic intrinsic subroutines | ATOMIC_ADD |
762762
| Collective intrinsic subroutines | CO_REDUCE |
763763
| Library subroutines | FDATE, GETLOG |

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,7 @@ struct IntrinsicLibrary {
339339
fir::ExtendedValue genStorageSize(mlir::Type,
340340
llvm::ArrayRef<fir::ExtendedValue>);
341341
fir::ExtendedValue genSum(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
342+
void genSignalSubroutine(llvm::ArrayRef<fir::ExtendedValue>);
342343
void genSleep(llvm::ArrayRef<fir::ExtendedValue>);
343344
void genSystemClock(llvm::ArrayRef<fir::ExtendedValue>);
344345
mlir::Value genTand(mlir::Type, llvm::ArrayRef<mlir::Value>);

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
namespace mlir {
2222
class Location;
23+
class Type;
2324
class Value;
2425
} // namespace mlir
2526

@@ -65,9 +66,16 @@ void genTransferSize(fir::FirOpBuilder &builder, mlir::Location loc,
6566
void genSystemClock(fir::FirOpBuilder &, mlir::Location, mlir::Value count,
6667
mlir::Value rate, mlir::Value max);
6768

69+
// generate signal runtime call
70+
// CALL SIGNAL(NUMBER, HANDLER [, STATUS])
71+
// status can be {} or a value. It may also be dynamically absent
72+
void genSignal(fir::FirOpBuilder &builder, mlir::Location loc,
73+
mlir::Value number, mlir::Value handler, mlir::Value status);
74+
6875
/// generate sleep runtime call
6976
void genSleep(fir::FirOpBuilder &builder, mlir::Location loc,
7077
mlir::Value seconds);
78+
7179
} // namespace runtime
7280
} // namespace fir
7381

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,14 @@ constexpr TypeBuilderFunc getModel<void *>() {
137137
};
138138
}
139139
template <>
140+
constexpr TypeBuilderFunc getModel<void (*)(int)>() {
141+
return [](mlir::MLIRContext *context) -> mlir::Type {
142+
return fir::LLVMPointerType::get(
143+
context,
144+
mlir::FunctionType::get(context, /*inputs=*/{}, /*results*/ {}));
145+
};
146+
}
147+
template <>
140148
constexpr TypeBuilderFunc getModel<void **>() {
141149
return [](mlir::MLIRContext *context) -> mlir::Type {
142150
return fir::ReferenceType::get(

flang/include/flang/Runtime/extensions.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#define FORTRAN_PROCEDURE_NAME(name) name##_
1818

19+
#include "flang/Runtime/entry-names.h"
1920
#include <cstddef>
2021
#include <cstdint>
2122

@@ -37,6 +38,9 @@ void FORTRAN_PROCEDURE_NAME(getarg)(
3738
// GNU extension subroutine GETLOG(C).
3839
void FORTRAN_PROCEDURE_NAME(getlog)(char *name, std::int64_t length);
3940

41+
// GNU extension function STATUS = SIGNAL(number, handler)
42+
std::int64_t RTNAME(Signal)(std::int64_t number, void (*handler)(int));
43+
4044
// GNU extension subroutine SLEEP(SECONDS)
4145
void RTNAME(Sleep)(std::int64_t seconds);
4246

flang/lib/Evaluate/intrinsics.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1401,6 +1401,15 @@ static const IntrinsicInterface intrinsicSubroutine[]{
14011401
{"count_max", AnyInt, Rank::scalar, Optionality::optional,
14021402
common::Intent::Out}},
14031403
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
1404+
{"signal",
1405+
{{"number", AnyInt, Rank::scalar, Optionality::required,
1406+
common::Intent::In},
1407+
// note: any pointer also accepts AnyInt
1408+
{"handler", AnyPointer, Rank::scalar, Optionality::required,
1409+
common::Intent::In},
1410+
{"status", AnyInt, Rank::scalar, Optionality::optional,
1411+
common::Intent::Out}},
1412+
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
14041413
{"sleep",
14051414
{{"seconds", AnyInt, Rank::scalar, Optionality::required,
14061415
common::Intent::In}},
@@ -1422,9 +1431,12 @@ static DynamicType GetBuiltinDerivedType(
14221431
auto iter{
14231432
builtinsScope->find(semantics::SourceName{which, std::strlen(which)})};
14241433
if (iter == builtinsScope->cend()) {
1434+
// keep the string all together
1435+
// clang-format off
14251436
common::die(
14261437
"INTERNAL: The __fortran_builtins module does not define the type '%s'",
14271438
which);
1439+
// clang-format on
14281440
}
14291441
const semantics::Symbol &symbol{*iter->second};
14301442
const semantics::Scope &scope{DEREF(symbol.scope())};

flang/lib/Optimizer/Builder/IntrinsicCall.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,10 @@ static constexpr IntrinsicHandler handlers[]{
550550
{"shiftl", &I::genShift<mlir::arith::ShLIOp>},
551551
{"shiftr", &I::genShift<mlir::arith::ShRUIOp>},
552552
{"sign", &I::genSign},
553+
{"signal",
554+
&I::genSignalSubroutine,
555+
{{{"number", asValue}, {"handler", asAddr}, {"status", asAddr}}},
556+
/*isElemental=*/false},
553557
{"size",
554558
&I::genSize,
555559
{{{"array", asBox},
@@ -5579,6 +5583,18 @@ mlir::Value IntrinsicLibrary::genShiftA(mlir::Type resultType,
55795583
shifted);
55805584
}
55815585

5586+
// SIGNAL
5587+
void IntrinsicLibrary::genSignalSubroutine(
5588+
llvm::ArrayRef<fir::ExtendedValue> args) {
5589+
assert(args.size() == 2 || args.size() == 3);
5590+
mlir::Value number = fir::getBase(args[0]);
5591+
mlir::Value handler = fir::getBase(args[1]);
5592+
mlir::Value status;
5593+
if (args.size() == 3)
5594+
status = fir::getBase(args[2]);
5595+
fir::runtime::genSignal(builder, loc, number, handler, status);
5596+
}
5597+
55825598
// SIGN
55835599
mlir::Value IntrinsicLibrary::genSign(mlir::Type resultType,
55845600
llvm::ArrayRef<mlir::Value> args) {

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

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "flang/Semantics/tools.h"
2222
#include "llvm/Support/Debug.h"
2323
#include <optional>
24+
#include <signal.h>
2425

2526
#define DEBUG_TYPE "flang-lower-runtime"
2627

@@ -237,11 +238,55 @@ void fir::runtime::genSystemClock(fir::FirOpBuilder &builder,
237238
makeCall(getRuntimeFunc<mkRTKey(SystemClockCountMax)>(loc, builder), max);
238239
}
239240

241+
// CALL SIGNAL(NUMBER, HANDLER [, STATUS])
242+
// The definition of the SIGNAL intrinsic allows HANDLER to be a function
243+
// pointer or an integer. STATUS can be dynamically optional
244+
void fir::runtime::genSignal(fir::FirOpBuilder &builder, mlir::Location loc,
245+
mlir::Value number, mlir::Value handler,
246+
mlir::Value status) {
247+
assert(mlir::isa<mlir::IntegerType>(number.getType()));
248+
mlir::Type int64 = builder.getIntegerType(64);
249+
number = builder.create<fir::ConvertOp>(loc, int64, number);
250+
251+
mlir::Type handlerUnwrappedTy = fir::unwrapRefType(handler.getType());
252+
if (mlir::isa_and_nonnull<mlir::IntegerType>(handlerUnwrappedTy)) {
253+
// pass the integer as a function pointer like one would to signal(2)
254+
handler = builder.create<fir::LoadOp>(loc, handler);
255+
mlir::Type fnPtrTy = fir::LLVMPointerType::get(
256+
mlir::FunctionType::get(handler.getContext(), {}, {}));
257+
handler = builder.create<fir::ConvertOp>(loc, fnPtrTy, handler);
258+
} else {
259+
assert(mlir::isa<fir::BoxProcType>(handler.getType()));
260+
handler = builder.create<fir::BoxAddrOp>(loc, handler);
261+
}
262+
263+
mlir::func::FuncOp func{
264+
fir::runtime::getRuntimeFunc<mkRTKey(Signal)>(loc, builder)};
265+
mlir::Value stat =
266+
builder.create<fir::CallOp>(loc, func, mlir::ValueRange{number, handler})
267+
->getResult(0);
268+
269+
// return status code via status argument (if present)
270+
if (status) {
271+
assert(mlir::isa<mlir::IntegerType>(fir::unwrapRefType(status.getType())));
272+
// status might be dynamically optional, so test if it is present
273+
mlir::Value isPresent =
274+
builder.create<IsPresentOp>(loc, builder.getI1Type(), status);
275+
builder.genIfOp(loc, /*results=*/{}, isPresent, /*withElseRegion=*/false)
276+
.genThen([&]() {
277+
stat = builder.create<fir::ConvertOp>(
278+
loc, fir::unwrapRefType(status.getType()), stat);
279+
builder.create<fir::StoreOp>(loc, stat, status);
280+
})
281+
.end();
282+
}
283+
}
284+
240285
void fir::runtime::genSleep(fir::FirOpBuilder &builder, mlir::Location loc,
241286
mlir::Value seconds) {
242287
mlir::Type int64 = builder.getIntegerType(64);
243288
seconds = builder.create<fir::ConvertOp>(loc, int64, seconds);
244289
mlir::func::FuncOp func{
245290
fir::runtime::getRuntimeFunc<mkRTKey(Sleep)>(loc, builder)};
246291
builder.create<fir::CallOp>(loc, func, seconds);
247-
}
292+
}

flang/runtime/extensions.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "flang/Runtime/io-api.h"
1919
#include <chrono>
2020
#include <ctime>
21+
#include <signal.h>
2122
#include <thread>
2223

2324
#ifdef _WIN32
@@ -116,6 +117,17 @@ void FORTRAN_PROCEDURE_NAME(getlog)(char *arg, std::int64_t length) {
116117
#endif
117118
}
118119

120+
std::int64_t RTNAME(Signal)(std::int64_t number, void (*handler)(int)) {
121+
// using auto for portability:
122+
// on Windows, this is a void *
123+
// on POSIX, this has the same type as handler
124+
auto result = signal(number, handler);
125+
126+
// GNU defines the intrinsic as returning an integer, not a pointer. So we
127+
// have to reinterpret_cast
128+
return static_cast<int64_t>(reinterpret_cast<std::uintptr_t>(result));
129+
}
130+
119131
// CALL SLEEP(SECONDS)
120132
void RTNAME(Sleep)(std::int64_t seconds) {
121133
// ensure that conversion to unsigned makes sense,
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
! test lowering of the SIGNAL intrinsic subroutine
2+
! RUN: bbc -emit-hlfir -o - %s | FileCheck %s
3+
4+
module m
5+
contains
6+
! CHECK-LABEL: func.func @handler(
7+
! CHECK-SAME: %[[VAL_0:.*]]: i32 {fir.bindc_name = "signum"}) attributes {fir.bindc_name = "handler"} {
8+
subroutine handler(signum) bind(C)
9+
use iso_c_binding, only: c_int
10+
integer(c_int), value :: signum
11+
end subroutine
12+
13+
! CHECK-LABEL: func.func @_QMmPsetup_signals(
14+
! CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<i32> {fir.bindc_name = "optional_status", fir.optional}) {
15+
subroutine setup_signals(optional_status)
16+
! not portable accross systems
17+
integer, parameter :: SIGFPE = 8
18+
integer, parameter :: SIGUSR1 = 10
19+
integer, parameter :: SIGUSR2 = 12
20+
integer, parameter :: SIGPIPE = 13
21+
integer, parameter :: SIG_IGN = 1
22+
integer :: stat = 0
23+
integer, optional, intent(out) :: optional_status
24+
25+
! CHECK: %[[VAL_1:.*]] = fir.alloca i32
26+
! CHECK: %[[VAL_2:.*]]:2 = hlfir.declare %[[VAL_0]] {fortran_attrs = #fir.var_attrs<intent_out, optional>, uniq_name = "_QMmFsetup_signalsEoptional_status"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
27+
! CHECK: %[[VAL_14:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = "_QMmFsetup_signalsEstat"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
28+
29+
call signal(SIGFPE, handler)
30+
! CHECK: %[[VAL_15:.*]] = arith.constant 8 : i32
31+
! CHECK: %[[VAL_16:.*]] = fir.address_of(@handler) : (i32) -> ()
32+
! CHECK: %[[VAL_17:.*]] = fir.emboxproc %[[VAL_16]] : ((i32) -> ()) -> !fir.boxproc<() -> ()>
33+
! CHECK: %[[VAL_18:.*]] = fir.convert %[[VAL_15]] : (i32) -> i64
34+
! CHECK: %[[VAL_19:.*]] = fir.box_addr %[[VAL_17]] : (!fir.boxproc<() -> ()>) -> (() -> ())
35+
! CHECK: %[[VAL_20:.*]] = fir.call @_FortranASignal(%[[VAL_18]], %[[VAL_19]]) fastmath<contract> : (i64, () -> ()) -> i64
36+
37+
call signal(SIGUSR1, handler, stat)
38+
! CHECK: %[[VAL_21:.*]] = arith.constant 10 : i32
39+
! CHECK: %[[VAL_22:.*]] = fir.address_of(@handler) : (i32) -> ()
40+
! CHECK: %[[VAL_23:.*]] = fir.emboxproc %[[VAL_22]] : ((i32) -> ()) -> !fir.boxproc<() -> ()>
41+
! CHECK: %[[VAL_24:.*]] = fir.convert %[[VAL_21]] : (i32) -> i64
42+
! CHECK: %[[VAL_25:.*]] = fir.box_addr %[[VAL_23]] : (!fir.boxproc<() -> ()>) -> (() -> ())
43+
! CHECK: %[[VAL_26:.*]] = fir.call @_FortranASignal(%[[VAL_24]], %[[VAL_25]]) fastmath<contract> : (i64, () -> ()) -> i64
44+
! CHECK: %[[VAL_27:.*]] = fir.is_present %[[VAL_14]]#1 : (!fir.ref<i32>) -> i1
45+
! CHECK: fir.if %[[VAL_27]] {
46+
! CHECK: %[[VAL_28:.*]] = fir.convert %[[VAL_26]] : (i64) -> i32
47+
! CHECK: fir.store %[[VAL_28]] to %[[VAL_14]]#1 : !fir.ref<i32>
48+
! CHECK: }
49+
50+
call signal(SIGUSR2, SIG_IGN, stat)
51+
! CHECK: %[[VAL_29:.*]] = arith.constant 12 : i32
52+
! CHECK: %[[VAL_30:.*]] = arith.constant 1 : i32
53+
! CHECK: fir.store %[[VAL_30]] to %[[VAL_1]] : !fir.ref<i32>
54+
! CHECK: %[[VAL_31:.*]] = fir.convert %[[VAL_29]] : (i32) -> i64
55+
! CHECK: %[[VAL_32:.*]] = fir.load %[[VAL_1]] : !fir.ref<i32>
56+
! CHECK: %[[VAL_33:.*]] = fir.convert %[[VAL_32]] : (i32) -> !fir.llvm_ptr<() -> ()>
57+
! CHECK: %[[VAL_34:.*]] = fir.call @_FortranASignal(%[[VAL_31]], %[[VAL_33]]) fastmath<contract> : (i64, !fir.llvm_ptr<() -> ()>) -> i64
58+
! CHECK: %[[VAL_35:.*]] = fir.is_present %[[VAL_14]]#1 : (!fir.ref<i32>) -> i1
59+
! CHECK: fir.if %[[VAL_35]] {
60+
! CHECK: %[[VAL_36:.*]] = fir.convert %[[VAL_34]] : (i64) -> i32
61+
! CHECK: fir.store %[[VAL_36]] to %[[VAL_14]]#1 : !fir.ref<i32>
62+
! CHECK: }
63+
64+
call signal(SIGPIPE, handler, optional_status)
65+
! CHECK: %[[VAL_37:.*]] = arith.constant 13 : i32
66+
! CHECK: %[[VAL_38:.*]] = fir.address_of(@handler) : (i32) -> ()
67+
! CHECK: %[[VAL_39:.*]] = fir.emboxproc %[[VAL_38]] : ((i32) -> ()) -> !fir.boxproc<() -> ()>
68+
! CHECK: %[[VAL_40:.*]] = fir.convert %[[VAL_37]] : (i32) -> i64
69+
! CHECK: %[[VAL_41:.*]] = fir.box_addr %[[VAL_39]] : (!fir.boxproc<() -> ()>) -> (() -> ())
70+
! CHECK: %[[VAL_42:.*]] = fir.call @_FortranASignal(%[[VAL_40]], %[[VAL_41]]) fastmath<contract> : (i64, () -> ()) -> i64
71+
! CHECK: %[[VAL_43:.*]] = fir.is_present %[[VAL_2]]#1 : (!fir.ref<i32>) -> i1
72+
! CHECK: fir.if %[[VAL_43]] {
73+
! CHECK: %[[VAL_44:.*]] = fir.convert %[[VAL_42]] : (i64) -> i32
74+
! CHECK: fir.store %[[VAL_44]] to %[[VAL_2]]#1 : !fir.ref<i32>
75+
! CHECK: }
76+
end subroutine
77+
end module

0 commit comments

Comments
 (0)