[flang-commits] [flang] [flang] Add EXECUTE_COMMAND_LINE runtime and lowering intrinsics implementation (PR #74077)

via flang-commits flang-commits at lists.llvm.org
Wed Dec 6 03:37:02 PST 2023


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-flang-runtime

Author: Yi Wu (yi-wu-arm)

<details>
<summary>Changes</summary>

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



---

Patch is 34.87 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/74077.diff


12 Files Affected:

- (modified) flang/docs/Intrinsics.md (+45) 
- (modified) flang/include/flang/Optimizer/Builder/IntrinsicCall.h (+1) 
- (added) flang/include/flang/Optimizer/Builder/Runtime/Execute.h (+34) 
- (added) flang/include/flang/Runtime/execute.h (+31) 
- (modified) flang/lib/Optimizer/Builder/CMakeLists.txt (+1) 
- (modified) flang/lib/Optimizer/Builder/IntrinsicCall.cpp (+43) 
- (added) flang/lib/Optimizer/Builder/Runtime/Execute.cpp (+44) 
- (modified) flang/runtime/CMakeLists.txt (+1) 
- (added) flang/runtime/execute.cpp (+246) 
- (added) flang/test/Lower/Intrinsics/execute_command_line-optional.f90 (+51) 
- (added) flang/test/Lower/Intrinsics/execute_command_line.f90 (+50) 
- (modified) flang/unittests/Runtime/CommandTest.cpp (+108) 


``````````diff
diff --git a/flang/docs/Intrinsics.md b/flang/docs/Intrinsics.md
index fef2b4ea4dd8c..40445526314b0 100644
--- a/flang/docs/Intrinsics.md
+++ b/flang/docs/Intrinsics.md
@@ -835,3 +835,48 @@ TRIM, UBOUND, UNPACK, VERIFY.
 
 Coarray, non standard, IEEE and ISO_C_BINDINGS intrinsic functions that can be
 used in constant expressions have currently no folding support at all.
+
+### Standard Intrinsics: EXECUTE_COMMAND_LINE
+
+#### Usage and Info
+
+- **Standard:** Fortran 2008 and later, specified in 16.9.73
+- **Class:** Subroutine
+- **Syntax:** `CALL EXECUTE_COMMAND_LINE(COMMAND [, WAIT, EXITSTAT, CMDSTAT, CMDMSG ])`
+- **Arguments:**
+
+  | Argument  | Description                                                  |
+  |-----------|--------------------------------------------------------------|
+  | `COMMAND` | Shall be a default CHARACTER scalar.                         |
+  | `WAIT`    | (Optional) Shall be a default LOGICAL scalar.                |
+  | `EXITSTAT`| (Optional) Shall be an INTEGER of the default kind.          |
+  | `CMDSTAT` | (Optional) Shall be an INTEGER of the default kind.          |
+  | `CMDMSG`  | (Optional) Shall be a CHARACTER scalar of the default kind.  |
+
+#### Implementation Specifics
+
+- **`COMMAND`:**
+  - Must be preset.
+
+- **`WAIT`:**
+  - If set to `false`, the command is executed asynchronously. If not preset or set to `false`, it is executed synchronously.
+  - Sync: achieved by passing command into `std::system` on all systems.
+  - Async: achieved by calling a `fork()` on POSIX-compatible systems, or `CreateProcess()` on Windows.
+
+- **`CMDSTAT`:**
+  - -2: No error condition occurs, but `WAIT` is present with the value `false`, and the processor does not support asynchronous execution.
+  - -1: The processor does not support command line execution.
+  - \+ (positive value): An error condition occurs.
+    - 1: Fork Error, where `pid_t < 0`, would only occur on POSIX-compatible systems.
+    - 2: Execution Error, a command exits with status -1.
+    - 3: Invalid Command Error, determined by the exit code depending on the system.
+      - On Windows, if the exit code is 1.
+      - On POSIX-compatible systems, if the exit code is 127 or 126.
+    - 4: Signal error, either it is stopped or killed by signal, would only occur on POSIX-compatible systems.
+  - 0: Otherwise.
+
+- **`CMDMSG`:**
+  - If an error condition occurs, it is assigned an explanatory message. Otherwise, it remains unchanged.
+  - If a condition occurs that would assign a nonzero value to `CMDSTAT` but the `CMDSTAT` variable is not present, error termination is initiated.
+    - 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).
+    - On Windows, this only applies to synchronous error termination.
diff --git a/flang/include/flang/Optimizer/Builder/IntrinsicCall.h b/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
index 5065f11ae9e72..543f587697ed5 100644
--- a/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
+++ b/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
@@ -214,6 +214,7 @@ struct IntrinsicLibrary {
   mlir::Value genDshiftr(mlir::Type, llvm::ArrayRef<mlir::Value>);
   fir::ExtendedValue genEoshift(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
   void genExit(llvm::ArrayRef<fir::ExtendedValue>);
+  void genExecuteCommandLine(mlir::ArrayRef<fir::ExtendedValue> args);
   mlir::Value genExponent(mlir::Type, llvm::ArrayRef<mlir::Value>);
   fir::ExtendedValue genExtendsTypeOf(mlir::Type,
                                       llvm::ArrayRef<fir::ExtendedValue>);
diff --git a/flang/include/flang/Optimizer/Builder/Runtime/Execute.h b/flang/include/flang/Optimizer/Builder/Runtime/Execute.h
new file mode 100644
index 0000000000000..f660419d70304
--- /dev/null
+++ b/flang/include/flang/Optimizer/Builder/Runtime/Execute.h
@@ -0,0 +1,34 @@
+//===-- Command.cpp -- generate command line runtime API calls ------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FORTRAN_OPTIMIZER_BUILDER_RUNTIME_EXECUTE_H
+#define FORTRAN_OPTIMIZER_BUILDER_RUNTIME_EXECUTE_H
+
+namespace mlir {
+class Value;
+class Location;
+} // namespace mlir
+
+namespace fir {
+class FirOpBuilder;
+} // namespace fir
+
+namespace fir::runtime {
+
+/// Generate a call to the ExecuteCommandLine runtime function which implements
+/// the GET_EXECUTE_ARGUMENT intrinsic.
+/// \p wait, \p exitstat, \p cmdstat and \p cmdmsg must be fir.box that can be
+/// absent (but not null mlir values). The status exitstat and cmdstat are
+/// returned, along with the message cmdmsg.
+void genExecuteCommandLine(fir::FirOpBuilder &, mlir::Location,
+                           mlir::Value command, mlir::Value wait,
+                           mlir::Value exitstat, mlir::Value cmdstat,
+                           mlir::Value cmdmsg);
+
+} // namespace fir::runtime
+#endif // FORTRAN_OPTIMIZER_BUILDER_RUNTIME_EXECUTE_H
diff --git a/flang/include/flang/Runtime/execute.h b/flang/include/flang/Runtime/execute.h
new file mode 100644
index 0000000000000..0c8086fa8ecf0
--- /dev/null
+++ b/flang/include/flang/Runtime/execute.h
@@ -0,0 +1,31 @@
+//===-- include/flang/Runtime/command.h -------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FORTRAN_RUNTIME_EXECUTE_H_
+#define FORTRAN_RUNTIME_EXECUTE_H_
+
+#include "flang/Runtime/entry-names.h"
+
+#include <cstdint>
+
+namespace Fortran::runtime {
+class Descriptor;
+
+extern "C" {
+
+// 16.9.83 EXECUTE_COMMAND_LINE
+// Execute a command line.
+// Returns a EXITSTAT, CMDSTAT, and CMDMSG as described in the standard.
+void RTNAME(ExecuteCommandLine)(const Descriptor *command = nullptr,
+    bool wait = true, const Descriptor *exitstat = nullptr,
+    const Descriptor *cmdstat = nullptr, const Descriptor *cmdmsg = nullptr,
+    const char *sourceFile = nullptr, int line = 0);
+}
+} // namespace Fortran::runtime
+
+#endif // FORTRAN_RUNTIME_EXECUTE_H_
diff --git a/flang/lib/Optimizer/Builder/CMakeLists.txt b/flang/lib/Optimizer/Builder/CMakeLists.txt
index 5e5daffd3ed7d..2d28b10f01fde 100644
--- a/flang/lib/Optimizer/Builder/CMakeLists.txt
+++ b/flang/lib/Optimizer/Builder/CMakeLists.txt
@@ -19,6 +19,7 @@ add_flang_library(FIRBuilder
   Runtime/Command.cpp
   Runtime/Derived.cpp
   Runtime/EnvironmentDefaults.cpp
+  Runtime/Execute.cpp
   Runtime/Inquiry.cpp
   Runtime/Intrinsics.cpp
   Runtime/Numeric.cpp
diff --git a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
index 24fdbe97856b3..9017ef2377871 100644
--- a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
+++ b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
@@ -25,6 +25,7 @@
 #include "flang/Optimizer/Builder/Runtime/Character.h"
 #include "flang/Optimizer/Builder/Runtime/Command.h"
 #include "flang/Optimizer/Builder/Runtime/Derived.h"
+#include "flang/Optimizer/Builder/Runtime/Execute.h"
 #include "flang/Optimizer/Builder/Runtime/Inquiry.h"
 #include "flang/Optimizer/Builder/Runtime/Intrinsics.h"
 #include "flang/Optimizer/Builder/Runtime/Numeric.h"
@@ -209,6 +210,14 @@ static constexpr IntrinsicHandler handlers[]{
        {"boundary", asBox, handleDynamicOptional},
        {"dim", asValue}}},
      /*isElemental=*/false},
+    {"execute_command_line",
+     &I::genExecuteCommandLine,
+     {{{"command", asBox},
+       {"wait", asValue, handleDynamicOptional},
+       {"exitstat", asBox, handleDynamicOptional},
+       {"cmdstat", asBox, handleDynamicOptional},
+       {"cmdmsg", asBox, handleDynamicOptional}}},
+     /*isElemental=*/false},
     {"exit",
      &I::genExit,
      {{{"status", asValue, handleDynamicOptional}}},
@@ -2776,6 +2785,40 @@ IntrinsicLibrary::genEoshift(mlir::Type resultType,
   return readAndAddCleanUp(resultMutableBox, resultType, "EOSHIFT");
 }
 
+// EXECUTE_COMMAND_LINE
+void IntrinsicLibrary::genExecuteCommandLine(
+    llvm::ArrayRef<fir::ExtendedValue> args) {
+  assert(args.size() == 5);
+  mlir::Value command = fir::getBase(args[0]);
+  const fir::ExtendedValue &wait = args[1];
+  const fir::ExtendedValue &exitstat = args[2];
+  const fir::ExtendedValue &cmdstat = args[3];
+  const fir::ExtendedValue &cmdmsg = args[4];
+
+  if (!command)
+    fir::emitFatalError(loc, "expected COMMAND parameter");
+
+  mlir::Type boxNoneTy = fir::BoxType::get(builder.getNoneType());
+
+  mlir::Value waitBool = isStaticallyPresent(wait)
+                             ? fir::getBase(wait)
+                             : builder.createBool(loc, true);
+  mlir::Value exitstatBox =
+      isStaticallyPresent(exitstat)
+          ? fir::getBase(exitstat)
+          : builder.create<fir::AbsentOp>(loc, boxNoneTy).getResult();
+  mlir::Value cmdstatBox =
+      isStaticallyPresent(cmdstat)
+          ? fir::getBase(cmdstat)
+          : builder.create<fir::AbsentOp>(loc, boxNoneTy).getResult();
+  mlir::Value cmdmsgBox =
+      isStaticallyPresent(cmdmsg)
+          ? fir::getBase(cmdmsg)
+          : builder.create<fir::AbsentOp>(loc, boxNoneTy).getResult();
+  fir::runtime::genExecuteCommandLine(builder, loc, command, waitBool,
+                                      exitstatBox, cmdstatBox, cmdmsgBox);
+}
+
 // EXIT
 void IntrinsicLibrary::genExit(llvm::ArrayRef<fir::ExtendedValue> args) {
   assert(args.size() == 1);
diff --git a/flang/lib/Optimizer/Builder/Runtime/Execute.cpp b/flang/lib/Optimizer/Builder/Runtime/Execute.cpp
new file mode 100644
index 0000000000000..71ee3996ac0da
--- /dev/null
+++ b/flang/lib/Optimizer/Builder/Runtime/Execute.cpp
@@ -0,0 +1,44 @@
+//===-- Execute.cpp -- generate command line runtime API calls ------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "flang/Optimizer/Builder/Runtime/Execute.h"
+#include "flang/Optimizer/Builder/FIRBuilder.h"
+#include "flang/Optimizer/Builder/Runtime/RTBuilder.h"
+#include "flang/Runtime/execute.h"
+
+using namespace Fortran::runtime;
+
+// Certain runtime intrinsics should only be run when select parameters of the
+// intrisic are supplied. In certain cases one of these parameters may not be
+// given, however the intrinsic needs to be run due to another required
+// parameter being supplied. In this case the missing parameter is assigned to
+// have an "absent" value. This typically happens in IntrinsicCall.cpp. For this
+// reason the extra indirection with `isAbsent` is needed for testing whether a
+// given parameter is actually present (so that parameters with "value" absent
+// are not considered as present).
+inline bool isAbsent(mlir::Value val) {
+  return mlir::isa_and_nonnull<fir::AbsentOp>(val.getDefiningOp());
+}
+
+void fir::runtime::genExecuteCommandLine(fir::FirOpBuilder &builder,
+                                         mlir::Location loc,
+                                         mlir::Value command, mlir::Value wait,
+                                         mlir::Value exitstat,
+                                         mlir::Value cmdstat,
+                                         mlir::Value cmdmsg) {
+  auto runtimeFunc =
+      fir::runtime::getRuntimeFunc<mkRTKey(ExecuteCommandLine)>(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(6));
+  llvm::SmallVector<mlir::Value> args = fir::runtime::createArguments(
+      builder, loc, runtimeFuncTy, command, wait, exitstat, cmdstat, cmdmsg,
+      sourceFile, sourceLine);
+  builder.create<fir::CallOp>(loc, runtimeFunc, args);
+}
diff --git a/flang/runtime/CMakeLists.txt b/flang/runtime/CMakeLists.txt
index 68ae97bed4e32..cd2bc51098d24 100644
--- a/flang/runtime/CMakeLists.txt
+++ b/flang/runtime/CMakeLists.txt
@@ -104,6 +104,7 @@ set(sources
   edit-input.cpp
   edit-output.cpp
   environment.cpp
+  execute.cpp
   extensions.cpp
   extrema.cpp
   file.cpp
diff --git a/flang/runtime/execute.cpp b/flang/runtime/execute.cpp
new file mode 100644
index 0000000000000..3a57194644a14
--- /dev/null
+++ b/flang/runtime/execute.cpp
@@ -0,0 +1,246 @@
+//===-- runtime/execute.cpp -----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "flang/Runtime/execute.h"
+#include "environment.h"
+#include "stat.h"
+#include "terminator.h"
+#include "tools.h"
+#include "flang/Runtime/descriptor.h"
+#include <cstdlib>
+#include <future>
+#include <limits>
+#ifdef _WIN32
+#define LEAN_AND_MEAN
+#define NOMINMAX
+#include <stdio.h>
+#include <windows.h>
+#else
+#include <signal.h>
+#include <unistd.h>
+#endif
+
+namespace Fortran::runtime {
+
+// cmdstat specified in 16.9.73
+// −1 if the processor does not support command line execution,
+// a processor-dependent positive value if an error condition occurs
+// −2 if no error condition occurs but WAIT is present with the value false
+// and the processor does not support asynchronous execution. Otherwise it is
+// assigned the value 0
+enum CMD_STAT {
+  ASYNC_NO_SUPPORT_ERR = -2,
+  NO_SUPPORT_ERR = -1,
+  CMD_EXECUTED = 0,
+  FORK_ERR = 1,
+  EXECL_ERR = 2,
+  INVALID_CL_ERR = 3,
+  SIGNAL_ERR = 4
+};
+
+static bool IsValidCharDescriptor(const Descriptor *value) {
+  return value && value->IsAllocated() &&
+      value->type() == TypeCode(TypeCategory::Character, 1) &&
+      value->rank() == 0;
+}
+
+static bool IsValidIntDescriptor(const Descriptor *length) {
+  auto typeCode{length->type().GetCategoryAndKind()};
+  // Check that our descriptor is allocated and is a scalar integer with
+  // kind != 1 (i.e. with a large enough decimal exponent range).
+  return length->IsAllocated() && length->rank() == 0 &&
+      length->type().IsInteger() && typeCode && typeCode->second != 1;
+}
+
+void CopyToDescriptor(const Descriptor &value, const char *rawValue,
+    std::int64_t rawValueLength, std::size_t offset = 0) {
+  std::int64_t toCopy{std::min(rawValueLength,
+      static_cast<std::int64_t>(value.ElementBytes() - offset))};
+
+  std::memcpy(value.OffsetElement(offset), rawValue, toCopy);
+}
+
+void CheckAndCopyToDescriptor(const Descriptor *value, const char *rawValue,
+    std::int64_t rawValueLength, std::size_t offset = 0) {
+  if (value) {
+    CopyToDescriptor(*value, rawValue, rawValueLength, offset);
+  }
+}
+
+static void StoreIntToDescriptor(
+    const Descriptor *intVal, std::int64_t value, Terminator &terminator) {
+  auto typeCode{intVal->type().GetCategoryAndKind()};
+  int kind{typeCode->second};
+  Fortran::runtime::ApplyIntegerKind<Fortran::runtime::StoreIntegerAt, void>(
+      kind, terminator, *intVal, /* atIndex = */ 0, value);
+}
+
+static void CheckAndStoreIntToDescriptor(
+    const Descriptor *intVal, std::int64_t value, Terminator &terminator) {
+  if (intVal) {
+    StoreIntToDescriptor(intVal, value, terminator);
+  }
+}
+
+template <int KIND> struct FitsInIntegerKind {
+  bool operator()([[maybe_unused]] std::int64_t value) {
+    if constexpr (KIND >= 8) {
+      return true;
+    } else {
+      return value <= std::numeric_limits<Fortran::runtime::CppTypeFor<
+                          Fortran::common::TypeCategory::Integer, KIND>>::max();
+    }
+  }
+};
+
+// If a condition occurs that would assign a nonzero value to CMDSTAT but
+// the CMDSTAT variable is not present, error termination is initiated.
+int TerminationCheck(int status, const Descriptor *command,
+    const Descriptor *cmdstat, const Descriptor *cmdmsg,
+    Terminator &terminator) {
+  if (status == -1) {
+    if (!cmdstat) {
+      terminator.Crash("Execution error with system status code: %d",
+          command->OffsetElement(), status);
+    } else {
+      CheckAndStoreIntToDescriptor(cmdstat, EXECL_ERR, terminator);
+      CopyToDescriptor(*cmdmsg, "Execution error", 15);
+    }
+  }
+#ifdef _WIN32
+  // On WIN32 API std::system returns exit status directly
+  int exitStatusVal{status};
+  if (exitStatusVal == 1) {
+#else
+  int exitStatusVal{WEXITSTATUS(status)};
+  if (exitStatusVal == 127 || exitStatusVal == 126) {
+#endif
+    if (!cmdstat) {
+      terminator.Crash(
+          "Invalid command quit with exit status code: %d", exitStatusVal);
+    } else {
+      CheckAndStoreIntToDescriptor(cmdstat, INVALID_CL_ERR, terminator);
+      CopyToDescriptor(*cmdmsg, "Invalid command line", 20);
+    }
+  }
+#if defined(WIFSIGNALED) && defined(WTERMSIG)
+  if (WIFSIGNALED(status)) {
+    if (!cmdstat) {
+      terminator.Crash("killed by signal: %d", WTERMSIG(status));
+    } else {
+      CheckAndStoreIntToDescriptor(cmdstat, SIGNAL_ERR, terminator);
+      CopyToDescriptor(*cmdmsg, "killed by signal", 18);
+    }
+  }
+#endif
+#if defined(WIFSTOPPED) && defined(WSTOPSIG)
+  if (WIFSTOPPED(status)) {
+    if (!cmdstat) {
+      terminator.Crash("stopped by signal: %d", WSTOPSIG(status));
+    } else {
+      CheckAndStoreIntToDescriptor(cmdstat, SIGNAL_ERR, terminator);
+      CopyToDescriptor(*cmdmsg, "stopped by signal", 17);
+    }
+  }
+#endif
+  return exitStatusVal;
+}
+
+void RTNAME(ExecuteCommandLine)(const Descriptor *command, bool wait,
+    const Descriptor *exitstat, const Descriptor *cmdstat,
+    const Descriptor *cmdmsg, const char *sourceFile, int line) {
+  Terminator terminator{sourceFile, line};
+
+  if (command) {
+    RUNTIME_CHECK(terminator, IsValidCharDescriptor(command));
+  }
+
+  if (exitstat) {
+    RUNTIME_CHECK(terminator, IsValidIntDescriptor(exitstat));
+    // If sync, assigned processor-dependent exit status. Otherwise unchanged
+  }
+
+  if (cmdstat) {
+    RUNTIME_CHECK(terminator, IsValidIntDescriptor(cmdstat));
+    // Assigned 0 as specifed in standard, if error then overwrite
+    StoreIntToDescriptor(cmdstat, CMD_EXECUTED, terminator);
+  }
+
+  if (cmdmsg) {
+    RUNTIME_CHECK(terminator, IsValidCharDescriptor(cmdmsg));
+  }
+
+  if (wait) {
+    // either wait is not specified or wait is true: synchronous mode
+    int status{std::system(command->OffsetElement())};
+    int exitStatusVal{
+        TerminationCheck(status, command, cmdstat, cmdmsg, terminator)};
+    CheckAndStoreIntToDescriptor(exitstat, exitStatusVal, terminator);
+  } else {
+// Asynchronous mode
+#ifdef _WIN32
+    STARTUPINFO si;
+    PROCESS_INFORMATION pi;
+    ZeroMemory(&si, sizeof(si));
+    si.cb = sizeof(si);
+    ZeroMemory(&pi, sizeof(pi));
+
+    // append "cmd.exe /c " to the beginning of command
+    const char *cmd{command->OffsetElement()};
+    const char *prefix{"cmd.exe /c "};
+    char *newCmd{(char *)malloc(std::strlen(prefix) + std::strlen(cmd) + 1)};
+    if (newCmd != NULL) {
+      std::strcpy(newCmd, prefix);
+      std::strcat(newCmd, cmd);
+    } else {
+      terminator.Crash("Memory allocation failed for newCmd");
+    }
+
+    // Convert the char to wide char
+    const size_t sizeNeeded{mbstowcs(NULL, newCmd, 0) + 1};
+    wchar_t *wcmd{new wchar_t[sizeNeeded]};
+    if (std::mbstowcs(wcmd, newCmd, sizeNeeded) == static_cast<size_t>(-1)) {
+      terminator.Crash("Char to wide char failed for newCmd");
+    }
+    free(newCmd);
+
+    if (CreateProcess(nullptr, wcmd, nullptr, nullptr, FALSE, 0, nullptr,
+            nullptr, &si, &pi)) {
+      CloseHandle(pi.hProcess);
+      CloseHandle(pi.hThread);
+    } else {
+      if (!cmdstat...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/74077


More information about the flang-commits mailing list