Skip to content

Commit e2b896a

Browse files
yiwu0b11jeffhammondYi Wu
authored
[flang] Add EXECUTE_COMMAND_LINE runtime and lowering intrinsics implementation (#74077)
This patch add support of intrinsics Fortran 2008 EXECUTE_COMMAND_LINE. The patch contains both the lowering and the runtime code and works on both Windows and Linux. The patch contains a list of commits, to convey the authorship and the history of changes. Some implementation specifics or status has been added to `flang/docs/Intrinsics.md`. I have provided a summary of the usage and the options required for the `EXECUTE_COMMAND_LINE intrinsic`. The intrinsic supports both a synchronous (by default) and an asynchronous option. | System | Mode | Implemention | |---------|-------|---------------------------| | Linux | Sync | std::system() | | Windows | Sync | std::system() | | Linux | Async | fork() | | Windows | Async | CreateProcess | Support for the SYSTEM GNU extension will be added in a separate PR. Co-authored with @jeffhammond --------- Signed-off-by: Jeff Hammond <[email protected]> Co-authored-by: Jeff Hammond <[email protected]> Co-authored-by: Yi Wu <[email protected]>
1 parent a26cc75 commit e2b896a

File tree

15 files changed

+719
-55
lines changed

15 files changed

+719
-55
lines changed

flang/docs/Intrinsics.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -841,3 +841,48 @@ TRIM, UBOUND, UNPACK, VERIFY.
841841

842842
Coarray, non standard, IEEE and ISO_C_BINDINGS intrinsic functions that can be
843843
used in constant expressions have currently no folding support at all.
844+
845+
### Standard Intrinsics: EXECUTE_COMMAND_LINE
846+
847+
#### Usage and Info
848+
849+
- **Standard:** Fortran 2008 and later, specified in 16.9.73
850+
- **Class:** Subroutine
851+
- **Syntax:** `CALL EXECUTE_COMMAND_LINE(COMMAND [, WAIT, EXITSTAT, CMDSTAT, CMDMSG ])`
852+
- **Arguments:**
853+
854+
| Argument | Description |
855+
|-----------|--------------------------------------------------------------|
856+
| `COMMAND` | Shall be a default CHARACTER scalar. |
857+
| `WAIT` | (Optional) Shall be a default LOGICAL scalar. |
858+
| `EXITSTAT`| (Optional) Shall be an INTEGER of the default kind. |
859+
| `CMDSTAT` | (Optional) Shall be an INTEGER of the default kind. |
860+
| `CMDMSG` | (Optional) Shall be a CHARACTER scalar of the default kind. |
861+
862+
#### Implementation Specifics
863+
864+
- **`COMMAND`:**
865+
- Must be preset.
866+
867+
- **`WAIT`:**
868+
- If set to `false`, the command is executed asynchronously. If not preset or set to `false`, it is executed synchronously.
869+
- Sync: achieved by passing command into `std::system` on all systems.
870+
- Async: achieved by calling a `fork()` on POSIX-compatible systems, or `CreateProcess()` on Windows.
871+
872+
- **`CMDSTAT`:**
873+
- -2: No error condition occurs, but `WAIT` is present with the value `false`, and the processor does not support asynchronous execution.
874+
- -1: The processor does not support command line execution.
875+
- \+ (positive value): An error condition occurs.
876+
- 1: Fork Error, where `pid_t < 0`, would only occur on POSIX-compatible systems.
877+
- 2: Execution Error, a command exits with status -1.
878+
- 3: Invalid Command Error, determined by the exit code depending on the system.
879+
- On Windows, if the exit code is 1.
880+
- On POSIX-compatible systems, if the exit code is 127 or 126.
881+
- 4: Signal error, either it is stopped or killed by signal, would only occur on POSIX-compatible systems.
882+
- 0: Otherwise.
883+
884+
- **`CMDMSG`:**
885+
- If an error condition occurs, it is assigned an explanatory message. Otherwise, it remains unchanged.
886+
- If a condition occurs that would assign a nonzero value to `CMDSTAT` but the `CMDSTAT` variable is not present, error termination is initiated.
887+
- On POSIX-compatible systems, this applies to both synchronous and asynchronous error termination. When the execution mode is set to async with error termination, the child process (async process) will be terminated with no effect on the parent process (continues).
888+
- On Windows, this only applies to synchronous error termination.

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ struct IntrinsicLibrary {
214214
mlir::Value genDshiftr(mlir::Type, llvm::ArrayRef<mlir::Value>);
215215
fir::ExtendedValue genEoshift(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
216216
void genExit(llvm::ArrayRef<fir::ExtendedValue>);
217+
void genExecuteCommandLine(mlir::ArrayRef<fir::ExtendedValue> args);
217218
mlir::Value genExponent(mlir::Type, llvm::ArrayRef<mlir::Value>);
218219
fir::ExtendedValue genExtendsTypeOf(mlir::Type,
219220
llvm::ArrayRef<fir::ExtendedValue>);
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//===-- Command.cpp -- generate command line runtime API calls ------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef FORTRAN_OPTIMIZER_BUILDER_RUNTIME_EXECUTE_H
10+
#define FORTRAN_OPTIMIZER_BUILDER_RUNTIME_EXECUTE_H
11+
12+
namespace mlir {
13+
class Value;
14+
class Location;
15+
} // namespace mlir
16+
17+
namespace fir {
18+
class FirOpBuilder;
19+
} // namespace fir
20+
21+
namespace fir::runtime {
22+
23+
/// Generate a call to the ExecuteCommandLine runtime function which implements
24+
/// the GET_EXECUTE_ARGUMENT intrinsic.
25+
/// \p wait must be bool that can be absent.
26+
/// \p exitstat, \p cmdstat and \p cmdmsg must be fir.box that can be
27+
/// absent (but not null mlir values). The status exitstat and cmdstat are
28+
/// returned, along with the message cmdmsg.
29+
void genExecuteCommandLine(fir::FirOpBuilder &, mlir::Location,
30+
mlir::Value command, mlir::Value wait,
31+
mlir::Value exitstat, mlir::Value cmdstat,
32+
mlir::Value cmdmsg);
33+
34+
} // namespace fir::runtime
35+
#endif // FORTRAN_OPTIMIZER_BUILDER_RUNTIME_EXECUTE_H

flang/include/flang/Runtime/execute.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//===-- include/flang/Runtime/command.h -------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef FORTRAN_RUNTIME_EXECUTE_H_
10+
#define FORTRAN_RUNTIME_EXECUTE_H_
11+
12+
#include "flang/Runtime/entry-names.h"
13+
14+
namespace Fortran::runtime {
15+
class Descriptor;
16+
17+
extern "C" {
18+
19+
// 16.9.83 EXECUTE_COMMAND_LINE
20+
// Execute a command line.
21+
// Returns a EXITSTAT, CMDSTAT, and CMDMSG as described in the standard.
22+
void RTNAME(ExecuteCommandLine)(const Descriptor &command, bool wait = true,
23+
const Descriptor *exitstat = nullptr, const Descriptor *cmdstat = nullptr,
24+
const Descriptor *cmdmsg = nullptr, const char *sourceFile = nullptr,
25+
int line = 0);
26+
}
27+
} // namespace Fortran::runtime
28+
29+
#endif // FORTRAN_RUNTIME_EXECUTE_H_

flang/lib/Optimizer/Builder/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ add_flang_library(FIRBuilder
2020
Runtime/Derived.cpp
2121
Runtime/EnvironmentDefaults.cpp
2222
Runtime/Exceptions.cpp
23+
Runtime/Execute.cpp
2324
Runtime/Inquiry.cpp
2425
Runtime/Intrinsics.cpp
2526
Runtime/Numeric.cpp

flang/lib/Optimizer/Builder/IntrinsicCall.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "flang/Optimizer/Builder/Runtime/Command.h"
2727
#include "flang/Optimizer/Builder/Runtime/Derived.h"
2828
#include "flang/Optimizer/Builder/Runtime/Exceptions.h"
29+
#include "flang/Optimizer/Builder/Runtime/Execute.h"
2930
#include "flang/Optimizer/Builder/Runtime/Inquiry.h"
3031
#include "flang/Optimizer/Builder/Runtime/Intrinsics.h"
3132
#include "flang/Optimizer/Builder/Runtime/Numeric.h"
@@ -213,6 +214,14 @@ static constexpr IntrinsicHandler handlers[]{
213214
{"boundary", asBox, handleDynamicOptional},
214215
{"dim", asValue}}},
215216
/*isElemental=*/false},
217+
{"execute_command_line",
218+
&I::genExecuteCommandLine,
219+
{{{"command", asBox},
220+
{"wait", asValue, handleDynamicOptional},
221+
{"exitstat", asBox, handleDynamicOptional},
222+
{"cmdstat", asBox, handleDynamicOptional},
223+
{"cmdmsg", asBox, handleDynamicOptional}}},
224+
/*isElemental=*/false},
216225
{"exit",
217226
&I::genExit,
218227
{{{"status", asValue, handleDynamicOptional}}},
@@ -2901,6 +2910,40 @@ IntrinsicLibrary::genEoshift(mlir::Type resultType,
29012910
return readAndAddCleanUp(resultMutableBox, resultType, "EOSHIFT");
29022911
}
29032912

2913+
// EXECUTE_COMMAND_LINE
2914+
void IntrinsicLibrary::genExecuteCommandLine(
2915+
llvm::ArrayRef<fir::ExtendedValue> args) {
2916+
assert(args.size() == 5);
2917+
mlir::Value command = fir::getBase(args[0]);
2918+
const fir::ExtendedValue &wait = args[1];
2919+
const fir::ExtendedValue &exitstat = args[2];
2920+
const fir::ExtendedValue &cmdstat = args[3];
2921+
const fir::ExtendedValue &cmdmsg = args[4];
2922+
2923+
if (!command)
2924+
fir::emitFatalError(loc, "expected COMMAND parameter");
2925+
2926+
mlir::Type boxNoneTy = fir::BoxType::get(builder.getNoneType());
2927+
2928+
mlir::Value waitBool = isStaticallyPresent(wait)
2929+
? fir::getBase(wait)
2930+
: builder.createBool(loc, true);
2931+
mlir::Value exitstatBox =
2932+
isStaticallyPresent(exitstat)
2933+
? fir::getBase(exitstat)
2934+
: builder.create<fir::AbsentOp>(loc, boxNoneTy).getResult();
2935+
mlir::Value cmdstatBox =
2936+
isStaticallyPresent(cmdstat)
2937+
? fir::getBase(cmdstat)
2938+
: builder.create<fir::AbsentOp>(loc, boxNoneTy).getResult();
2939+
mlir::Value cmdmsgBox =
2940+
isStaticallyPresent(cmdmsg)
2941+
? fir::getBase(cmdmsg)
2942+
: builder.create<fir::AbsentOp>(loc, boxNoneTy).getResult();
2943+
fir::runtime::genExecuteCommandLine(builder, loc, command, waitBool,
2944+
exitstatBox, cmdstatBox, cmdmsgBox);
2945+
}
2946+
29042947
// EXIT
29052948
void IntrinsicLibrary::genExit(llvm::ArrayRef<fir::ExtendedValue> args) {
29062949
assert(args.size() == 1);
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//===-- Execute.cpp -- generate command line runtime API calls ------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "flang/Optimizer/Builder/Runtime/Execute.h"
10+
#include "flang/Optimizer/Builder/FIRBuilder.h"
11+
#include "flang/Optimizer/Builder/Runtime/RTBuilder.h"
12+
#include "flang/Runtime/execute.h"
13+
14+
using namespace Fortran::runtime;
15+
16+
// Certain runtime intrinsics should only be run when select parameters of the
17+
// intrisic are supplied. In certain cases one of these parameters may not be
18+
// given, however the intrinsic needs to be run due to another required
19+
// parameter being supplied. In this case the missing parameter is assigned to
20+
// have an "absent" value. This typically happens in IntrinsicCall.cpp. For this
21+
// reason the extra indirection with `isAbsent` is needed for testing whether a
22+
// given parameter is actually present (so that parameters with "value" absent
23+
// are not considered as present).
24+
inline bool isAbsent(mlir::Value val) {
25+
return mlir::isa_and_nonnull<fir::AbsentOp>(val.getDefiningOp());
26+
}
27+
28+
void fir::runtime::genExecuteCommandLine(fir::FirOpBuilder &builder,
29+
mlir::Location loc,
30+
mlir::Value command, mlir::Value wait,
31+
mlir::Value exitstat,
32+
mlir::Value cmdstat,
33+
mlir::Value cmdmsg) {
34+
auto runtimeFunc =
35+
fir::runtime::getRuntimeFunc<mkRTKey(ExecuteCommandLine)>(loc, builder);
36+
mlir::FunctionType runtimeFuncTy = runtimeFunc.getFunctionType();
37+
mlir::Value sourceFile = fir::factory::locationToFilename(builder, loc);
38+
mlir::Value sourceLine =
39+
fir::factory::locationToLineNo(builder, loc, runtimeFuncTy.getInput(6));
40+
llvm::SmallVector<mlir::Value> args = fir::runtime::createArguments(
41+
builder, loc, runtimeFuncTy, command, wait, exitstat, cmdstat, cmdmsg,
42+
sourceFile, sourceLine);
43+
builder.create<fir::CallOp>(loc, runtimeFunc, args);
44+
}

flang/runtime/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ set(sources
105105
edit-output.cpp
106106
environment.cpp
107107
exceptions.cpp
108+
execute.cpp
108109
extensions.cpp
109110
extrema.cpp
110111
file.cpp

0 commit comments

Comments
 (0)