Skip to content

[flang] add SYSTEM runtime and lowering intrinsics support #74309

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 23 commits into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
14f8c3e
Add SYSTEM runtime and lowering intrinsic support
yiwu0b11 Dec 4, 2023
be27d36
test fixes
Dec 4, 2023
d821529
remove unnecessary clang-format change
yiwu0b11 Dec 4, 2023
9a73cb8
required parameter use reference not pointer, ensure command is null-…
yiwu0b11 Dec 7, 2023
da3f993
remove redundant clang-format and minor fixes
yiwu0b11 Dec 12, 2023
2dbeb81
tests fixes: avoid hard-coded SSA
yiwu0b11 Dec 14, 2023
27aa276
move EnsureNullTerminated to tools.h, capitalized and free memory aft…
yiwu0b11 Dec 14, 2023
cb88f6e
declare in tools.g, define in tools.cpp
yiwu0b11 Dec 14, 2023
fb15f83
free memoryin the end
yiwu0b11 Dec 20, 2023
d147dbb
memory deallocation fixes, and use std::size_t
yiwu0b11 Dec 29, 2023
a273460
Merge remote-tracking branch 'yi/main' into yi_dev_system
yiwu0b11 Jan 11, 2024
4336e53
Merge remote-tracking branch 'yi/main' into yi_dev_system
yiwu0b11 Jan 16, 2024
cce1664
refactor: move SYSTEM from command.cpp to execute.cpp
yiwu0b11 Jan 16, 2024
31c3a42
clang format and change exitstat type from AnyInt to DefaultInt to ma…
yiwu0b11 Jan 16, 2024
25c26f9
Windows fixes
yiwu0b11 Jan 16, 2024
836cd28
reuse ExecuteCommandLine runtime library and lowering
yiwu0b11 Jan 16, 2024
1b908ad
add tests
yiwu0b11 Jan 17, 2024
da6089d
remove debug leftover and test fixes based on #78302
yiwu0b11 Jan 17, 2024
ceac5e9
more code clean up
yiwu0b11 Jan 17, 2024
a157933
test fixes
yiwu0b11 Jan 17, 2024
762982b
Merge remote-tracking branch 'yi/main' into yi_dev_system
yiwu0b11 Jan 19, 2024
97c7bbf
Merge remote-tracking branch 'yi/main' into yi_dev_system
yiwu0b11 Jan 26, 2024
e18ac12
change inout to out and use getI16Type
yiwu0b11 Jan 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion flang/docs/Intrinsics.md
Original file line number Diff line number Diff line change
Expand Up @@ -757,7 +757,7 @@ This phase currently supports all the intrinsic procedures listed above but the
| Object characteristic inquiry functions | ALLOCATED, ASSOCIATED, EXTENDS_TYPE_OF, IS_CONTIGUOUS, PRESENT, RANK, SAME_TYPE, STORAGE_SIZE |
| Type inquiry intrinsic functions | BIT_SIZE, DIGITS, EPSILON, HUGE, KIND, MAXEXPONENT, MINEXPONENT, NEW_LINE, PRECISION, RADIX, RANGE, TINY|
| 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 |
| 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 |
| 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 |
| Atomic intrinsic subroutines | ATOMIC_ADD |
| Collective intrinsic subroutines | CO_REDUCE |
| Library subroutines | FDATE, GETLOG |
Expand Down
1 change: 1 addition & 0 deletions flang/include/flang/Optimizer/Builder/IntrinsicCall.h
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@ struct IntrinsicLibrary {
fir::ExtendedValue genSum(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
void genSignalSubroutine(llvm::ArrayRef<fir::ExtendedValue>);
void genSleep(llvm::ArrayRef<fir::ExtendedValue>);
void genSystem(mlir::ArrayRef<fir::ExtendedValue> args);
void genSystemClock(llvm::ArrayRef<fir::ExtendedValue>);
mlir::Value genTand(mlir::Type, llvm::ArrayRef<mlir::Value>);
mlir::Value genTrailz(mlir::Type, llvm::ArrayRef<mlir::Value>);
Expand Down
5 changes: 5 additions & 0 deletions flang/lib/Evaluate/intrinsics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1393,6 +1393,11 @@ static const IntrinsicInterface intrinsicSubroutine[]{
{"get", DefaultInt, Rank::vector, Optionality::optional,
common::Intent::Out}},
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
{"system",
{{"command", DefaultChar, Rank::scalar},
{"exitstat", DefaultInt, Rank::scalar, Optionality::optional,
common::Intent::Out}},
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
{"system_clock",
{{"count", AnyInt, Rank::scalar, Optionality::optional,
common::Intent::Out},
Expand Down
36 changes: 36 additions & 0 deletions flang/lib/Optimizer/Builder/IntrinsicCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,10 @@ static constexpr IntrinsicHandler handlers[]{
{"dim", asValue},
{"mask", asBox, handleDynamicOptional}}},
/*isElemental=*/false},
{"system",
&I::genSystem,
{{{"command", asBox}, {"exitstat", asBox, handleDynamicOptional}}},
/*isElemental=*/false},
{"system_clock",
&I::genSystemClock,
{{{"count", asAddr}, {"count_rate", asAddr}, {"count_max", asAddr}}},
Expand Down Expand Up @@ -5934,6 +5938,38 @@ IntrinsicLibrary::genSum(mlir::Type resultType,
resultType, args);
}

// SYSTEM
void IntrinsicLibrary::genSystem(llvm::ArrayRef<fir::ExtendedValue> args) {
assert(args.size() == 2);
mlir::Value command = fir::getBase(args[0]);
const fir::ExtendedValue &exitstat = args[1];
assert(command && "expected COMMAND parameter");

mlir::Type boxNoneTy = fir::BoxType::get(builder.getNoneType());

mlir::Value waitBool = builder.createBool(loc, true);
mlir::Value exitstatBox =
isStaticallyPresent(exitstat)
? fir::getBase(exitstat)
: builder.create<fir::AbsentOp>(loc, boxNoneTy).getResult();

// Create a dummmy cmdstat to prevent EXECUTE_COMMAND_LINE terminate itself
// when cmdstat is assigned with a non-zero value but not present
mlir::Value tempValue =
builder.createIntegerConstant(loc, builder.getI2Type(), 0);
mlir::Value temp = builder.createTemporary(loc, builder.getI16Type());
mlir::Value castVal =
builder.createConvert(loc, builder.getI16Type(), tempValue);
builder.create<fir::StoreOp>(loc, castVal, temp);
mlir::Value cmdstatBox = builder.createBox(loc, temp);

mlir::Value cmdmsgBox =
builder.create<fir::AbsentOp>(loc, boxNoneTy).getResult();

fir::runtime::genExecuteCommandLine(builder, loc, command, waitBool,
exitstatBox, cmdstatBox, cmdmsgBox);
}

// SYSTEM_CLOCK
void IntrinsicLibrary::genSystemClock(llvm::ArrayRef<fir::ExtendedValue> args) {
assert(args.size() == 3);
Expand Down
34 changes: 34 additions & 0 deletions flang/test/Lower/Intrinsics/system-optional.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
! RUN: bbc -emit-hlfir %s -o - | FileCheck %s

! CHECK-LABEL: func.func @_QPall_args(
! CHECK-SAME: %[[commandArg:.*]]: !fir.boxchar<1> {fir.bindc_name = "command", fir.optional},
! CHECK-SAME: %[[exitstatArg:.*]]: !fir.ref<i32> {fir.bindc_name = "exitstat", fir.optional}) {
subroutine all_args(command, exitstat)
CHARACTER(*), OPTIONAL :: command
INTEGER, OPTIONAL :: exitstat
call system(command, exitstat)

! CHECK-NEXT: %[[cmdstatVal:.*]] = fir.alloca i16
! CHECK-NEXT: %[[commandUnbox:.*]]:2 = fir.unboxchar %[[commandArg]] : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
! CHECK-NEXT: %[[commandDeclare:.*]]:2 = hlfir.declare %[[commandUnbox]]#0 typeparams %[[commandUnbox]]#1 {fortran_attrs = #fir.var_attrs<optional>, uniq_name = "_QFall_argsEcommand"} : (!fir.ref<!fir.char<1,?>>, index) -> (!fir.boxchar<1>, !fir.ref<!fir.char<1,?>>)
! CHECK-NEXT: %[[exitstatDeclare:.*]]:2 = hlfir.declare %[[exitstatArg]] {fortran_attrs = #fir.var_attrs<optional>, uniq_name = "_QFall_argsEexitstat"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
! CHECK-NEXT: %[[exitstatIsPresent:.*]] = fir.is_present %[[exitstatDeclare]]#0 : (!fir.ref<i32>) -> i1
! CHECK-NEXT: %[[commandBox:.*]] = fir.embox %[[commandDeclare]]#1 typeparams %[[commandUnbox]]#1 : (!fir.ref<!fir.char<1,?>>, index) -> !fir.box<!fir.char<1,?>>
! CHECK-NEXT: %[[exitstatBox:.*]] = fir.embox %[[exitstatDeclare]]#1 : (!fir.ref<i32>) -> !fir.box<i32>
! CHECK-NEXT: %[[absentIntBox:.*]] = fir.absent !fir.box<i32>
! CHECK-NEXT: %[[exitstatRealBox:.*]] = arith.select %[[exitstatIsPresent]], %[[exitstatBox]], %[[absentIntBox]] : !fir.box<i32>
! CHECK-NEXT: %[[true:.*]] = arith.constant true
! CHECK-NEXT: %[[c0_i2:.*]] = arith.constant 0 : i2
! CHECK-NEXT: %[[c0_i16:.*]] = fir.convert %[[c0_i2]] : (i2) -> i16
! CHECK-NEXT: fir.store %[[c0_i16]] to %[[cmdstatVal]] : !fir.ref<i16>
! CHECK-NEXT: %[[cmdstatBox:.*]] = fir.embox %[[cmdstatVal]] : (!fir.ref<i16>) -> !fir.box<i16>
! CHECK-NEXT: %[[absentBox:.*]] = fir.absent !fir.box<none>
! CHECK: %[[c9_i32:.*]] = arith.constant 9 : i32
! CHECK-NEXT: %[[command:.*]] = fir.convert %[[commandBox]] : (!fir.box<!fir.char<1,?>>) -> !fir.box<none>
! CHECK-NEXT: %[[exitstat:.*]] = fir.convert %[[exitstatRealBox]] : (!fir.box<i32>) -> !fir.box<none>
! CHECK-NEXT: %[[cmdstat:.*]] = fir.convert %[[cmdstatBox]] : (!fir.box<i16>) -> !fir.box<none>
! CHECK: %[[VAL_16:.*]] = fir.call @_FortranAExecuteCommandLine(%[[command]], %[[true]], %[[exitstat]], %[[cmdstat]], %[[absentBox]], %[[VAL_15:.*]], %[[c9_i32]]) fastmath<contract> : (!fir.box<none>, i1, !fir.box<none>, !fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> none
! CHECK-NEXT: return
! CHECK-NEXT: }

end subroutine all_args
53 changes: 53 additions & 0 deletions flang/test/Lower/Intrinsics/system.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
! RUN: bbc -emit-hlfir %s -o - | FileCheck %s

! CHECK-LABEL: func.func @_QPall_args(
! CHECK-SAME: %[[commandArg:.*]]: !fir.boxchar<1> {fir.bindc_name = "command"},
! CHECK-SAME: %[[exitstatArg:.*]]: !fir.ref<i32> {fir.bindc_name = "exitstat"}) {
subroutine all_args(command, exitstat)
CHARACTER(*) :: command
INTEGER :: exitstat
call system(command, exitstat)
! CHECK-NEXT: %[[cmdstatVal:.*]] = fir.alloca i16
! CHECK-NEXT: %[[commandUnbox:.*]]:2 = fir.unboxchar %[[commandArg]] : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
! CHECK-NEXT: %[[commandDeclare:.*]]:2 = hlfir.declare %[[commandUnbox]]#0 typeparams %[[commandUnbox]]#1 {uniq_name = "_QFall_argsEcommand"} : (!fir.ref<!fir.char<1,?>>, index) -> (!fir.boxchar<1>, !fir.ref<!fir.char<1,?>>)
! CHECK-NEXT: %[[exitstatDeclare:.*]]:2 = hlfir.declare %[[exitstatArg]] {uniq_name = "_QFall_argsEexitstat"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
! CHECK-NEXT: %[[commandBox:.*]] = fir.embox %[[commandDeclare]]#1 typeparams %[[commandUnbox]]#1 : (!fir.ref<!fir.char<1,?>>, index) -> !fir.box<!fir.char<1,?>>
! CHECK-NEXT: %[[exitstatBox:.*]] = fir.embox %[[exitstatDeclare]]#1 : (!fir.ref<i32>) -> !fir.box<i32>
! CHECK-NEXT: %[[true:.*]] = arith.constant true
! CHECK-NEXT: %[[c0_i2:.*]] = arith.constant 0 : i2
! CHECK-NEXT: %[[c0_i16:.*]] = fir.convert %[[c0_i2]] : (i2) -> i16
! CHECK-NEXT: fir.store %[[c0_i16]] to %[[cmdstatVal]] : !fir.ref<i16>
! CHECK-NEXT: %[[cmdstatBox:.*]] = fir.embox %[[cmdstatVal]] : (!fir.ref<i16>) -> !fir.box<i16>
! CHECK-NEXT: %[[absentBox:.*]] = fir.absent !fir.box<none>
! CHECK: %[[c9_i32:.*]] = arith.constant 9 : i32
! CHECK-NEXT: %[[command:.*]] = fir.convert %[[commandBox]] : (!fir.box<!fir.char<1,?>>) -> !fir.box<none>
! CHECK-NEXT: %[[exitstat:.*]] = fir.convert %[[exitstatBox]] : (!fir.box<i32>) -> !fir.box<none>
! CHECK-NEXT: %[[cmdstat:.*]] = fir.convert %[[cmdstatBox]] : (!fir.box<i16>) -> !fir.box<none>
! CHECK: %[[VAL_13:.*]] = fir.call @_FortranAExecuteCommandLine(%[[command]], %[[true]], %[[exitstat]], %[[cmdstat]], %[[absentBox]], %[[VAL_12:.*]], %[[c9_i32]]) fastmath<contract> : (!fir.box<none>, i1, !fir.box<none>, !fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> none
! CHECK-NEXT: return
! CHECK-NEXT: }
end subroutine all_args

! CHECK-LABEL: func.func @_QPonly_command(
! CHECK-SAME: %[[commandArg:.*]]: !fir.boxchar<1> {fir.bindc_name = "command"}) {
subroutine only_command(command)
CHARACTER(*) :: command
call system(command)
! CHECK-NEXT: %[[cmdstatVal:.*]] = fir.alloca i16
! CHECK-NEXT: %[[commandUnbox:.*]]:2 = fir.unboxchar %arg0 : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
! CHECK-NEXT: %[[commandDeclare:.*]]:2 = hlfir.declare %[[commandUnbox]]#0 typeparams %[[commandUnbox]]#1 {uniq_name = "_QFonly_commandEcommand"} : (!fir.ref<!fir.char<1,?>>, index) -> (!fir.boxchar<1>, !fir.ref<!fir.char<1,?>>)
! CHECK-NEXT: %[[commandBox:.*]] = fir.embox %[[commandDeclare]]#1 typeparams %[[commandUnbox]]#1 : (!fir.ref<!fir.char<1,?>>, index) -> !fir.box<!fir.char<1,?>>
! CHECK-NEXT: %[[true:.*]] = arith.constant true
! CHECK-NEXT: %[[absentBox:.*]] = fir.absent !fir.box<none>
! CHECK-NEXT: %[[c0_i2:.*]] = arith.constant 0 : i2
! CHECK-NEXT: %[[c0_i16:.*]] = fir.convert %[[c0_i2]] : (i2) -> i16
! CHECK-NEXT: fir.store %[[c0_i16]] to %[[cmdstatVal]] : !fir.ref<i16>
! CHECK-NEXT: %[[cmdstatBox:.*]] = fir.embox %[[cmdstatVal]] : (!fir.ref<i16>) -> !fir.box<i16>
! CHECK-NEXT: %[[absentBox2:.*]] = fir.absent !fir.box<none>
! CHECK: %[[c35_i32:.*]] = arith.constant 35 : i32
! CHECK-NEXT: %[[command:.*]] = fir.convert %[[commandBox]] : (!fir.box<!fir.char<1,?>>) -> !fir.box<none>
! CHECK-NEXT: %[[cmdstat:.*]] = fir.convert %[[cmdstatBox]] : (!fir.box<i16>) -> !fir.box<none>
! CHECK: %[[VAL_12:.*]] = fir.call @_FortranAExecuteCommandLine(%[[command]], %[[true]], %[[absentBox]], %[[cmdstat]], %[[absentBox2]], %[[VAL_11:.*]], %[[c35_i32]]) fastmath<contract> : (!fir.box<none>, i1, !fir.box<none>, !fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> none
! CHECK-NEXT: return
! CHECK-NEXT: }
end subroutine only_command
54 changes: 54 additions & 0 deletions flang/unittests/Runtime/CommandTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,60 @@ TEST_F(ZeroArguments, ECLInvalidCommandAsyncDontAffectAsync) {
*command.get(), false, nullptr, nullptr, nullptr));
}

TEST_F(ZeroArguments, SystemValidCommandExitStat) {
// envrionment setup for SYSTEM from EXECUTE_COMMAND_LINE runtime
OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
bool wait{true};
// setup finished

OwningPtr<Descriptor> command{CharDescriptor("echo hi")};
OwningPtr<Descriptor> exitStat{EmptyIntDescriptor()};

RTNAME(ExecuteCommandLine)
(*command.get(), wait, exitStat.get(), cmdStat.get(), nullptr);
CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 0);
}

TEST_F(ZeroArguments, SystemInvalidCommandExitStat) {
// envrionment setup for SYSTEM from EXECUTE_COMMAND_LINE runtime
OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
bool wait{true};
// setup finished

OwningPtr<Descriptor> command{CharDescriptor("InvalidCommand")};
OwningPtr<Descriptor> exitStat{EmptyIntDescriptor()};

RTNAME(ExecuteCommandLine)
(*command.get(), wait, exitStat.get(), cmdStat.get(), nullptr);
#ifdef _WIN32
CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);
#else
CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 127);
#endif
}

TEST_F(ZeroArguments, SystemValidCommandOptionalExitStat) {
// envrionment setup for SYSTEM from EXECUTE_COMMAND_LINE runtime
OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
bool wait{true};
// setup finished

OwningPtr<Descriptor> command{CharDescriptor("echo hi")};
EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)(
*command.get(), wait, nullptr, cmdStat.get(), nullptr));
}

TEST_F(ZeroArguments, SystemInvalidCommandOptionalExitStat) {
// envrionment setup for SYSTEM from EXECUTE_COMMAND_LINE runtime
OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
bool wait{true};
// setup finished

OwningPtr<Descriptor> command{CharDescriptor("InvalidCommand")};
EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)(
*command.get(), wait, nullptr, cmdStat.get(), nullptr););
}

static const char *oneArgArgv[]{"aProgram", "anArgumentOfLength20"};
class OneArgument : public CommandFixture {
protected:
Expand Down