[flang-commits] [clang] [clang-tools-extra] [flang] [llvm] [flang] add SYSTEM runtime and lowering intrinsics support (PR #74309)
Yi Wu via flang-commits
flang-commits at lists.llvm.org
Tue Jan 16 06:22:32 PST 2024
https://github.com/yi-wu-arm updated https://github.com/llvm/llvm-project/pull/74309
>From 14f8c3e38791cc6b06455b8beffe37a6f7105e03 Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Mon, 4 Dec 2023 10:32:03 +0000
Subject: [PATCH 01/13] Add SYSTEM runtime and lowering intrinsic support
Calls std::system() function and pass the command,
cmd on Windows or shell on Linux.
Command parameter is required, exitstatus is optional.
call system(command)
call system(command, exitstatus)
---
flang/docs/Intrinsics.md | 2 +-
.../flang/Optimizer/Builder/IntrinsicCall.h | 1 +
.../flang/Optimizer/Builder/Runtime/Command.h | 5 +++
flang/include/flang/Runtime/command.h | 5 +++
flang/lib/Evaluate/intrinsics.cpp | 16 +++++---
flang/lib/Optimizer/Builder/IntrinsicCall.cpp | 22 ++++++++++
.../lib/Optimizer/Builder/Runtime/Command.cpp | 13 ++++++
flang/runtime/command.cpp | 19 +++++++++
flang/test/Lower/Intrinsics/system.f90 | 39 ++++++++++++++++++
flang/unittests/Runtime/CommandTest.cpp | 41 +++++++++++++++++++
10 files changed, 157 insertions(+), 6 deletions(-)
create mode 100644 flang/test/Lower/Intrinsics/system.f90
diff --git a/flang/docs/Intrinsics.md b/flang/docs/Intrinsics.md
index fef2b4ea4dd8c8..871332399628e9 100644
--- a/flang/docs/Intrinsics.md
+++ b/flang/docs/Intrinsics.md
@@ -751,7 +751,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, 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, SYSTEM, SYSTEM_CLOCK |
| Atomic intrinsic subroutines | ATOMIC_ADD |
| Collective intrinsic subroutines | CO_REDUCE |
diff --git a/flang/include/flang/Optimizer/Builder/IntrinsicCall.h b/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
index 5065f11ae9e726..669d076c3e0e7d 100644
--- a/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
+++ b/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
@@ -321,6 +321,7 @@ struct IntrinsicLibrary {
fir::ExtendedValue genStorageSize(mlir::Type,
llvm::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genSum(mlir::Type, 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>);
diff --git a/flang/include/flang/Optimizer/Builder/Runtime/Command.h b/flang/include/flang/Optimizer/Builder/Runtime/Command.h
index 976fb3aa0b6fbb..9d6a39639844fc 100644
--- a/flang/include/flang/Optimizer/Builder/Runtime/Command.h
+++ b/flang/include/flang/Optimizer/Builder/Runtime/Command.h
@@ -53,5 +53,10 @@ mlir::Value genGetEnvVariable(fir::FirOpBuilder &, mlir::Location,
mlir::Value length, mlir::Value trimName,
mlir::Value errmsg);
+/// Generate a call to System runtime function which implements
+/// the non-standard System GNU extension.
+void genSystem(fir::FirOpBuilder &, mlir::Location, mlir::Value command,
+ mlir::Value exitstat);
+
} // namespace fir::runtime
#endif // FORTRAN_OPTIMIZER_BUILDER_RUNTIME_COMMAND_H
diff --git a/flang/include/flang/Runtime/command.h b/flang/include/flang/Runtime/command.h
index c67d171c8e2f1b..f325faa7bd09fa 100644
--- a/flang/include/flang/Runtime/command.h
+++ b/flang/include/flang/Runtime/command.h
@@ -55,6 +55,11 @@ std::int32_t RTNAME(GetEnvVariable)(const Descriptor &name,
const Descriptor *value = nullptr, const Descriptor *length = nullptr,
bool trim_name = true, const Descriptor *errmsg = nullptr,
const char *sourceFile = nullptr, int line = 0);
+
+// Calls std::system()
+void RTNAME(System)(const Descriptor *command = nullptr,
+ const Descriptor *exitstat = nullptr, const char *sourceFile = nullptr,
+ int line = 0);
}
} // namespace Fortran::runtime
diff --git a/flang/lib/Evaluate/intrinsics.cpp b/flang/lib/Evaluate/intrinsics.cpp
index c5faf319fafb7d..bb6ac8ac7159a5 100644
--- a/flang/lib/Evaluate/intrinsics.cpp
+++ b/flang/lib/Evaluate/intrinsics.cpp
@@ -1387,6 +1387,11 @@ static const IntrinsicInterface intrinsicSubroutine[]{
{"get", DefaultInt, Rank::vector, Optionality::optional,
common::Intent::Out}},
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
+ {"system",
+ {{"command", DefaultChar, Rank::scalar},
+ {"exitstat", AnyInt, Rank::scalar, Optionality::optional,
+ common::Intent::InOut}},
+ {}, Rank::elemental, IntrinsicClass::impureSubroutine},
{"system_clock",
{{"count", AnyInt, Rank::scalar, Optionality::optional,
common::Intent::Out},
@@ -1885,7 +1890,7 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
int elementalRank{0};
for (std::size_t j{0}; j < dummies; ++j) {
const IntrinsicDummyArgument &d{dummy[std::min(j, dummyArgPatterns - 1)]};
- if (const ActualArgument *arg{actualForDummy[j]}) {
+ if (const ActualArgument * arg{actualForDummy[j]}) {
bool isAssumedRank{IsAssumedRank(*arg)};
if (isAssumedRank && d.rank != Rank::anyOrAssumedRank &&
d.rank != Rank::arrayOrAssumedRank) {
@@ -2222,7 +2227,8 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
case Rank::locReduced:
case Rank::scalarIfDim:
if (dummy[*dimArg].optionality == Optionality::required) {
- if (const Symbol *whole{
+ if (const Symbol *
+ whole{
UnwrapWholeSymbolOrComponentDataRef(actualForDummy[*dimArg])}) {
if (IsOptional(*whole) || IsAllocatableOrObjectPointer(whole)) {
if (context.languageFeatures().ShouldWarn(
@@ -2300,7 +2306,7 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
// Rearrange the actual arguments into dummy argument order.
ActualArguments rearranged(dummies);
for (std::size_t j{0}; j < dummies; ++j) {
- if (ActualArgument *arg{actualForDummy[j]}) {
+ if (ActualArgument * arg{actualForDummy[j]}) {
rearranged[j] = std::move(*arg);
}
}
@@ -2937,7 +2943,7 @@ static bool ApplySpecificChecks(SpecificCall &call, FoldingContext &context) {
ok &= CheckForCoindexedObject(context, call.arguments[3], name, "errmsg");
if (call.arguments[0] && call.arguments[1]) {
for (int j{0}; j < 2; ++j) {
- if (const Symbol *last{GetLastSymbol(call.arguments[j])};
+ if (const Symbol * last{GetLastSymbol(call.arguments[j])};
last && !IsAllocatable(last->GetUltimate())) {
context.messages().Say(call.arguments[j]->sourceLocation(),
"Argument #%d to MOVE_ALLOC must be allocatable"_err_en_US,
@@ -2957,7 +2963,7 @@ static bool ApplySpecificChecks(SpecificCall &call, FoldingContext &context) {
const auto &arg{call.arguments[0]};
if (arg) {
if (const auto *expr{arg->UnwrapExpr()}) {
- if (const Symbol *symbol{UnwrapWholeSymbolDataRef(*expr)}) {
+ if (const Symbol * symbol{UnwrapWholeSymbolDataRef(*expr)}) {
ok = symbol->attrs().test(semantics::Attr::OPTIONAL);
}
}
diff --git a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
index 24fdbe97856b3a..8fded510a18012 100644
--- a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
+++ b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
@@ -508,6 +508,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}}},
@@ -5316,6 +5320,24 @@ IntrinsicLibrary::genSum(mlir::Type resultType,
resultType, args);
}
+// SYSTEM
+void IntrinsicLibrary::genSystem(llvm::ArrayRef<fir::ExtendedValue> args) {
+ assert(args.size() >= 1);
+ mlir::Value command = fir::getBase(args[0]);
+ const fir::ExtendedValue &exitstat = args[1];
+
+ if (!command)
+ fir::emitFatalError(loc, "expected COMMAND parameter");
+
+ mlir::Type boxNoneTy = fir::BoxType::get(builder.getNoneType());
+
+ mlir::Value exitstatBox =
+ isStaticallyPresent(exitstat)
+ ? fir::getBase(exitstat)
+ : builder.create<fir::AbsentOp>(loc, boxNoneTy).getResult();
+
+ fir::runtime::genSystem(builder, loc, command, exitstatBox);
+}
// SYSTEM_CLOCK
void IntrinsicLibrary::genSystemClock(llvm::ArrayRef<fir::ExtendedValue> args) {
assert(args.size() == 3);
diff --git a/flang/lib/Optimizer/Builder/Runtime/Command.cpp b/flang/lib/Optimizer/Builder/Runtime/Command.cpp
index 1d719e7bbd9a2d..00af35a74bf871 100644
--- a/flang/lib/Optimizer/Builder/Runtime/Command.cpp
+++ b/flang/lib/Optimizer/Builder/Runtime/Command.cpp
@@ -88,3 +88,16 @@ mlir::Value fir::runtime::genGetEnvVariable(fir::FirOpBuilder &builder,
sourceFile, sourceLine);
return builder.create<fir::CallOp>(loc, runtimeFunc, args).getResult(0);
}
+
+void fir::runtime::genSystem(fir::FirOpBuilder &builder, mlir::Location loc,
+ mlir::Value command, mlir::Value exitstat) {
+ auto runtimeFunc =
+ fir::runtime::getRuntimeFunc<mkRTKey(System)>(loc, builder);
+ mlir::FunctionType runtimeFuncTy = runtimeFunc.getFunctionType();
+ mlir::Value sourceFile = fir::factory::locationToFilename(builder, loc);
+ mlir::Value sourceLine =
+ fir::factory::locationToLineNo(builder, loc, runtimeFuncTy.getInput(3));
+ llvm::SmallVector<mlir::Value> args = fir::runtime::createArguments(
+ builder, loc, runtimeFuncTy, command, exitstat, sourceFile, sourceLine);
+ builder.create<fir::CallOp>(loc, runtimeFunc, args);
+}
diff --git a/flang/runtime/command.cpp b/flang/runtime/command.cpp
index 8e6135b5487c05..a0f71147275fd1 100644
--- a/flang/runtime/command.cpp
+++ b/flang/runtime/command.cpp
@@ -282,4 +282,23 @@ std::int32_t RTNAME(GetEnvVariable)(const Descriptor &name,
return StatOk;
}
+void RTNAME(System)(const Descriptor *command, const Descriptor *exitstat,
+ const char *sourceFile, int line) {
+ Terminator terminator{sourceFile, line};
+
+ if (command) {
+ RUNTIME_CHECK(terminator, IsValidCharDescriptor(command));
+ int status{std::system(command->OffsetElement())};
+ if (exitstat) {
+ RUNTIME_CHECK(terminator, IsValidIntDescriptor(exitstat));
+#ifdef _WIN32
+ StoreLengthToDescriptor(exitstat, status, terminator);
+#else
+ int exitstatVal{WEXITSTATUS(status)};
+ StoreLengthToDescriptor(exitstat, exitstatVal, terminator);
+#endif
+ }
+ }
+}
+
} // namespace Fortran::runtime
diff --git a/flang/test/Lower/Intrinsics/system.f90 b/flang/test/Lower/Intrinsics/system.f90
new file mode 100644
index 00000000000000..da9ccd9d22ae2f
--- /dev/null
+++ b/flang/test/Lower/Intrinsics/system.f90
@@ -0,0 +1,39 @@
+! RUN: bbc -emit-hlfir %s -o - | FileCheck %s
+
+! CHECK-LABEL: func.func @_QPall_args() {
+! CHECK: %0 = fir.alloca !fir.char<1,30> {bindc_name = "command", uniq_name = "_QFall_argsEcommand"}
+! CHECK-NEXT: %1:2 = hlfir.declare %0 typeparams %c30 {uniq_name = "_QFall_argsEcommand"} : (!fir.ref<!fir.char<1,30>>, index) -> (!fir.ref<!fir.char<1,30>>, !fir.ref<!fir.char<1,30>>)
+! CHECK-NEXT: %2 = fir.alloca i32 {bindc_name = "exitval", uniq_name = "_QFall_argsEexitval"}
+! CHECK-NEXT: %3:2 = hlfir.declare %2 {uniq_name = "_QFall_argsEexitval"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+! CHECK-NEXT: %4 = fir.embox %1#1 : (!fir.ref<!fir.char<1,30>>) -> !fir.box<!fir.char<1,30>>
+! CHECK-NEXT: %5 = fir.embox %3#1 : (!fir.ref<i32>) -> !fir.box<i32>
+! CHECK-NEXT: %6 = fir.address_of(@_QQclX6c088457724b07fb671a04391501cd9d) : !fir.ref<!fir.char<1,73>>
+! CHECK-NEXT: %c21_i32 = arith.constant 21 : i32
+! CHECK-NEXT: %7 = fir.convert %4 : (!fir.box<!fir.char<1,30>>) -> !fir.box<none>
+! CHECK-NEXT: %8 = fir.convert %5 : (!fir.box<i32>) -> !fir.box<none>
+! CHECK-NEXT: %9 = fir.convert %6 : (!fir.ref<!fir.char<1,73>>) -> !fir.ref<i8>
+! CHECK-NEXT: %10 = fir.call @_FortranASystem(%7, %8, %9, %c21_i32) fastmath<contract> : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> none
+! CHECK-NEXT: return
+! CHECK-NEXT: }
+subroutine all_args()
+CHARACTER(30) :: command
+INTEGER :: exitVal
+call system(command, exitVal)
+end subroutine all_args
+
+! CHECK-LABEL: func.func @_QPonly_command() {
+! CHECK: %0 = fir.alloca !fir.char<1,30> {bindc_name = "command", uniq_name = "_QFonly_commandEcommand"}
+! CHECK-NEXT: %1:2 = hlfir.declare %0 typeparams %c30 {uniq_name = "_QFonly_commandEcommand"} : (!fir.ref<!fir.char<1,30>>, index) -> (!fir.ref<!fir.char<1,30>>, !fir.ref<!fir.char<1,30>>)
+! CHECK-NEXT: %2 = fir.embox %1#1 : (!fir.ref<!fir.char<1,30>>) -> !fir.box<!fir.char<1,30>>
+! CHECK-NEXT: %3 = fir.absent !fir.box<none>
+! CHECK-NEXT: %4 = fir.address_of(@_QQclX6c088457724b07fb671a04391501cd9d) : !fir.ref<!fir.char<1,73>>
+! CHECK-NEXT: %c38_i32 = arith.constant 38 : i32
+! CHECK-NEXT: %5 = fir.convert %2 : (!fir.box<!fir.char<1,30>>) -> !fir.box<none>
+! CHECK-NEXT: %6 = fir.convert %4 : (!fir.ref<!fir.char<1,73>>) -> !fir.ref<i8>
+! CHECK-NEXT: %7 = fir.call @_FortranASystem(%5, %3, %6, %c38_i32) fastmath<contract> : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> none
+! CHECK-NEXT: return
+! CHECK-NEXT: }
+subroutine only_command()
+CHARACTER(30) :: command
+call system(command)
+end subroutine only_command
diff --git a/flang/unittests/Runtime/CommandTest.cpp b/flang/unittests/Runtime/CommandTest.cpp
index 9f66c7924c86e3..f0bbc721a67073 100644
--- a/flang/unittests/Runtime/CommandTest.cpp
+++ b/flang/unittests/Runtime/CommandTest.cpp
@@ -46,6 +46,17 @@ static OwningPtr<Descriptor> EmptyIntDescriptor() {
return descriptor;
}
+template <int kind = sizeof(std::int64_t)>
+static OwningPtr<Descriptor> IntDescriptor(const int &value) {
+ OwningPtr<Descriptor> descriptor{Descriptor::Create(TypeCategory::Integer,
+ kind, nullptr, 0, nullptr, CFI_attribute_allocatable)};
+ if (descriptor->Allocate() != 0) {
+ return nullptr;
+ }
+ std::memcpy(descriptor->OffsetElement<int>(), &value, sizeof(int));
+ return descriptor;
+}
+
class CommandFixture : public ::testing::Test {
protected:
CommandFixture(int argc, const char *argv[]) {
@@ -227,6 +238,36 @@ TEST_F(ZeroArguments, GetCommandArgument) {
TEST_F(ZeroArguments, GetCommand) { CheckCommandValue(commandOnlyArgv, 1); }
+TEST_F(ZeroArguments, SystemValidCommandExitStat) {
+ OwningPtr<Descriptor> command{CharDescriptor("echo hi")};
+ OwningPtr<Descriptor> exitStat{EmptyIntDescriptor()};
+
+ RTNAME(System)(command.get(), exitStat.get());
+ CheckDescriptorEqInt(exitStat.get(), 0);
+}
+
+TEST_F(ZeroArguments, SystemInvalidCommandExitStat) {
+ OwningPtr<Descriptor> command{CharDescriptor("InvalidCommand")};
+ OwningPtr<Descriptor> exitStat{EmptyIntDescriptor()};
+
+ RTNAME(System)(command.get(), exitStat.get());
+#ifdef _WIN32
+ CheckDescriptorEqInt(exitStat.get(), 1);
+#else
+ CheckDescriptorEqInt(exitStat.get(), 127);
+#endif
+}
+
+TEST_F(ZeroArguments, SystemValidCommandOptionalExitStat) {
+ OwningPtr<Descriptor> command{CharDescriptor("echo hi")};
+ EXPECT_NO_FATAL_FAILURE(RTNAME(System)(command.get(), nullptr));
+}
+
+TEST_F(ZeroArguments, SystemInvalidCommandOptionalExitStat) {
+ OwningPtr<Descriptor> command{CharDescriptor("InvalidCommand")};
+ EXPECT_NO_FATAL_FAILURE(RTNAME(System)(command.get(), nullptr));
+}
+
static const char *oneArgArgv[]{"aProgram", "anArgumentOfLength20"};
class OneArgument : public CommandFixture {
protected:
>From be27d36ca18910b9cf521da26948a3fd1c03e228 Mon Sep 17 00:00:00 2001
From: Yi Wu <yiwu02 at wdev-yiwu02.arm.com>
Date: Mon, 4 Dec 2023 12:01:23 +0000
Subject: [PATCH 02/13] test fixes
---
flang/test/Lower/Intrinsics/system.f90 | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
diff --git a/flang/test/Lower/Intrinsics/system.f90 b/flang/test/Lower/Intrinsics/system.f90
index da9ccd9d22ae2f..0c3e5fd63047c6 100644
--- a/flang/test/Lower/Intrinsics/system.f90
+++ b/flang/test/Lower/Intrinsics/system.f90
@@ -7,12 +7,10 @@
! CHECK-NEXT: %3:2 = hlfir.declare %2 {uniq_name = "_QFall_argsEexitval"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
! CHECK-NEXT: %4 = fir.embox %1#1 : (!fir.ref<!fir.char<1,30>>) -> !fir.box<!fir.char<1,30>>
! CHECK-NEXT: %5 = fir.embox %3#1 : (!fir.ref<i32>) -> !fir.box<i32>
-! CHECK-NEXT: %6 = fir.address_of(@_QQclX6c088457724b07fb671a04391501cd9d) : !fir.ref<!fir.char<1,73>>
-! CHECK-NEXT: %c21_i32 = arith.constant 21 : i32
+! CHECK: %c19_i32 = arith.constant 19 : i32
! CHECK-NEXT: %7 = fir.convert %4 : (!fir.box<!fir.char<1,30>>) -> !fir.box<none>
! CHECK-NEXT: %8 = fir.convert %5 : (!fir.box<i32>) -> !fir.box<none>
-! CHECK-NEXT: %9 = fir.convert %6 : (!fir.ref<!fir.char<1,73>>) -> !fir.ref<i8>
-! CHECK-NEXT: %10 = fir.call @_FortranASystem(%7, %8, %9, %c21_i32) fastmath<contract> : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> none
+! CHECK: %10 = fir.call @_FortranASystem(%7, %8, %9, %c19_i32) fastmath<contract> : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> none
! CHECK-NEXT: return
! CHECK-NEXT: }
subroutine all_args()
@@ -26,11 +24,9 @@ end subroutine all_args
! CHECK-NEXT: %1:2 = hlfir.declare %0 typeparams %c30 {uniq_name = "_QFonly_commandEcommand"} : (!fir.ref<!fir.char<1,30>>, index) -> (!fir.ref<!fir.char<1,30>>, !fir.ref<!fir.char<1,30>>)
! CHECK-NEXT: %2 = fir.embox %1#1 : (!fir.ref<!fir.char<1,30>>) -> !fir.box<!fir.char<1,30>>
! CHECK-NEXT: %3 = fir.absent !fir.box<none>
-! CHECK-NEXT: %4 = fir.address_of(@_QQclX6c088457724b07fb671a04391501cd9d) : !fir.ref<!fir.char<1,73>>
-! CHECK-NEXT: %c38_i32 = arith.constant 38 : i32
+! CHECK: %c34_i32 = arith.constant 34 : i32
! CHECK-NEXT: %5 = fir.convert %2 : (!fir.box<!fir.char<1,30>>) -> !fir.box<none>
-! CHECK-NEXT: %6 = fir.convert %4 : (!fir.ref<!fir.char<1,73>>) -> !fir.ref<i8>
-! CHECK-NEXT: %7 = fir.call @_FortranASystem(%5, %3, %6, %c38_i32) fastmath<contract> : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> none
+! CHECK: %7 = fir.call @_FortranASystem(%5, %3, %6, %c34_i32) fastmath<contract> : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> none
! CHECK-NEXT: return
! CHECK-NEXT: }
subroutine only_command()
>From d8215296237586aabc180ed36c4220f58ccef8e3 Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Mon, 4 Dec 2023 12:22:38 +0000
Subject: [PATCH 03/13] remove unnecessary clang-format change
---
flang/lib/Evaluate/intrinsics.cpp | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/flang/lib/Evaluate/intrinsics.cpp b/flang/lib/Evaluate/intrinsics.cpp
index bb6ac8ac7159a5..98dd2b27a08ebf 100644
--- a/flang/lib/Evaluate/intrinsics.cpp
+++ b/flang/lib/Evaluate/intrinsics.cpp
@@ -1890,7 +1890,7 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
int elementalRank{0};
for (std::size_t j{0}; j < dummies; ++j) {
const IntrinsicDummyArgument &d{dummy[std::min(j, dummyArgPatterns - 1)]};
- if (const ActualArgument * arg{actualForDummy[j]}) {
+ if (const ActualArgument *arg{actualForDummy[j]}) {
bool isAssumedRank{IsAssumedRank(*arg)};
if (isAssumedRank && d.rank != Rank::anyOrAssumedRank &&
d.rank != Rank::arrayOrAssumedRank) {
@@ -2227,8 +2227,7 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
case Rank::locReduced:
case Rank::scalarIfDim:
if (dummy[*dimArg].optionality == Optionality::required) {
- if (const Symbol *
- whole{
+ if (const Symbol *whole{
UnwrapWholeSymbolOrComponentDataRef(actualForDummy[*dimArg])}) {
if (IsOptional(*whole) || IsAllocatableOrObjectPointer(whole)) {
if (context.languageFeatures().ShouldWarn(
@@ -2306,7 +2305,7 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
// Rearrange the actual arguments into dummy argument order.
ActualArguments rearranged(dummies);
for (std::size_t j{0}; j < dummies; ++j) {
- if (ActualArgument * arg{actualForDummy[j]}) {
+ if (ActualArgument *arg{actualForDummy[j]}) {
rearranged[j] = std::move(*arg);
}
}
@@ -2963,7 +2962,7 @@ static bool ApplySpecificChecks(SpecificCall &call, FoldingContext &context) {
const auto &arg{call.arguments[0]};
if (arg) {
if (const auto *expr{arg->UnwrapExpr()}) {
- if (const Symbol * symbol{UnwrapWholeSymbolDataRef(*expr)}) {
+ if (const Symbol *symbol{UnwrapWholeSymbolDataRef(*expr)}) {
ok = symbol->attrs().test(semantics::Attr::OPTIONAL);
}
}
>From 9a73cb8b5e30a0963dceb3b8f2e6265f400f2f71 Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Thu, 7 Dec 2023 12:20:05 +0000
Subject: [PATCH 04/13] required parameter use reference not pointer, ensure
command is null-terminated
---
flang/include/flang/Runtime/command.h | 2 +-
flang/runtime/command.cpp | 37 ++++++++++++++++++-------
flang/unittests/Runtime/CommandTest.cpp | 8 +++---
3 files changed, 32 insertions(+), 15 deletions(-)
diff --git a/flang/include/flang/Runtime/command.h b/flang/include/flang/Runtime/command.h
index f325faa7bd09fa..e477a2da1c5954 100644
--- a/flang/include/flang/Runtime/command.h
+++ b/flang/include/flang/Runtime/command.h
@@ -57,7 +57,7 @@ std::int32_t RTNAME(GetEnvVariable)(const Descriptor &name,
const char *sourceFile = nullptr, int line = 0);
// Calls std::system()
-void RTNAME(System)(const Descriptor *command = nullptr,
+void RTNAME(System)(const Descriptor &command,
const Descriptor *exitstat = nullptr, const char *sourceFile = nullptr,
int line = 0);
}
diff --git a/flang/runtime/command.cpp b/flang/runtime/command.cpp
index a0f71147275fd1..709e1cb4904611 100644
--- a/flang/runtime/command.cpp
+++ b/flang/runtime/command.cpp
@@ -282,22 +282,39 @@ std::int32_t RTNAME(GetEnvVariable)(const Descriptor &name,
return StatOk;
}
-void RTNAME(System)(const Descriptor *command, const Descriptor *exitstat,
+const char *ensureNullTerminated(
+ const char *str, size_t length, Terminator &terminator) {
+ if (length < strlen(str)) {
+ char *newCmd{(char *)malloc(length + 1)};
+ if (newCmd == NULL) {
+ terminator.Crash("Command not null-terminated, memory allocation failed "
+ "for null-terminated newCmd.");
+ }
+
+ strncpy(newCmd, str, length);
+ newCmd[length] = '\0';
+ return newCmd;
+ } else {
+ return str;
+ }
+}
+
+void RTNAME(System)(const Descriptor &command, const Descriptor *exitstat,
const char *sourceFile, int line) {
Terminator terminator{sourceFile, line};
- if (command) {
- RUNTIME_CHECK(terminator, IsValidCharDescriptor(command));
- int status{std::system(command->OffsetElement())};
- if (exitstat) {
- RUNTIME_CHECK(terminator, IsValidIntDescriptor(exitstat));
+ const char *newCmd{ensureNullTerminated(
+ command.OffsetElement(), command.ElementBytes(), terminator)};
+ int status{std::system(newCmd)};
+
+ if (exitstat) {
+ RUNTIME_CHECK(terminator, IsValidIntDescriptor(exitstat));
#ifdef _WIN32
- StoreLengthToDescriptor(exitstat, status, terminator);
+ StoreLengthToDescriptor(exitstat, status, terminator);
#else
- int exitstatVal{WEXITSTATUS(status)};
- StoreLengthToDescriptor(exitstat, exitstatVal, terminator);
+ int exitstatVal{WEXITSTATUS(status)};
+ StoreLengthToDescriptor(exitstat, exitstatVal, terminator);
#endif
- }
}
}
diff --git a/flang/unittests/Runtime/CommandTest.cpp b/flang/unittests/Runtime/CommandTest.cpp
index f0bbc721a67073..466e64cf65299e 100644
--- a/flang/unittests/Runtime/CommandTest.cpp
+++ b/flang/unittests/Runtime/CommandTest.cpp
@@ -242,7 +242,7 @@ TEST_F(ZeroArguments, SystemValidCommandExitStat) {
OwningPtr<Descriptor> command{CharDescriptor("echo hi")};
OwningPtr<Descriptor> exitStat{EmptyIntDescriptor()};
- RTNAME(System)(command.get(), exitStat.get());
+ RTNAME(System)(*command.get(), exitStat.get());
CheckDescriptorEqInt(exitStat.get(), 0);
}
@@ -250,7 +250,7 @@ TEST_F(ZeroArguments, SystemInvalidCommandExitStat) {
OwningPtr<Descriptor> command{CharDescriptor("InvalidCommand")};
OwningPtr<Descriptor> exitStat{EmptyIntDescriptor()};
- RTNAME(System)(command.get(), exitStat.get());
+ RTNAME(System)(*command.get(), exitStat.get());
#ifdef _WIN32
CheckDescriptorEqInt(exitStat.get(), 1);
#else
@@ -260,12 +260,12 @@ TEST_F(ZeroArguments, SystemInvalidCommandExitStat) {
TEST_F(ZeroArguments, SystemValidCommandOptionalExitStat) {
OwningPtr<Descriptor> command{CharDescriptor("echo hi")};
- EXPECT_NO_FATAL_FAILURE(RTNAME(System)(command.get(), nullptr));
+ EXPECT_NO_FATAL_FAILURE(RTNAME(System)(*command.get(), nullptr));
}
TEST_F(ZeroArguments, SystemInvalidCommandOptionalExitStat) {
OwningPtr<Descriptor> command{CharDescriptor("InvalidCommand")};
- EXPECT_NO_FATAL_FAILURE(RTNAME(System)(command.get(), nullptr));
+ EXPECT_NO_FATAL_FAILURE(RTNAME(System)(*command.get(), nullptr));
}
static const char *oneArgArgv[]{"aProgram", "anArgumentOfLength20"};
>From da3f993b775c0e9a241a38b73eb06a41ea65ecd3 Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Tue, 12 Dec 2023 11:30:16 +0000
Subject: [PATCH 05/13] remove redundant clang-format and minor fixes
---
flang/lib/Evaluate/intrinsics.cpp | 2 +-
flang/runtime/command.cpp | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/flang/lib/Evaluate/intrinsics.cpp b/flang/lib/Evaluate/intrinsics.cpp
index 98dd2b27a08ebf..a1066ef521961e 100644
--- a/flang/lib/Evaluate/intrinsics.cpp
+++ b/flang/lib/Evaluate/intrinsics.cpp
@@ -2942,7 +2942,7 @@ static bool ApplySpecificChecks(SpecificCall &call, FoldingContext &context) {
ok &= CheckForCoindexedObject(context, call.arguments[3], name, "errmsg");
if (call.arguments[0] && call.arguments[1]) {
for (int j{0}; j < 2; ++j) {
- if (const Symbol * last{GetLastSymbol(call.arguments[j])};
+ if (const Symbol *last{GetLastSymbol(call.arguments[j])};
last && !IsAllocatable(last->GetUltimate())) {
context.messages().Say(call.arguments[j]->sourceLocation(),
"Argument #%d to MOVE_ALLOC must be allocatable"_err_en_US,
diff --git a/flang/runtime/command.cpp b/flang/runtime/command.cpp
index 709e1cb4904611..03efb57e2c8cd7 100644
--- a/flang/runtime/command.cpp
+++ b/flang/runtime/command.cpp
@@ -284,7 +284,7 @@ std::int32_t RTNAME(GetEnvVariable)(const Descriptor &name,
const char *ensureNullTerminated(
const char *str, size_t length, Terminator &terminator) {
- if (length < strlen(str)) {
+ if (length <= strlen(str)) {
char *newCmd{(char *)malloc(length + 1)};
if (newCmd == NULL) {
terminator.Crash("Command not null-terminated, memory allocation failed "
>From 2dbeb8162750e9a2bed81b09f20cef66c8967587 Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Thu, 14 Dec 2023 13:19:55 +0000
Subject: [PATCH 06/13] tests fixes: avoid hard-coded SSA
---
.../test/Lower/Intrinsics/system-optional.f90 | 24 +++++++++
flang/test/Lower/Intrinsics/system.f90 | 54 ++++++++++---------
2 files changed, 52 insertions(+), 26 deletions(-)
create mode 100644 flang/test/Lower/Intrinsics/system-optional.f90
diff --git a/flang/test/Lower/Intrinsics/system-optional.f90 b/flang/test/Lower/Intrinsics/system-optional.f90
new file mode 100644
index 00000000000000..d9f5a79965a98a
--- /dev/null
+++ b/flang/test/Lower/Intrinsics/system-optional.f90
@@ -0,0 +1,24 @@
+! 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: %[[exitvalArg:.*]]: !fir.ref<i32> {fir.bindc_name = "exitval", fir.optional}) {
+subroutine all_args(command, exitVal)
+CHARACTER(*), OPTIONAL :: command
+INTEGER, OPTIONAL :: exitVal
+call system(command, exitVal)
+! 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: %[[exitvalDeclare:.*]]:2 = hlfir.declare %[[exitvalArg]] {fortran_attrs = #fir.var_attrs<optional>, uniq_name = "_QFall_argsEexitval"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+! CHECK-NEXT: %[[exitvalIsPresent:.*]] = fir.is_present %[[exitvalDeclare]]#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: %[[exitvalBox:.*]] = fir.embox %[[exitvalDeclare]]#1 : (!fir.ref<i32>) -> !fir.box<i32>
+! CHECK-NEXT: %[[absentBox:.*]] = fir.absent !fir.box<i32>
+! CHECK-NEXT: %[[exitvalSelect:.*]] = arith.select %[[exitvalIsPresent]], %[[exitvalBox]], %[[absentBox]] : !fir.box<i32>
+! CHECK: %c9_i32 = arith.constant 9 : i32
+! CHECK-NEXT: %[[command:.*]] = fir.convert %[[commandBox]] : (!fir.box<!fir.char<1,?>>) -> !fir.box<none>
+! CHECK-NEXT: %[[exitval:.*]] = fir.convert %[[exitvalSelect]] : (!fir.box<i32>) -> !fir.box<none>
+! CHECK: %[[VAL_12:.*]] = fir.call @_FortranASystem(%[[command]], %[[exitval]], %[[VAL_11:.*]], %c9_i32) fastmath<contract> : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> none
+! CHECK-NEXT: return
+! CHECK-NEXT: }
+end subroutine all_args
diff --git a/flang/test/Lower/Intrinsics/system.f90 b/flang/test/Lower/Intrinsics/system.f90
index 0c3e5fd63047c6..46fc828a544acc 100644
--- a/flang/test/Lower/Intrinsics/system.f90
+++ b/flang/test/Lower/Intrinsics/system.f90
@@ -1,35 +1,37 @@
! RUN: bbc -emit-hlfir %s -o - | FileCheck %s
-! CHECK-LABEL: func.func @_QPall_args() {
-! CHECK: %0 = fir.alloca !fir.char<1,30> {bindc_name = "command", uniq_name = "_QFall_argsEcommand"}
-! CHECK-NEXT: %1:2 = hlfir.declare %0 typeparams %c30 {uniq_name = "_QFall_argsEcommand"} : (!fir.ref<!fir.char<1,30>>, index) -> (!fir.ref<!fir.char<1,30>>, !fir.ref<!fir.char<1,30>>)
-! CHECK-NEXT: %2 = fir.alloca i32 {bindc_name = "exitval", uniq_name = "_QFall_argsEexitval"}
-! CHECK-NEXT: %3:2 = hlfir.declare %2 {uniq_name = "_QFall_argsEexitval"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
-! CHECK-NEXT: %4 = fir.embox %1#1 : (!fir.ref<!fir.char<1,30>>) -> !fir.box<!fir.char<1,30>>
-! CHECK-NEXT: %5 = fir.embox %3#1 : (!fir.ref<i32>) -> !fir.box<i32>
-! CHECK: %c19_i32 = arith.constant 19 : i32
-! CHECK-NEXT: %7 = fir.convert %4 : (!fir.box<!fir.char<1,30>>) -> !fir.box<none>
-! CHECK-NEXT: %8 = fir.convert %5 : (!fir.box<i32>) -> !fir.box<none>
-! CHECK: %10 = fir.call @_FortranASystem(%7, %8, %9, %c19_i32) fastmath<contract> : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> none
-! CHECK-NEXT: return
-! CHECK-NEXT: }
-subroutine all_args()
-CHARACTER(30) :: command
+! CHECK-LABEL: func.func @_QPall_args(
+! CHECK-SAME: %[[commandArg:.*]]: !fir.boxchar<1> {fir.bindc_name = "command"},
+! CHECK-SAME: %[[exitvalArg:.*]]: !fir.ref<i32> {fir.bindc_name = "exitval"}) {
+subroutine all_args(command, exitVal)
+CHARACTER(*) :: command
INTEGER :: exitVal
call system(command, exitVal)
+! 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: %[[exitvalDeclare:.*]]:2 = hlfir.declare %[[exitvalArg]] {uniq_name = "_QFall_argsEexitval"} : (!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: %[[exitvalBox:.*]] = fir.embox %[[exitvalDeclare]]#1 : (!fir.ref<i32>) -> !fir.box<i32>
+! CHECK: %c9_i32 = arith.constant 9 : i32
+! CHECK-NEXT: %[[command:.*]] = fir.convert %[[commandBox]] : (!fir.box<!fir.char<1,?>>) -> !fir.box<none>
+! CHECK-NEXT: %[[exitval:.*]] = fir.convert %[[exitvalBox]] : (!fir.box<i32>) -> !fir.box<none>
+! CHECK: %[[VAL_9:.*]] = fir.call @_FortranASystem(%[[command]], %[[exitval]], %[[VAL_8:.*]], %c9_i32) fastmath<contract> : (!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: %0 = fir.alloca !fir.char<1,30> {bindc_name = "command", uniq_name = "_QFonly_commandEcommand"}
-! CHECK-NEXT: %1:2 = hlfir.declare %0 typeparams %c30 {uniq_name = "_QFonly_commandEcommand"} : (!fir.ref<!fir.char<1,30>>, index) -> (!fir.ref<!fir.char<1,30>>, !fir.ref<!fir.char<1,30>>)
-! CHECK-NEXT: %2 = fir.embox %1#1 : (!fir.ref<!fir.char<1,30>>) -> !fir.box<!fir.char<1,30>>
-! CHECK-NEXT: %3 = fir.absent !fir.box<none>
-! CHECK: %c34_i32 = arith.constant 34 : i32
-! CHECK-NEXT: %5 = fir.convert %2 : (!fir.box<!fir.char<1,30>>) -> !fir.box<none>
-! CHECK: %7 = fir.call @_FortranASystem(%5, %3, %6, %c34_i32) fastmath<contract> : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> none
+! 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: %[[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 = "_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: %[[absentBox:.*]] = fir.absent !fir.box<none>
+! CHECK: %c27_i32 = arith.constant 27 : i32
+! CHECK-NEXT: %[[command:.*]] = fir.convert %[[commandBox]] : (!fir.box<!fir.char<1,?>>) -> !fir.box<none>
+! CHECK: %[[VAL_7:.*]] = fir.call @_FortranASystem(%[[command]], %[[absentBox]], %[[VAL_6:.*]], %c27_i32) fastmath<contract> : (!fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> none
! CHECK-NEXT: return
! CHECK-NEXT: }
-subroutine only_command()
-CHARACTER(30) :: command
-call system(command)
end subroutine only_command
>From 27aa276d16fcd754377286318f9738f59695b7e5 Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Thu, 14 Dec 2023 13:43:52 +0000
Subject: [PATCH 07/13] move EnsureNullTerminated to tools.h, capitalized and
free memory after use
---
flang/runtime/command.cpp | 20 ++------------------
flang/runtime/tools.h | 12 ++++++++++++
2 files changed, 14 insertions(+), 18 deletions(-)
diff --git a/flang/runtime/command.cpp b/flang/runtime/command.cpp
index 03efb57e2c8cd7..4db7ed38e8d20a 100644
--- a/flang/runtime/command.cpp
+++ b/flang/runtime/command.cpp
@@ -282,28 +282,11 @@ std::int32_t RTNAME(GetEnvVariable)(const Descriptor &name,
return StatOk;
}
-const char *ensureNullTerminated(
- const char *str, size_t length, Terminator &terminator) {
- if (length <= strlen(str)) {
- char *newCmd{(char *)malloc(length + 1)};
- if (newCmd == NULL) {
- terminator.Crash("Command not null-terminated, memory allocation failed "
- "for null-terminated newCmd.");
- }
-
- strncpy(newCmd, str, length);
- newCmd[length] = '\0';
- return newCmd;
- } else {
- return str;
- }
-}
-
void RTNAME(System)(const Descriptor &command, const Descriptor *exitstat,
const char *sourceFile, int line) {
Terminator terminator{sourceFile, line};
- const char *newCmd{ensureNullTerminated(
+ const char *newCmd{EnsureNullTerminated(
command.OffsetElement(), command.ElementBytes(), terminator)};
int status{std::system(newCmd)};
@@ -315,6 +298,7 @@ void RTNAME(System)(const Descriptor &command, const Descriptor *exitstat,
int exitstatVal{WEXITSTATUS(status)};
StoreLengthToDescriptor(exitstat, exitstatVal, terminator);
#endif
+ FreeMemory((void *)newCmd);
}
}
diff --git a/flang/runtime/tools.h b/flang/runtime/tools.h
index ea659190e14391..735acad728422b 100644
--- a/flang/runtime/tools.h
+++ b/flang/runtime/tools.h
@@ -411,5 +411,17 @@ RT_API_ATTRS void ShallowCopy(const Descriptor &to, const Descriptor &from,
bool toIsContiguous, bool fromIsContiguous);
RT_API_ATTRS void ShallowCopy(const Descriptor &to, const Descriptor &from);
+inline RT_API_ATTRS const char *EnsureNullTerminated(
+ const char *str, size_t length, Terminator &terminator) {
+ if (length <= std::strlen(str)) {
+ char *newCmd{(char *)AllocateMemoryOrCrash(terminator, length + 1)};
+ std::memcpy(newCmd, str, length);
+ newCmd[length] = '\0';
+ return newCmd;
+ } else {
+ return str;
+ }
+}
+
} // namespace Fortran::runtime
#endif // FORTRAN_RUNTIME_TOOLS_H_
>From cb88f6ebe458193cac62199005392fc7bda6e2db Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Thu, 14 Dec 2023 16:59:59 +0000
Subject: [PATCH 08/13] declare in tools.g, define in tools.cpp
---
flang/runtime/tools.cpp | 12 ++++++++++++
flang/runtime/tools.h | 13 ++-----------
2 files changed, 14 insertions(+), 11 deletions(-)
diff --git a/flang/runtime/tools.cpp b/flang/runtime/tools.cpp
index a027559d9f4a74..b46cec9239d428 100644
--- a/flang/runtime/tools.cpp
+++ b/flang/runtime/tools.cpp
@@ -173,5 +173,17 @@ RT_API_ATTRS void ShallowCopy(const Descriptor &to, const Descriptor &from) {
ShallowCopy(to, from, to.IsContiguous(), from.IsContiguous());
}
+RT_API_ATTRS const char *EnsureNullTerminated(
+ const char *str, size_t length, Terminator &terminator) {
+ if (length <= std::strlen(str)) {
+ char *newCmd{(char *)AllocateMemoryOrCrash(terminator, length + 1)};
+ std::memcpy(newCmd, str, length);
+ newCmd[length] = '\0';
+ return newCmd;
+ } else {
+ return str;
+ }
+}
+
RT_OFFLOAD_API_GROUP_END
} // namespace Fortran::runtime
diff --git a/flang/runtime/tools.h b/flang/runtime/tools.h
index 735acad728422b..30a9add01ffbce 100644
--- a/flang/runtime/tools.h
+++ b/flang/runtime/tools.h
@@ -411,17 +411,8 @@ RT_API_ATTRS void ShallowCopy(const Descriptor &to, const Descriptor &from,
bool toIsContiguous, bool fromIsContiguous);
RT_API_ATTRS void ShallowCopy(const Descriptor &to, const Descriptor &from);
-inline RT_API_ATTRS const char *EnsureNullTerminated(
- const char *str, size_t length, Terminator &terminator) {
- if (length <= std::strlen(str)) {
- char *newCmd{(char *)AllocateMemoryOrCrash(terminator, length + 1)};
- std::memcpy(newCmd, str, length);
- newCmd[length] = '\0';
- return newCmd;
- } else {
- return str;
- }
-}
+RT_API_ATTRS const char *EnsureNullTerminated(
+ const char *str, size_t length, Terminator &terminator);
} // namespace Fortran::runtime
#endif // FORTRAN_RUNTIME_TOOLS_H_
>From fb15f831b25286bb95d09968d181e92893608f18 Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Wed, 20 Dec 2023 16:04:42 +0000
Subject: [PATCH 09/13] free memoryin the end
---
flang/runtime/command.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/flang/runtime/command.cpp b/flang/runtime/command.cpp
index 4db7ed38e8d20a..11187b07fc267f 100644
--- a/flang/runtime/command.cpp
+++ b/flang/runtime/command.cpp
@@ -298,8 +298,8 @@ void RTNAME(System)(const Descriptor &command, const Descriptor *exitstat,
int exitstatVal{WEXITSTATUS(status)};
StoreLengthToDescriptor(exitstat, exitstatVal, terminator);
#endif
- FreeMemory((void *)newCmd);
}
+ FreeMemory((void *)newCmd);
}
} // namespace Fortran::runtime
>From d147dbb031083017ab0ee26f1ac2bf00275d6ef1 Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Fri, 29 Dec 2023 13:18:54 +0000
Subject: [PATCH 10/13] memory deallocation fixes, and use std::size_t
---
flang/runtime/command.cpp | 5 ++++-
flang/runtime/tools.cpp | 6 ++++--
flang/runtime/tools.h | 6 +++++-
3 files changed, 13 insertions(+), 4 deletions(-)
diff --git a/flang/runtime/command.cpp b/flang/runtime/command.cpp
index 11187b07fc267f..9a21313ecb2e6a 100644
--- a/flang/runtime/command.cpp
+++ b/flang/runtime/command.cpp
@@ -299,7 +299,10 @@ void RTNAME(System)(const Descriptor &command, const Descriptor *exitstat,
StoreLengthToDescriptor(exitstat, exitstatVal, terminator);
#endif
}
- FreeMemory((void *)newCmd);
+ // Deallocate memory if EnsureNullTerminated dynamically allocate a memory
+ if (newCmd != command.OffsetElement()) {
+ FreeMemory((void *)newCmd);
+ }
}
} // namespace Fortran::runtime
diff --git a/flang/runtime/tools.cpp b/flang/runtime/tools.cpp
index b46cec9239d428..c9b7a9922d6453 100644
--- a/flang/runtime/tools.cpp
+++ b/flang/runtime/tools.cpp
@@ -174,8 +174,10 @@ RT_API_ATTRS void ShallowCopy(const Descriptor &to, const Descriptor &from) {
}
RT_API_ATTRS const char *EnsureNullTerminated(
- const char *str, size_t length, Terminator &terminator) {
- if (length <= std::strlen(str)) {
+ const char *str, std::size_t length, Terminator &terminator) {
+ const void *nullTerminatorPos{std::memchr(str, '\0', length)};
+
+ if (nullTerminatorPos == nullptr) {
char *newCmd{(char *)AllocateMemoryOrCrash(terminator, length + 1)};
std::memcpy(newCmd, str, length);
newCmd[length] = '\0';
diff --git a/flang/runtime/tools.h b/flang/runtime/tools.h
index 30a9add01ffbce..003ac66e7c7c99 100644
--- a/flang/runtime/tools.h
+++ b/flang/runtime/tools.h
@@ -411,8 +411,12 @@ RT_API_ATTRS void ShallowCopy(const Descriptor &to, const Descriptor &from,
bool toIsContiguous, bool fromIsContiguous);
RT_API_ATTRS void ShallowCopy(const Descriptor &to, const Descriptor &from);
+// Ensures that a character string is null-terminated, allocating a /p length +1
+// size memory for null-terminator if necessary. Returns the original or a newly
+// allocated null-terminated string (responsibility for deallocation is on the
+// caller).
RT_API_ATTRS const char *EnsureNullTerminated(
- const char *str, size_t length, Terminator &terminator);
+ const char *str, std::size_t length, Terminator &terminator);
} // namespace Fortran::runtime
#endif // FORTRAN_RUNTIME_TOOLS_H_
>From cce16643a918059206f7d8d449aad5364b2e079d Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Tue, 16 Jan 2024 12:08:58 +0000
Subject: [PATCH 11/13] refactor: move SYSTEM from command.cpp to execute.cpp
---
.../flang/Optimizer/Builder/Runtime/Command.h | 5 ---
.../flang/Optimizer/Builder/Runtime/Execute.h | 5 +++
flang/include/flang/Runtime/command.h | 5 ---
flang/include/flang/Runtime/execute.h | 5 +++
.../lib/Optimizer/Builder/Runtime/Command.cpp | 13 ------
.../lib/Optimizer/Builder/Runtime/Execute.cpp | 13 ++++++
flang/runtime/command.cpp | 23 ----------
flang/runtime/execute.cpp | 23 ++++++++++
flang/runtime/tools.h | 44 -------------------
9 files changed, 46 insertions(+), 90 deletions(-)
diff --git a/flang/include/flang/Optimizer/Builder/Runtime/Command.h b/flang/include/flang/Optimizer/Builder/Runtime/Command.h
index 9d6a39639844fc..976fb3aa0b6fbb 100644
--- a/flang/include/flang/Optimizer/Builder/Runtime/Command.h
+++ b/flang/include/flang/Optimizer/Builder/Runtime/Command.h
@@ -53,10 +53,5 @@ mlir::Value genGetEnvVariable(fir::FirOpBuilder &, mlir::Location,
mlir::Value length, mlir::Value trimName,
mlir::Value errmsg);
-/// Generate a call to System runtime function which implements
-/// the non-standard System GNU extension.
-void genSystem(fir::FirOpBuilder &, mlir::Location, mlir::Value command,
- mlir::Value exitstat);
-
} // namespace fir::runtime
#endif // FORTRAN_OPTIMIZER_BUILDER_RUNTIME_COMMAND_H
diff --git a/flang/include/flang/Optimizer/Builder/Runtime/Execute.h b/flang/include/flang/Optimizer/Builder/Runtime/Execute.h
index a1e6ef20876049..d6621f00fafef7 100644
--- a/flang/include/flang/Optimizer/Builder/Runtime/Execute.h
+++ b/flang/include/flang/Optimizer/Builder/Runtime/Execute.h
@@ -31,5 +31,10 @@ void genExecuteCommandLine(fir::FirOpBuilder &, mlir::Location,
mlir::Value exitstat, mlir::Value cmdstat,
mlir::Value cmdmsg);
+/// Generate a call to System runtime function which implements
+/// the non-standard System GNU extension.
+void genSystem(fir::FirOpBuilder &, mlir::Location, mlir::Value command,
+ mlir::Value exitstat);
+
} // namespace fir::runtime
#endif // FORTRAN_OPTIMIZER_BUILDER_RUNTIME_EXECUTE_H
diff --git a/flang/include/flang/Runtime/command.h b/flang/include/flang/Runtime/command.h
index e477a2da1c5954..c67d171c8e2f1b 100644
--- a/flang/include/flang/Runtime/command.h
+++ b/flang/include/flang/Runtime/command.h
@@ -55,11 +55,6 @@ std::int32_t RTNAME(GetEnvVariable)(const Descriptor &name,
const Descriptor *value = nullptr, const Descriptor *length = nullptr,
bool trim_name = true, const Descriptor *errmsg = nullptr,
const char *sourceFile = nullptr, int line = 0);
-
-// Calls std::system()
-void RTNAME(System)(const Descriptor &command,
- const Descriptor *exitstat = nullptr, const char *sourceFile = nullptr,
- int line = 0);
}
} // namespace Fortran::runtime
diff --git a/flang/include/flang/Runtime/execute.h b/flang/include/flang/Runtime/execute.h
index ca137b9d1823c4..9d40910dccd679 100644
--- a/flang/include/flang/Runtime/execute.h
+++ b/flang/include/flang/Runtime/execute.h
@@ -23,6 +23,11 @@ void RTNAME(ExecuteCommandLine)(const Descriptor &command, bool wait = true,
const Descriptor *exitstat = nullptr, const Descriptor *cmdstat = nullptr,
const Descriptor *cmdmsg = nullptr, const char *sourceFile = nullptr,
int line = 0);
+
+// Calls std::system()
+void RTNAME(System)(const Descriptor &command,
+ const Descriptor *exitstat = nullptr, const char *sourceFile = nullptr,
+ int line = 0);
}
} // namespace Fortran::runtime
diff --git a/flang/lib/Optimizer/Builder/Runtime/Command.cpp b/flang/lib/Optimizer/Builder/Runtime/Command.cpp
index 00af35a74bf871..1d719e7bbd9a2d 100644
--- a/flang/lib/Optimizer/Builder/Runtime/Command.cpp
+++ b/flang/lib/Optimizer/Builder/Runtime/Command.cpp
@@ -88,16 +88,3 @@ mlir::Value fir::runtime::genGetEnvVariable(fir::FirOpBuilder &builder,
sourceFile, sourceLine);
return builder.create<fir::CallOp>(loc, runtimeFunc, args).getResult(0);
}
-
-void fir::runtime::genSystem(fir::FirOpBuilder &builder, mlir::Location loc,
- mlir::Value command, mlir::Value exitstat) {
- auto runtimeFunc =
- fir::runtime::getRuntimeFunc<mkRTKey(System)>(loc, builder);
- mlir::FunctionType runtimeFuncTy = runtimeFunc.getFunctionType();
- mlir::Value sourceFile = fir::factory::locationToFilename(builder, loc);
- mlir::Value sourceLine =
- fir::factory::locationToLineNo(builder, loc, runtimeFuncTy.getInput(3));
- llvm::SmallVector<mlir::Value> args = fir::runtime::createArguments(
- builder, loc, runtimeFuncTy, command, exitstat, sourceFile, sourceLine);
- builder.create<fir::CallOp>(loc, runtimeFunc, args);
-}
diff --git a/flang/lib/Optimizer/Builder/Runtime/Execute.cpp b/flang/lib/Optimizer/Builder/Runtime/Execute.cpp
index 71ee3996ac0da7..ce16b5de65e293 100644
--- a/flang/lib/Optimizer/Builder/Runtime/Execute.cpp
+++ b/flang/lib/Optimizer/Builder/Runtime/Execute.cpp
@@ -42,3 +42,16 @@ void fir::runtime::genExecuteCommandLine(fir::FirOpBuilder &builder,
sourceFile, sourceLine);
builder.create<fir::CallOp>(loc, runtimeFunc, args);
}
+
+void fir::runtime::genSystem(fir::FirOpBuilder &builder, mlir::Location loc,
+ mlir::Value command, mlir::Value exitstat) {
+ auto runtimeFunc =
+ fir::runtime::getRuntimeFunc<mkRTKey(System)>(loc, builder);
+ mlir::FunctionType runtimeFuncTy = runtimeFunc.getFunctionType();
+ mlir::Value sourceFile = fir::factory::locationToFilename(builder, loc);
+ mlir::Value sourceLine =
+ fir::factory::locationToLineNo(builder, loc, runtimeFuncTy.getInput(3));
+ llvm::SmallVector<mlir::Value> args = fir::runtime::createArguments(
+ builder, loc, runtimeFuncTy, command, exitstat, sourceFile, sourceLine);
+ builder.create<fir::CallOp>(loc, runtimeFunc, args);
+}
diff --git a/flang/runtime/command.cpp b/flang/runtime/command.cpp
index 5b2837c96f694a..7c44890545bd3f 100644
--- a/flang/runtime/command.cpp
+++ b/flang/runtime/command.cpp
@@ -241,27 +241,4 @@ std::int32_t RTNAME(GetEnvVariable)(const Descriptor &name,
return StatOk;
}
-void RTNAME(System)(const Descriptor &command, const Descriptor *exitstat,
- const char *sourceFile, int line) {
- Terminator terminator{sourceFile, line};
-
- const char *newCmd{EnsureNullTerminated(
- command.OffsetElement(), command.ElementBytes(), terminator)};
- int status{std::system(newCmd)};
-
- if (exitstat) {
- RUNTIME_CHECK(terminator, IsValidIntDescriptor(exitstat));
-#ifdef _WIN32
- StoreLengthToDescriptor(exitstat, status, terminator);
-#else
- int exitstatVal{WEXITSTATUS(status)};
- StoreLengthToDescriptor(exitstat, exitstatVal, terminator);
-#endif
- }
- // Deallocate memory if EnsureNullTerminated dynamically allocate a memory
- if (newCmd != command.OffsetElement()) {
- FreeMemory((void *)newCmd);
- }
-}
-
} // namespace Fortran::runtime
diff --git a/flang/runtime/execute.cpp b/flang/runtime/execute.cpp
index d38cb8384bc864..a2791a8e155207 100644
--- a/flang/runtime/execute.cpp
+++ b/flang/runtime/execute.cpp
@@ -204,4 +204,27 @@ void RTNAME(ExecuteCommandLine)(const Descriptor &command, bool wait,
}
}
+void RTNAME(System)(const Descriptor &command, const Descriptor *exitstat,
+ const char *sourceFile, int line) {
+ Terminator terminator{sourceFile, line};
+
+ char *newCmd{EnsureNullTerminated(
+ command.OffsetElement(), command.ElementBytes(), terminator)};
+ int status{std::system(newCmd)};
+
+ if (exitstat) {
+ RUNTIME_CHECK(terminator, IsValidIntDescriptor(exitstat));
+#ifdef _WIN32
+ StoreLengthToDescriptor(exitstat, status, terminator);
+#else
+ int exitstatVal{WEXITSTATUS(status)};
+ StoreIntToDescriptor(exitstat, exitstatVal, terminator);
+#endif
+ }
+ // Deallocate memory if EnsureNullTerminated dynamically allocate a memory
+ if (newCmd != command.OffsetElement()) {
+ FreeMemory(static_cast<char *>(newCmd));
+ }
+}
+
} // namespace Fortran::runtime
diff --git a/flang/runtime/tools.h b/flang/runtime/tools.h
index 514a0a926f7541..86c06149dc4e4a 100644
--- a/flang/runtime/tools.h
+++ b/flang/runtime/tools.h
@@ -481,50 +481,6 @@ RT_API_ATTRS void CopyAndPad(
}
}
-// Ensures that a character string is null-terminated, allocating a /p length +1
-// size memory for null-terminator if necessary. Returns the original or a newly
-// allocated null-terminated string (responsibility for deallocation is on the
-// caller).
-RT_API_ATTRS const char *EnsureNullTerminated(
- const char *str, std::size_t length, Terminator &terminator);
-
-RT_API_ATTRS bool IsValidCharDescriptor(const Descriptor *value);
-
-RT_API_ATTRS bool IsValidIntDescriptor(const Descriptor *intVal);
-
-// Copy a null-terminated character array \p rawValue to descriptor \p value.
-// The copy starts at the given \p offset, if not present then start at 0.
-// If descriptor `errmsg` is provided, error messages will be stored to it.
-// Returns stats specified in standard.
-RT_API_ATTRS std::int32_t CopyCharsToDescriptor(const Descriptor &value,
- const char *rawValue, std::size_t rawValueLength,
- const Descriptor *errmsg = nullptr, std::size_t offset = 0);
-
-RT_API_ATTRS void StoreIntToDescriptor(
- const Descriptor *length, std::int64_t value, Terminator &terminator);
-
-// Defines a utility function for copying and padding characters
-template <typename TO, typename FROM>
-RT_API_ATTRS void CopyAndPad(
- TO *to, const FROM *from, std::size_t toChars, std::size_t fromChars) {
- if constexpr (sizeof(TO) != sizeof(FROM)) {
- std::size_t copyChars{std::min(toChars, fromChars)};
- for (std::size_t j{0}; j < copyChars; ++j) {
- to[j] = from[j];
- }
- for (std::size_t j{copyChars}; j < toChars; ++j) {
- to[j] = static_cast<TO>(' ');
- }
- } else if (toChars <= fromChars) {
- std::memcpy(to, from, toChars * sizeof(TO));
- } else {
- std::memcpy(to, from, std::min(toChars, fromChars) * sizeof(TO));
- for (std::size_t j{fromChars}; j < toChars; ++j) {
- to[j] = static_cast<TO>(' ');
- }
- }
-}
-
} // namespace Fortran::runtime
#endif // FORTRAN_RUNTIME_TOOLS_H_
>From 31c3a42490548d702edd5fffb34eaebf9ad0737c Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Tue, 16 Jan 2024 12:14:33 +0000
Subject: [PATCH 12/13] clang format and change exitstat type from AnyInt to
DefaultInt to match gfortran gnu standard
---
flang/lib/Evaluate/intrinsics.cpp | 2 +-
flang/runtime/tools.h | 1 -
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/flang/lib/Evaluate/intrinsics.cpp b/flang/lib/Evaluate/intrinsics.cpp
index 639db1808f126d..068b3cfafcbc11 100644
--- a/flang/lib/Evaluate/intrinsics.cpp
+++ b/flang/lib/Evaluate/intrinsics.cpp
@@ -1389,7 +1389,7 @@ static const IntrinsicInterface intrinsicSubroutine[]{
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
{"system",
{{"command", DefaultChar, Rank::scalar},
- {"exitstat", AnyInt, Rank::scalar, Optionality::optional,
+ {"exitstat", DefaultInt, Rank::scalar, Optionality::optional,
common::Intent::InOut}},
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
{"system_clock",
diff --git a/flang/runtime/tools.h b/flang/runtime/tools.h
index 86c06149dc4e4a..89e5069995748b 100644
--- a/flang/runtime/tools.h
+++ b/flang/runtime/tools.h
@@ -481,6 +481,5 @@ RT_API_ATTRS void CopyAndPad(
}
}
-
} // namespace Fortran::runtime
#endif // FORTRAN_RUNTIME_TOOLS_H_
>From 25c26f97d28e5a793e8469c5ce2a3534164eb93d Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Tue, 16 Jan 2024 14:21:31 +0000
Subject: [PATCH 13/13] Windows fixes
---
flang/runtime/execute.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/flang/runtime/execute.cpp b/flang/runtime/execute.cpp
index a2791a8e155207..1aef04ebfc2a02 100644
--- a/flang/runtime/execute.cpp
+++ b/flang/runtime/execute.cpp
@@ -215,13 +215,13 @@ void RTNAME(System)(const Descriptor &command, const Descriptor *exitstat,
if (exitstat) {
RUNTIME_CHECK(terminator, IsValidIntDescriptor(exitstat));
#ifdef _WIN32
- StoreLengthToDescriptor(exitstat, status, terminator);
+ StoreIntToDescriptor(exitstat, status, terminator);
#else
int exitstatVal{WEXITSTATUS(status)};
StoreIntToDescriptor(exitstat, exitstatVal, terminator);
#endif
}
- // Deallocate memory if EnsureNullTerminated dynamically allocate a memory
+ // Deallocate memory if EnsureNullTerminated dynamically allocated memory
if (newCmd != command.OffsetElement()) {
FreeMemory(static_cast<char *>(newCmd));
}
More information about the flang-commits
mailing list