[llvm] [clang-tools-extra] [clang] [flang] [flang ]GETLOG runtime and extension implementation: get login username (PR #70917)

Yi Wu via cfe-commits cfe-commits at lists.llvm.org
Wed Dec 6 02:59:13 PST 2023


https://github.com/yi-wu-arm updated https://github.com/llvm/llvm-project/pull/70917

>From 0e98aa7ca15b05b91813eaeeb6ae1305e5f5384d Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Mon, 6 Nov 2023 19:49:13 +0000
Subject: [PATCH 01/15] GETLOG runtime and extension implementation: get login
 username

Get login username, ussage:
CHARACTER(32) :: login
CALL getlog(login)
WRITE(*,*) login
---
 flang/docs/Intrinsics.md                      |  2 +-
 .../Optimizer/Builder/Runtime/RTBuilder.h     |  8 ++++
 flang/include/flang/Runtime/command.h         |  6 +++
 flang/include/flang/Runtime/extensions.h      |  2 +
 flang/runtime/command.cpp                     | 40 +++++++++++++++++++
 flang/runtime/extensions.cpp                  |  6 +++
 flang/unittests/Runtime/CommandTest.cpp       | 15 ++++++-
 7 files changed, 77 insertions(+), 2 deletions(-)

diff --git a/flang/docs/Intrinsics.md b/flang/docs/Intrinsics.md
index ab0a940e53e55..cfe5dcd141e98 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, 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, GETLOG, MOVE_ALLOC, RANDOM_INIT, RANDOM_NUMBER, RANDOM_SEED, SYSTEM_CLOCK |
 | Atomic intrinsic subroutines | ATOMIC_ADD |
 | Collective intrinsic subroutines | CO_REDUCE |
 
diff --git a/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h b/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h
index b2774263e7a31..830df7ad006b5 100644
--- a/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h
+++ b/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h
@@ -62,6 +62,14 @@ using FuncTypeBuilderFunc = mlir::FunctionType (*)(mlir::MLIRContext *);
 /// standard type `i32` when `sizeof(int)` is 4.
 template <typename T>
 static constexpr TypeBuilderFunc getModel();
+
+template <>
+constexpr TypeBuilderFunc getModel<unsigned int>() {
+  return [](mlir::MLIRContext *context) -> mlir::Type {
+    return mlir::IntegerType::get(context, 8 * sizeof(unsigned int));
+  };
+}
+
 template <>
 constexpr TypeBuilderFunc getModel<short int>() {
   return [](mlir::MLIRContext *context) -> mlir::Type {
diff --git a/flang/include/flang/Runtime/command.h b/flang/include/flang/Runtime/command.h
index ec62893905454..1c212ef61697c 100644
--- a/flang/include/flang/Runtime/command.h
+++ b/flang/include/flang/Runtime/command.h
@@ -47,6 +47,12 @@ std::int32_t RTNAME(GetEnvVariable)(const Descriptor &name,
     bool trim_name = true, const Descriptor *errmsg = nullptr,
     const char *sourceFile = nullptr, int line = 0);
 }
+
+// Try to get the name of current user
+// Returns a STATUS as described in the standard.
+std::int32_t RTNAME(GetLog)(
+    const Descriptor *argument = nullptr, const Descriptor *errmsg = nullptr);
+
 } // namespace Fortran::runtime
 
 #endif // FORTRAN_RUNTIME_COMMAND_H_
diff --git a/flang/include/flang/Runtime/extensions.h b/flang/include/flang/Runtime/extensions.h
index ad592814e5acb..d199d5e387b86 100644
--- a/flang/include/flang/Runtime/extensions.h
+++ b/flang/include/flang/Runtime/extensions.h
@@ -28,5 +28,7 @@ std::int32_t FORTRAN_PROCEDURE_NAME(iargc)();
 void FORTRAN_PROCEDURE_NAME(getarg)(
     std::int32_t &n, std::int8_t *arg, std::int64_t length);
 
+void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *name, std::int64_t length);
+
 } // extern "C"
 #endif // FORTRAN_RUNTIME_EXTENSIONS_H_
diff --git a/flang/runtime/command.cpp b/flang/runtime/command.cpp
index b81a0791c5e57..6b2f313e227a1 100644
--- a/flang/runtime/command.cpp
+++ b/flang/runtime/command.cpp
@@ -15,6 +15,30 @@
 #include <cstdlib>
 #include <limits>
 
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#define NOMINMAX
+#include <windows.h>
+
+#include <Lmcons.h> // UNLEN=256
+
+inline char *getlogin() {
+  char *username = NULL;
+  DWORD size = UNLEN + 1; // Constant for the maximum username length
+  username = (char *)malloc(size);
+
+  if (GetUserName(username, &size)) {
+    // Username retrieved successfully
+    return username;
+  } else {
+    free(username);
+    return NULL;
+  }
+}
+#else
+#include <unistd.h>
+#endif
+
 namespace Fortran::runtime {
 std::int32_t RTNAME(ArgumentCount)() {
   int argc{executionEnvironment.argc};
@@ -222,6 +246,22 @@ std::int32_t RTNAME(GetCommand)(const Descriptor *value,
   return stat;
 }
 
+std::int32_t RTNAME(GetLog)(const Descriptor *value, const Descriptor *errmsg) {
+  FillWithSpaces(*value);
+
+  const char *arg = getlogin();
+  std::int64_t argLen{StringLength(arg)};
+  if (argLen <= 0) {
+    return ToErrmsg(errmsg, StatMissingArgument);
+  }
+
+  if (value) {
+    return CopyToDescriptor(*value, arg, argLen, errmsg);
+  }
+
+  return StatOk;
+}
+
 static std::size_t LengthWithoutTrailingSpaces(const Descriptor &d) {
   std::size_t s{d.ElementBytes() - 1};
   while (*d.OffsetElement(s) == ' ') {
diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp
index b8e9b6eae1320..47b269d1e5b42 100644
--- a/flang/runtime/extensions.cpp
+++ b/flang/runtime/extensions.cpp
@@ -37,5 +37,11 @@ void FORTRAN_PROCEDURE_NAME(getarg)(
   (void)RTNAME(GetCommandArgument)(
       n, &value, nullptr, nullptr, __FILE__, __LINE__);
 }
+
+void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *arg, std::int64_t length) {
+  Descriptor value{*Descriptor::Create(1, length, arg, 0)};
+  (void)RTNAME(GetLog)(&value, nullptr);
+}
+
 } // namespace Fortran::runtime
 } // extern "C"
diff --git a/flang/unittests/Runtime/CommandTest.cpp b/flang/unittests/Runtime/CommandTest.cpp
index c3571c9684e4b..e33dd7b0469eb 100644
--- a/flang/unittests/Runtime/CommandTest.cpp
+++ b/flang/unittests/Runtime/CommandTest.cpp
@@ -225,6 +225,12 @@ TEST_F(ZeroArguments, GetCommandArgument) {
   CheckMissingArgumentValue(1);
 }
 
+TEST_F(ZeroArguments, GetLog) {
+  CheckMissingArgumentValue(-1);
+  CheckArgumentValue(commandOnlyArgv[0], 0);
+  CheckMissingArgumentValue(1);
+}
+
 TEST_F(ZeroArguments, GetCommand) { CheckCommandValue(commandOnlyArgv, 1); }
 
 static const char *oneArgArgv[]{"aProgram", "anArgumentOfLength20"};
@@ -242,6 +248,13 @@ TEST_F(OneArgument, GetCommandArgument) {
   CheckMissingArgumentValue(2);
 }
 
+TEST_F(OneArgument, GetLog) {
+  CheckMissingArgumentValue(-1);
+  CheckArgumentValue(oneArgArgv[0], 0);
+  CheckArgumentValue(oneArgArgv[1], 1);
+  CheckMissingArgumentValue(2);
+}
+
 TEST_F(OneArgument, GetCommand) { CheckCommandValue(oneArgArgv, 2); }
 
 static const char *severalArgsArgv[]{
@@ -284,7 +297,7 @@ TEST_F(SeveralArguments, ArgValueTooShort) {
   ASSERT_NE(tooShort, nullptr);
   EXPECT_EQ(RTNAME(GetCommandArgument)(1, tooShort.get()), -1);
   CheckDescriptorEqStr(tooShort.get(), severalArgsArgv[1]);
-
+  
   OwningPtr<Descriptor> length{EmptyIntDescriptor()};
   ASSERT_NE(length, nullptr);
   OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()};

>From ce001f38e45511d0c4381a65a95c0e76220e45c9 Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Tue, 7 Nov 2023 15:09:23 +0000
Subject: [PATCH 02/15] add include string.h, wchar.h, link library, and format

---
 flang/runtime/command.cpp               | 25 ++++++++++++++++---------
 flang/unittests/Runtime/CommandTest.cpp |  2 +-
 2 files changed, 17 insertions(+), 10 deletions(-)

diff --git a/flang/runtime/command.cpp b/flang/runtime/command.cpp
index 6b2f313e227a1..c5171a1ef36f2 100644
--- a/flang/runtime/command.cpp
+++ b/flang/runtime/command.cpp
@@ -14,26 +14,33 @@
 #include "flang/Runtime/descriptor.h"
 #include <cstdlib>
 #include <limits>
+#include <string.h>
 
 #ifdef _WIN32
 #define WIN32_LEAN_AND_MEAN
 #define NOMINMAX
 #include <windows.h>
 
-#include <Lmcons.h> // UNLEN=256
+#include <lmcons.h> // UNLEN=256
+#include <wchar.h> // wchar_t cast to LPWSTR
+#pragma comment(lib, "Advapi32.lib") // Link Advapi32.lib for GetUserName
 
-inline char *getlogin() {
-  char *username = NULL;
-  DWORD size = UNLEN + 1; // Constant for the maximum username length
-  username = (char *)malloc(size);
+static inline char *getlogin() {
+  static char username[UNLEN + 1];
+  wchar_t w_username[UNLEN + 1];
+  DWORD namelen = sizeof(w_username) / sizeof(w_username[0]);
 
-  if (GetUserName(username, &size)) {
-    // Username retrieved successfully
-    return username;
+  if (GetUserName(w_username, &namelen)) {
+    // Convert the wchar_t string to a regular C string
+    if (wcstombs(username, w_username, UNLEN + 1) == -1) {
+      // Conversion failed
+      return NULL;
+    }
+    return (username[0] == 0 ? NULL : username);
   } else {
-    free(username);
     return NULL;
   }
+  return nullptr;
 }
 #else
 #include <unistd.h>
diff --git a/flang/unittests/Runtime/CommandTest.cpp b/flang/unittests/Runtime/CommandTest.cpp
index e33dd7b0469eb..686dc273142ba 100644
--- a/flang/unittests/Runtime/CommandTest.cpp
+++ b/flang/unittests/Runtime/CommandTest.cpp
@@ -297,7 +297,7 @@ TEST_F(SeveralArguments, ArgValueTooShort) {
   ASSERT_NE(tooShort, nullptr);
   EXPECT_EQ(RTNAME(GetCommandArgument)(1, tooShort.get()), -1);
   CheckDescriptorEqStr(tooShort.get(), severalArgsArgv[1]);
-  
+
   OwningPtr<Descriptor> length{EmptyIntDescriptor()};
   ASSERT_NE(length, nullptr);
   OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()};

>From 0de53646977c88ebc7bdb799325e5d92269a2f62 Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Fri, 10 Nov 2023 10:44:12 +0000
Subject: [PATCH 03/15] use getlogin_r instead of getlogin, check for
 POSIX-compliant

accourding to https://linux.die.net/man/3/getlogin_r,
`_REENTRANT || _POSIX_C_SOURCE >= 199506L` checks the existance of getlogin_r
---
 flang/runtime/command.cpp | 44 ++++++++++++++++++++++++++++-----------
 1 file changed, 32 insertions(+), 12 deletions(-)

diff --git a/flang/runtime/command.cpp b/flang/runtime/command.cpp
index c5171a1ef36f2..aa7c777d767cb 100644
--- a/flang/runtime/command.cpp
+++ b/flang/runtime/command.cpp
@@ -24,26 +24,30 @@
 #include <lmcons.h> // UNLEN=256
 #include <wchar.h> // wchar_t cast to LPWSTR
 #pragma comment(lib, "Advapi32.lib") // Link Advapi32.lib for GetUserName
+#define LOGIN_NAME_MAX UNLEN
 
-static inline char *getlogin() {
-  static char username[UNLEN + 1];
+inline int getlogin_r(char *buf, size_t bufSize) {
   wchar_t w_username[UNLEN + 1];
   DWORD namelen = sizeof(w_username) / sizeof(w_username[0]);
 
   if (GetUserName(w_username, &namelen)) {
     // Convert the wchar_t string to a regular C string
-    if (wcstombs(username, w_username, UNLEN + 1) == -1) {
+    if (wcstombs(buf, w_username, UNLEN + 1) == -1) {
       // Conversion failed
-      return NULL;
+      return -1;
     }
-    return (username[0] == 0 ? NULL : username);
+    return (buf[0] == 0 ? -1 : 0);
   } else {
-    return NULL;
+    return -1;
   }
-  return nullptr;
+  return -1;
 }
-#else
+#elif _REENTRANT || _POSIX_C_SOURCE >= 199506L
+// System is posix-compliant and has getlogin_r
 #include <unistd.h>
+#else
+// System is not posix-compliant
+inline int getlogin_r(char *buf, size_t bufsize) { return -1; }
 #endif
 
 namespace Fortran::runtime {
@@ -253,17 +257,33 @@ std::int32_t RTNAME(GetCommand)(const Descriptor *value,
   return stat;
 }
 
+// Trim space from right/end
+char *RTrim(char *str) {
+  int i = strlen(str);
+  while (' ' == str[--i]) {
+    str[i] = 0;
+  }
+  return str;
+}
+
 std::int32_t RTNAME(GetLog)(const Descriptor *value, const Descriptor *errmsg) {
   FillWithSpaces(*value);
 
-  const char *arg = getlogin();
-  std::int64_t argLen{StringLength(arg)};
-  if (argLen <= 0) {
+  std::array<char, LOGIN_NAME_MAX + 1> str;
+  int err = getlogin_r(str.data(), str.size());
+  if (err != 0) {
+    return ToErrmsg(errmsg, StatMissingArgument);
+  }
+
+  RTrim(str.data());
+  std::int64_t strLen{StringLength(str.data())};
+
+  if (strLen <= 0) {
     return ToErrmsg(errmsg, StatMissingArgument);
   }
 
   if (value) {
-    return CopyToDescriptor(*value, arg, argLen, errmsg);
+    return CopyToDescriptor(*value, str.data(), strLen, errmsg);
   }
 
   return StatOk;

>From 72727baf225979fcf3108be7a6d936aaed20b97c Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Fri, 10 Nov 2023 15:06:08 +0000
Subject: [PATCH 04/15] move implementation to extension.cpp, add assert for
 failure, and format

---
 .../Optimizer/Builder/Runtime/RTBuilder.h     |  8 ---
 flang/include/flang/Runtime/command.h         |  6 --
 flang/runtime/command.cpp                     | 67 -------------------
 flang/runtime/extensions.cpp                  | 47 ++++++++++++-
 4 files changed, 45 insertions(+), 83 deletions(-)

diff --git a/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h b/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h
index 830df7ad006b5..b2774263e7a31 100644
--- a/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h
+++ b/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h
@@ -62,14 +62,6 @@ using FuncTypeBuilderFunc = mlir::FunctionType (*)(mlir::MLIRContext *);
 /// standard type `i32` when `sizeof(int)` is 4.
 template <typename T>
 static constexpr TypeBuilderFunc getModel();
-
-template <>
-constexpr TypeBuilderFunc getModel<unsigned int>() {
-  return [](mlir::MLIRContext *context) -> mlir::Type {
-    return mlir::IntegerType::get(context, 8 * sizeof(unsigned int));
-  };
-}
-
 template <>
 constexpr TypeBuilderFunc getModel<short int>() {
   return [](mlir::MLIRContext *context) -> mlir::Type {
diff --git a/flang/include/flang/Runtime/command.h b/flang/include/flang/Runtime/command.h
index 1c212ef61697c..ec62893905454 100644
--- a/flang/include/flang/Runtime/command.h
+++ b/flang/include/flang/Runtime/command.h
@@ -47,12 +47,6 @@ std::int32_t RTNAME(GetEnvVariable)(const Descriptor &name,
     bool trim_name = true, const Descriptor *errmsg = nullptr,
     const char *sourceFile = nullptr, int line = 0);
 }
-
-// Try to get the name of current user
-// Returns a STATUS as described in the standard.
-std::int32_t RTNAME(GetLog)(
-    const Descriptor *argument = nullptr, const Descriptor *errmsg = nullptr);
-
 } // namespace Fortran::runtime
 
 #endif // FORTRAN_RUNTIME_COMMAND_H_
diff --git a/flang/runtime/command.cpp b/flang/runtime/command.cpp
index aa7c777d767cb..b81a0791c5e57 100644
--- a/flang/runtime/command.cpp
+++ b/flang/runtime/command.cpp
@@ -14,41 +14,6 @@
 #include "flang/Runtime/descriptor.h"
 #include <cstdlib>
 #include <limits>
-#include <string.h>
-
-#ifdef _WIN32
-#define WIN32_LEAN_AND_MEAN
-#define NOMINMAX
-#include <windows.h>
-
-#include <lmcons.h> // UNLEN=256
-#include <wchar.h> // wchar_t cast to LPWSTR
-#pragma comment(lib, "Advapi32.lib") // Link Advapi32.lib for GetUserName
-#define LOGIN_NAME_MAX UNLEN
-
-inline int getlogin_r(char *buf, size_t bufSize) {
-  wchar_t w_username[UNLEN + 1];
-  DWORD namelen = sizeof(w_username) / sizeof(w_username[0]);
-
-  if (GetUserName(w_username, &namelen)) {
-    // Convert the wchar_t string to a regular C string
-    if (wcstombs(buf, w_username, UNLEN + 1) == -1) {
-      // Conversion failed
-      return -1;
-    }
-    return (buf[0] == 0 ? -1 : 0);
-  } else {
-    return -1;
-  }
-  return -1;
-}
-#elif _REENTRANT || _POSIX_C_SOURCE >= 199506L
-// System is posix-compliant and has getlogin_r
-#include <unistd.h>
-#else
-// System is not posix-compliant
-inline int getlogin_r(char *buf, size_t bufsize) { return -1; }
-#endif
 
 namespace Fortran::runtime {
 std::int32_t RTNAME(ArgumentCount)() {
@@ -257,38 +222,6 @@ std::int32_t RTNAME(GetCommand)(const Descriptor *value,
   return stat;
 }
 
-// Trim space from right/end
-char *RTrim(char *str) {
-  int i = strlen(str);
-  while (' ' == str[--i]) {
-    str[i] = 0;
-  }
-  return str;
-}
-
-std::int32_t RTNAME(GetLog)(const Descriptor *value, const Descriptor *errmsg) {
-  FillWithSpaces(*value);
-
-  std::array<char, LOGIN_NAME_MAX + 1> str;
-  int err = getlogin_r(str.data(), str.size());
-  if (err != 0) {
-    return ToErrmsg(errmsg, StatMissingArgument);
-  }
-
-  RTrim(str.data());
-  std::int64_t strLen{StringLength(str.data())};
-
-  if (strLen <= 0) {
-    return ToErrmsg(errmsg, StatMissingArgument);
-  }
-
-  if (value) {
-    return CopyToDescriptor(*value, str.data(), strLen, errmsg);
-  }
-
-  return StatOk;
-}
-
 static std::size_t LengthWithoutTrailingSpaces(const Descriptor &d) {
   std::size_t s{d.ElementBytes() - 1};
   while (*d.OffsetElement(s) == ' ') {
diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp
index 47b269d1e5b42..a699196699ab8 100644
--- a/flang/runtime/extensions.cpp
+++ b/flang/runtime/extensions.cpp
@@ -13,6 +13,41 @@
 #include "flang/Runtime/command.h"
 #include "flang/Runtime/descriptor.h"
 #include "flang/Runtime/io-api.h"
+#include <string.h>
+
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#define NOMINMAX
+#include <windows.h>
+
+#include <lmcons.h> // UNLEN=256
+#include <wchar.h> // wchar_t cast to LPWSTR
+#pragma comment(lib, "Advapi32.lib") // Link Advapi32.lib for GetUserName
+#define LOGIN_NAME_MAX UNLEN
+
+inline int getlogin_r(char *buf, size_t bufSize) {
+  wchar_t w_username[UNLEN + 1];
+  DWORD namelen = sizeof(w_username) / sizeof(w_username[0]);
+
+  if (GetUserName(w_username, &namelen)) {
+    // Convert the wchar_t string to a regular C string
+    if (wcstombs(buf, w_username, UNLEN + 1) == -1) {
+      // Conversion failed
+      return -1;
+    }
+    return (buf[0] == 0 ? -1 : 0);
+  } else {
+    return -1;
+  }
+  return -1;
+}
+#elif _REENTRANT || _POSIX_C_SOURCE >= 199506L
+// System is posix-compliant and has getlogin_r
+#include <unistd.h>
+#else
+// System is not posix-compliant
+inline int getlogin_r(char *buf, size_t bufsize) { return -1; }
+#endif
 
 extern "C" {
 
@@ -39,8 +74,16 @@ void FORTRAN_PROCEDURE_NAME(getarg)(
 }
 
 void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *arg, std::int64_t length) {
-  Descriptor value{*Descriptor::Create(1, length, arg, 0)};
-  (void)RTNAME(GetLog)(&value, nullptr);
+  std::array<char, LOGIN_NAME_MAX + 1> str;
+  int error = getlogin_r(str.data(), str.size());
+  assert(error == 0 && "getlogin_r returned an error");
+
+  // Trim space from right/end
+  int i = str.size();
+  while (' ' == str[--i]) {
+    str[i] = 0;
+  }
+  strncpy(reinterpret_cast<char *>(arg), str.data(), length);
 }
 
 } // namespace Fortran::runtime

>From 17cf43659f6c9e07ae58f54499706a0055cc5c71 Mon Sep 17 00:00:00 2001
From: Yi Wu <yiwu02 at wdev-yiwu02.arm.com>
Date: Mon, 13 Nov 2023 16:03:19 +0000
Subject: [PATCH 05/15] Use a more secure version for transfoom wchar_t to char

---
 flang/runtime/extensions.cpp | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp
index a699196699ab8..aa14fb952ceb2 100644
--- a/flang/runtime/extensions.cpp
+++ b/flang/runtime/extensions.cpp
@@ -21,17 +21,19 @@
 #include <windows.h>
 
 #include <lmcons.h> // UNLEN=256
+#include <stdlib.h> // wcstombs_s
 #include <wchar.h> // wchar_t cast to LPWSTR
 #pragma comment(lib, "Advapi32.lib") // Link Advapi32.lib for GetUserName
 #define LOGIN_NAME_MAX UNLEN
 
 inline int getlogin_r(char *buf, size_t bufSize) {
   wchar_t w_username[UNLEN + 1];
-  DWORD namelen = sizeof(w_username) / sizeof(w_username[0]);
+  DWORD nameLen = UNLEN + 1;
 
-  if (GetUserName(w_username, &namelen)) {
-    // Convert the wchar_t string to a regular C string
-    if (wcstombs(buf, w_username, UNLEN + 1) == -1) {
+  if (GetUserNameW(w_username, &nameLen)) {
+    // Convert the wchar_t string to a regular C string using wcstombs_s
+    if (wcstombs_s(nullptr, buf, sizeof(w_username), w_username, _TRUNCATE) !=
+        0) {
       // Conversion failed
       return -1;
     }
@@ -41,6 +43,7 @@ inline int getlogin_r(char *buf, size_t bufSize) {
   }
   return -1;
 }
+
 #elif _REENTRANT || _POSIX_C_SOURCE >= 199506L
 // System is posix-compliant and has getlogin_r
 #include <unistd.h>

>From a1b1b66fab3add35f33e6f56618b3c28e5cfdbf8 Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Wed, 15 Nov 2023 11:06:01 +0000
Subject: [PATCH 06/15] use cstring not string.h, add terminator, fill buffer
 with space

take copyBufferAndPad() out of anonymous namespace,
so it can be called in other places.
---
 flang/include/flang/Runtime/time-intrinsic.h | 11 ++++++++
 flang/runtime/extensions.cpp                 | 29 +++++++++++---------
 flang/runtime/time-intrinsic.cpp             | 26 ++++++++++--------
 3 files changed, 42 insertions(+), 24 deletions(-)

diff --git a/flang/include/flang/Runtime/time-intrinsic.h b/flang/include/flang/Runtime/time-intrinsic.h
index 650c02436ee49..a0c863712131b 100644
--- a/flang/include/flang/Runtime/time-intrinsic.h
+++ b/flang/include/flang/Runtime/time-intrinsic.h
@@ -9,6 +9,17 @@
 // Defines the API between compiled code and the implementations of time-related
 // intrinsic subroutines in the runtime library.
 
+// time-intrinsic.h
+#ifndef TIME_INTRINSIC_H
+#define TIME_INTRINSIC_H
+
+#include <cstddef>
+
+void copyBufferAndPad(
+    char *dest, std::size_t destChars, char *buffer, std::size_t len);
+
+#endif // TIME_INTRINSIC_H
+
 #ifndef FORTRAN_RUNTIME_TIME_INTRINSIC_H_
 #define FORTRAN_RUNTIME_TIME_INTRINSIC_H_
 
diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp
index aa14fb952ceb2..ea53d97d98005 100644
--- a/flang/runtime/extensions.cpp
+++ b/flang/runtime/extensions.cpp
@@ -10,18 +10,20 @@
 // extensions that will eventually be implemented in Fortran.
 
 #include "flang/Runtime/extensions.h"
+#include "terminator.h"
 #include "flang/Runtime/command.h"
 #include "flang/Runtime/descriptor.h"
 #include "flang/Runtime/io-api.h"
-#include <string.h>
+#include "flang/Runtime/time-intrinsic.h" // copyBufferAndPad
+#include <cstring>
 
 #ifdef _WIN32
 #define WIN32_LEAN_AND_MEAN
 #define NOMINMAX
 #include <windows.h>
 
+#include <cstdlib> // wcstombs_s
 #include <lmcons.h> // UNLEN=256
-#include <stdlib.h> // wcstombs_s
 #include <wchar.h> // wchar_t cast to LPWSTR
 #pragma comment(lib, "Advapi32.lib") // Link Advapi32.lib for GetUserName
 #define LOGIN_NAME_MAX UNLEN
@@ -30,10 +32,9 @@ inline int getlogin_r(char *buf, size_t bufSize) {
   wchar_t w_username[UNLEN + 1];
   DWORD nameLen = UNLEN + 1;
 
-  if (GetUserNameW(w_username, &nameLen)) {
+  if (GetUserName(w_username, &nameLen)) {
     // Convert the wchar_t string to a regular C string using wcstombs_s
-    if (wcstombs_s(nullptr, buf, sizeof(w_username), w_username, _TRUNCATE) !=
-        0) {
+    if (wcstombs_s(nullptr, buf, bufSize, w_username, _TRUNCATE) != 0) {
       // Conversion failed
       return -1;
     }
@@ -49,7 +50,11 @@ inline int getlogin_r(char *buf, size_t bufSize) {
 #include <unistd.h>
 #else
 // System is not posix-compliant
-inline int getlogin_r(char *buf, size_t bufsize) { return -1; }
+inline int getlogin_r(char *buf, size_t bufSize) {
+  std::memset(buf, ' ', bufSize - 1);
+  buf[bufSize - 1] = '\0';
+  return 0;
+}
 #endif
 
 extern "C" {
@@ -78,15 +83,13 @@ void FORTRAN_PROCEDURE_NAME(getarg)(
 
 void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *arg, std::int64_t length) {
   std::array<char, LOGIN_NAME_MAX + 1> str;
+
   int error = getlogin_r(str.data(), str.size());
-  assert(error == 0 && "getlogin_r returned an error");
+  Terminator terminator{__FILE__, __LINE__};
+  RUNTIME_CHECK(terminator, error == 0);
 
-  // Trim space from right/end
-  int i = str.size();
-  while (' ' == str[--i]) {
-    str[i] = 0;
-  }
-  strncpy(reinterpret_cast<char *>(arg), str.data(), length);
+  copyBufferAndPad(
+      reinterpret_cast<char *>(arg), length, str.data(), str.size());
 }
 
 } // namespace Fortran::runtime
diff --git a/flang/runtime/time-intrinsic.cpp b/flang/runtime/time-intrinsic.cpp
index 68d63253139f1..6a42734d78739 100644
--- a/flang/runtime/time-intrinsic.cpp
+++ b/flang/runtime/time-intrinsic.cpp
@@ -39,6 +39,17 @@
 // overload will have a dummy parameter whose type indicates whether or not it
 // should be preferred. Any other parameters required for SFINAE should have
 // default values provided.
+
+// outside anonymous namespace, function reused
+void copyBufferAndPad(
+    char *dest, std::size_t destChars, char *buffer, std::size_t len) {
+  auto copyLen{std::min(len, destChars)};
+  std::memcpy(dest, buffer, copyLen);
+  for (auto i{copyLen}; i < destChars; ++i) {
+    dest[i] = ' ';
+  }
+}
+
 namespace {
 // Types for the dummy parameter indicating the priority of a given overload.
 // We will invoke our helper with an integer literal argument, so the overload
@@ -279,29 +290,22 @@ static void GetDateAndTime(Fortran::runtime::Terminator &terminator, char *date,
 
   static constexpr std::size_t buffSize{16};
   char buffer[buffSize];
-  auto copyBufferAndPad{
-      [&](char *dest, std::size_t destChars, std::size_t len) {
-        auto copyLen{std::min(len, destChars)};
-        std::memcpy(dest, buffer, copyLen);
-        for (auto i{copyLen}; i < destChars; ++i) {
-          dest[i] = ' ';
-        }
-      }};
+
   if (date) {
     auto len = std::strftime(buffer, buffSize, "%Y%m%d", &localTime);
-    copyBufferAndPad(date, dateChars, len);
+    copyBufferAndPad(date, dateChars, buffer, len);
   }
   if (time) {
     auto len{std::snprintf(buffer, buffSize, "%02d%02d%02d.%03jd",
         localTime.tm_hour, localTime.tm_min, localTime.tm_sec, ms)};
-    copyBufferAndPad(time, timeChars, len);
+    copyBufferAndPad(time, timeChars, buffer, len);
   }
   if (zone) {
     // Note: this may leave the buffer empty on many platforms. Classic flang
     // has a much more complex way of doing this (see __io_timezone in classic
     // flang).
     auto len{std::strftime(buffer, buffSize, "%z", &localTime)};
-    copyBufferAndPad(zone, zoneChars, len);
+    copyBufferAndPad(zone, zoneChars, buffer, len);
   }
   if (values) {
     auto typeCode{values->type().GetCategoryAndKind()};

>From fb910d1e9587a1558b62ccd54ab0be064e0429e9 Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Wed, 22 Nov 2023 13:40:38 +0000
Subject: [PATCH 07/15] brace initialize array

---
 flang/runtime/extensions.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp
index ea53d97d98005..23daa3eeb9d6c 100644
--- a/flang/runtime/extensions.cpp
+++ b/flang/runtime/extensions.cpp
@@ -29,7 +29,7 @@
 #define LOGIN_NAME_MAX UNLEN
 
 inline int getlogin_r(char *buf, size_t bufSize) {
-  wchar_t w_username[UNLEN + 1];
+  wchar_t w_username[UNLEN + 1] = {};
   DWORD nameLen = UNLEN + 1;
 
   if (GetUserName(w_username, &nameLen)) {
@@ -82,7 +82,7 @@ void FORTRAN_PROCEDURE_NAME(getarg)(
 }
 
 void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *arg, std::int64_t length) {
-  std::array<char, LOGIN_NAME_MAX + 1> str;
+  std::array<char, LOGIN_NAME_MAX + 1> str = {};
 
   int error = getlogin_r(str.data(), str.size());
   Terminator terminator{__FILE__, __LINE__};

>From f066c81f943a266afbdcdd1bbd3123c27b690114 Mon Sep 17 00:00:00 2001
From: Yi Wu <43659785+yi-wu-arm at users.noreply.github.com>
Date: Wed, 22 Nov 2023 14:04:46 +0000
Subject: [PATCH 08/15] Update flang/runtime/extensions.cpp: brace
 initialization

Co-authored-by: Kiran Chandramohan <kiranchandramohan at gmail.com>
---
 flang/runtime/extensions.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp
index 23daa3eeb9d6c..e630abea799bd 100644
--- a/flang/runtime/extensions.cpp
+++ b/flang/runtime/extensions.cpp
@@ -30,7 +30,7 @@
 
 inline int getlogin_r(char *buf, size_t bufSize) {
   wchar_t w_username[UNLEN + 1] = {};
-  DWORD nameLen = UNLEN + 1;
+  DWORD nameLen{UNLEN + 1};
 
   if (GetUserName(w_username, &nameLen)) {
     // Convert the wchar_t string to a regular C string using wcstombs_s

>From 8997af86097897765f8b1a5aba84ab357d07a5bc Mon Sep 17 00:00:00 2001
From: Yi Wu <43659785+yi-wu-arm at users.noreply.github.com>
Date: Wed, 22 Nov 2023 14:06:08 +0000
Subject: [PATCH 09/15] Update flang/runtime/extensions.cpp: brace
 initialization

Co-authored-by: Kiran Chandramohan <kiranchandramohan at gmail.com>
---
 flang/runtime/extensions.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp
index e630abea799bd..646ad120fcdfa 100644
--- a/flang/runtime/extensions.cpp
+++ b/flang/runtime/extensions.cpp
@@ -84,7 +84,7 @@ void FORTRAN_PROCEDURE_NAME(getarg)(
 void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *arg, std::int64_t length) {
   std::array<char, LOGIN_NAME_MAX + 1> str = {};
 
-  int error = getlogin_r(str.data(), str.size());
+  int error{getlogin_r(str.data(), str.size())};
   Terminator terminator{__FILE__, __LINE__};
   RUNTIME_CHECK(terminator, error == 0);
 

>From bad673a37d15418dd5119b639388e4600bbba197 Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Wed, 22 Nov 2023 15:07:15 +0000
Subject: [PATCH 10/15] Revert "brace initialize array"

This reverts commit fb910d1e9587a1558b62ccd54ab0be064e0429e9.
---
 flang/runtime/extensions.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp
index 646ad120fcdfa..36f76a2f0e7b1 100644
--- a/flang/runtime/extensions.cpp
+++ b/flang/runtime/extensions.cpp
@@ -29,7 +29,7 @@
 #define LOGIN_NAME_MAX UNLEN
 
 inline int getlogin_r(char *buf, size_t bufSize) {
-  wchar_t w_username[UNLEN + 1] = {};
+  wchar_t w_username[UNLEN + 1];
   DWORD nameLen{UNLEN + 1};
 
   if (GetUserName(w_username, &nameLen)) {
@@ -82,7 +82,7 @@ void FORTRAN_PROCEDURE_NAME(getarg)(
 }
 
 void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *arg, std::int64_t length) {
-  std::array<char, LOGIN_NAME_MAX + 1> str = {};
+  std::array<char, LOGIN_NAME_MAX + 1> str;
 
   int error{getlogin_r(str.data(), str.size())};
   Terminator terminator{__FILE__, __LINE__};

>From b55bb5f23064eb342fb860e7e23ba49a84947b4c Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Mon, 27 Nov 2023 17:24:18 +0000
Subject: [PATCH 11/15] final nits

---
 flang/include/flang/Runtime/time-intrinsic.h |  4 ++--
 flang/runtime/extensions.cpp                 |  4 ++--
 flang/runtime/time-intrinsic.cpp             | 10 +++++-----
 3 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/flang/include/flang/Runtime/time-intrinsic.h b/flang/include/flang/Runtime/time-intrinsic.h
index a0c863712131b..6f1e0f390abeb 100644
--- a/flang/include/flang/Runtime/time-intrinsic.h
+++ b/flang/include/flang/Runtime/time-intrinsic.h
@@ -15,8 +15,8 @@
 
 #include <cstddef>
 
-void copyBufferAndPad(
-    char *dest, std::size_t destChars, char *buffer, std::size_t len);
+void CopyBufferAndPad(
+    char *dest, std::size_t destChars, const char *buffer, std::size_t len);
 
 #endif // TIME_INTRINSIC_H
 
diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp
index 36f76a2f0e7b1..1a3d2c8f74593 100644
--- a/flang/runtime/extensions.cpp
+++ b/flang/runtime/extensions.cpp
@@ -14,7 +14,7 @@
 #include "flang/Runtime/command.h"
 #include "flang/Runtime/descriptor.h"
 #include "flang/Runtime/io-api.h"
-#include "flang/Runtime/time-intrinsic.h" // copyBufferAndPad
+#include "flang/Runtime/time-intrinsic.h" // CopyBufferAndPad
 #include <cstring>
 
 #ifdef _WIN32
@@ -88,7 +88,7 @@ void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *arg, std::int64_t length) {
   Terminator terminator{__FILE__, __LINE__};
   RUNTIME_CHECK(terminator, error == 0);
 
-  copyBufferAndPad(
+  CopyBufferAndPad(
       reinterpret_cast<char *>(arg), length, str.data(), str.size());
 }
 
diff --git a/flang/runtime/time-intrinsic.cpp b/flang/runtime/time-intrinsic.cpp
index 6a42734d78739..6d234a58ff8a4 100644
--- a/flang/runtime/time-intrinsic.cpp
+++ b/flang/runtime/time-intrinsic.cpp
@@ -41,8 +41,8 @@
 // default values provided.
 
 // outside anonymous namespace, function reused
-void copyBufferAndPad(
-    char *dest, std::size_t destChars, char *buffer, std::size_t len) {
+void CopyBufferAndPad(
+    char *dest, std::size_t destChars, const char *buffer, std::size_t len) {
   auto copyLen{std::min(len, destChars)};
   std::memcpy(dest, buffer, copyLen);
   for (auto i{copyLen}; i < destChars; ++i) {
@@ -293,19 +293,19 @@ static void GetDateAndTime(Fortran::runtime::Terminator &terminator, char *date,
 
   if (date) {
     auto len = std::strftime(buffer, buffSize, "%Y%m%d", &localTime);
-    copyBufferAndPad(date, dateChars, buffer, len);
+    CopyBufferAndPad(date, dateChars, buffer, len);
   }
   if (time) {
     auto len{std::snprintf(buffer, buffSize, "%02d%02d%02d.%03jd",
         localTime.tm_hour, localTime.tm_min, localTime.tm_sec, ms)};
-    copyBufferAndPad(time, timeChars, buffer, len);
+    CopyBufferAndPad(time, timeChars, buffer, len);
   }
   if (zone) {
     // Note: this may leave the buffer empty on many platforms. Classic flang
     // has a much more complex way of doing this (see __io_timezone in classic
     // flang).
     auto len{std::strftime(buffer, buffSize, "%z", &localTime)};
-    copyBufferAndPad(zone, zoneChars, buffer, len);
+    CopyBufferAndPad(zone, zoneChars, buffer, len);
   }
   if (values) {
     auto typeCode{values->type().GetCategoryAndKind()};

>From d1470b0fe34f025519552ddc682f742ae821a2f9 Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Tue, 5 Dec 2023 13:14:58 +0000
Subject: [PATCH 12/15] Revert "use cstring not string.h, add terminator, fill
 buffer with space"

This reverts commit a1b1b66fab3add35f33e6f56618b3c28e5cfdbf8.
---
 flang/include/flang/Runtime/time-intrinsic.h | 11 ---------
 flang/runtime/extensions.cpp                 |  9 +++----
 flang/runtime/time-intrinsic.cpp             | 26 +++++++++-----------
 3 files changed, 15 insertions(+), 31 deletions(-)

diff --git a/flang/include/flang/Runtime/time-intrinsic.h b/flang/include/flang/Runtime/time-intrinsic.h
index 6f1e0f390abeb..650c02436ee49 100644
--- a/flang/include/flang/Runtime/time-intrinsic.h
+++ b/flang/include/flang/Runtime/time-intrinsic.h
@@ -9,17 +9,6 @@
 // Defines the API between compiled code and the implementations of time-related
 // intrinsic subroutines in the runtime library.
 
-// time-intrinsic.h
-#ifndef TIME_INTRINSIC_H
-#define TIME_INTRINSIC_H
-
-#include <cstddef>
-
-void CopyBufferAndPad(
-    char *dest, std::size_t destChars, const char *buffer, std::size_t len);
-
-#endif // TIME_INTRINSIC_H
-
 #ifndef FORTRAN_RUNTIME_TIME_INTRINSIC_H_
 #define FORTRAN_RUNTIME_TIME_INTRINSIC_H_
 
diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp
index 1a3d2c8f74593..7e0f6494779f1 100644
--- a/flang/runtime/extensions.cpp
+++ b/flang/runtime/extensions.cpp
@@ -14,7 +14,6 @@
 #include "flang/Runtime/command.h"
 #include "flang/Runtime/descriptor.h"
 #include "flang/Runtime/io-api.h"
-#include "flang/Runtime/time-intrinsic.h" // CopyBufferAndPad
 #include <cstring>
 
 #ifdef _WIN32
@@ -82,14 +81,14 @@ void FORTRAN_PROCEDURE_NAME(getarg)(
 }
 
 void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *arg, std::int64_t length) {
-  std::array<char, LOGIN_NAME_MAX + 1> str;
+  int charLen{LOGIN_NAME_MAX + 1};
+  char str[charLen];
 
-  int error{getlogin_r(str.data(), str.size())};
+  int error{getlogin_r(*str, charLen)};
   Terminator terminator{__FILE__, __LINE__};
   RUNTIME_CHECK(terminator, error == 0);
 
-  CopyBufferAndPad(
-      reinterpret_cast<char *>(arg), length, str.data(), str.size());
+  CopyBufferAndPad(reinterpret_cast<char *>(arg), length, *str, charLen);
 }
 
 } // namespace Fortran::runtime
diff --git a/flang/runtime/time-intrinsic.cpp b/flang/runtime/time-intrinsic.cpp
index 6d234a58ff8a4..68d63253139f1 100644
--- a/flang/runtime/time-intrinsic.cpp
+++ b/flang/runtime/time-intrinsic.cpp
@@ -39,17 +39,6 @@
 // overload will have a dummy parameter whose type indicates whether or not it
 // should be preferred. Any other parameters required for SFINAE should have
 // default values provided.
-
-// outside anonymous namespace, function reused
-void CopyBufferAndPad(
-    char *dest, std::size_t destChars, const char *buffer, std::size_t len) {
-  auto copyLen{std::min(len, destChars)};
-  std::memcpy(dest, buffer, copyLen);
-  for (auto i{copyLen}; i < destChars; ++i) {
-    dest[i] = ' ';
-  }
-}
-
 namespace {
 // Types for the dummy parameter indicating the priority of a given overload.
 // We will invoke our helper with an integer literal argument, so the overload
@@ -290,22 +279,29 @@ static void GetDateAndTime(Fortran::runtime::Terminator &terminator, char *date,
 
   static constexpr std::size_t buffSize{16};
   char buffer[buffSize];
-
+  auto copyBufferAndPad{
+      [&](char *dest, std::size_t destChars, std::size_t len) {
+        auto copyLen{std::min(len, destChars)};
+        std::memcpy(dest, buffer, copyLen);
+        for (auto i{copyLen}; i < destChars; ++i) {
+          dest[i] = ' ';
+        }
+      }};
   if (date) {
     auto len = std::strftime(buffer, buffSize, "%Y%m%d", &localTime);
-    CopyBufferAndPad(date, dateChars, buffer, len);
+    copyBufferAndPad(date, dateChars, len);
   }
   if (time) {
     auto len{std::snprintf(buffer, buffSize, "%02d%02d%02d.%03jd",
         localTime.tm_hour, localTime.tm_min, localTime.tm_sec, ms)};
-    CopyBufferAndPad(time, timeChars, buffer, len);
+    copyBufferAndPad(time, timeChars, len);
   }
   if (zone) {
     // Note: this may leave the buffer empty on many platforms. Classic flang
     // has a much more complex way of doing this (see __io_timezone in classic
     // flang).
     auto len{std::strftime(buffer, buffSize, "%z", &localTime)};
-    CopyBufferAndPad(zone, zoneChars, buffer, len);
+    copyBufferAndPad(zone, zoneChars, len);
   }
   if (values) {
     auto typeCode{values->type().GetCategoryAndKind()};

>From 85d42d49042ee003ee0cb1a946be02c102d530a1 Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Tue, 5 Dec 2023 16:39:57 +0000
Subject: [PATCH 13/15] move and use CopyAndPad from character.h

The compiler must have access to the implementations
of templated functions at the points where they're instantiated.
---
 flang/include/flang/Runtime/character.h | 30 +++++++++++++++++++++++++
 flang/runtime/character.cpp             | 22 +-----------------
 flang/runtime/extensions.cpp            | 11 ++++-----
 3 files changed, 37 insertions(+), 26 deletions(-)

diff --git a/flang/include/flang/Runtime/character.h b/flang/include/flang/Runtime/character.h
index 24f26920bdd2c..ec1fb02751164 100644
--- a/flang/include/flang/Runtime/character.h
+++ b/flang/include/flang/Runtime/character.h
@@ -6,6 +6,36 @@
 //
 //===----------------------------------------------------------------------===//
 
+// character.h
+#ifndef CHARACTER_H
+#define CHARACTER_H
+
+#include <cstddef>
+#include <algorithm>
+
+template <typename TO, typename FROM>
+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, fromChars * sizeof(TO));
+    for (std::size_t j{fromChars}; j < toChars; ++j) {
+      to[j] = static_cast<TO>(' ');
+    }
+  }
+}
+
+#endif // CHARACTER_H
+
 // Defines API between compiled code and the CHARACTER
 // support functions in the runtime library.
 
diff --git a/flang/runtime/character.cpp b/flang/runtime/character.cpp
index dd522f19a7ede..2a62a60874e74 100644
--- a/flang/runtime/character.cpp
+++ b/flang/runtime/character.cpp
@@ -11,6 +11,7 @@
 #include "tools.h"
 #include "flang/Common/bit-population-count.h"
 #include "flang/Common/uint128.h"
+#include "flang/Runtime/character.h"
 #include "flang/Runtime/cpp-type.h"
 #include "flang/Runtime/descriptor.h"
 #include <algorithm>
@@ -461,27 +462,6 @@ static void GeneralCharFuncKind(Descriptor &result, const Descriptor &string,
   }
 }
 
-template <typename TO, typename FROM>
-static 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, fromChars * sizeof(TO));
-    for (std::size_t j{fromChars}; j < toChars; ++j) {
-      to[j] = static_cast<TO>(' ');
-    }
-  }
-}
-
 template <typename CHAR, bool ISMIN>
 static void MaxMinHelper(Descriptor &accumulator, const Descriptor &x,
     const Terminator &terminator) {
diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp
index 7e0f6494779f1..ee34eef99d4db 100644
--- a/flang/runtime/extensions.cpp
+++ b/flang/runtime/extensions.cpp
@@ -11,10 +11,10 @@
 
 #include "flang/Runtime/extensions.h"
 #include "terminator.h"
+#include "flang/Runtime/character.h"
 #include "flang/Runtime/command.h"
 #include "flang/Runtime/descriptor.h"
 #include "flang/Runtime/io-api.h"
-#include <cstring>
 
 #ifdef _WIN32
 #define WIN32_LEAN_AND_MEAN
@@ -81,14 +81,15 @@ void FORTRAN_PROCEDURE_NAME(getarg)(
 }
 
 void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *arg, std::int64_t length) {
-  int charLen{LOGIN_NAME_MAX + 1};
-  char str[charLen];
+  const int nameMaxLen = LOGIN_NAME_MAX + 1;
+  char str[nameMaxLen];
 
-  int error{getlogin_r(*str, charLen)};
+  int error{getlogin_r(str, nameMaxLen)};
   Terminator terminator{__FILE__, __LINE__};
   RUNTIME_CHECK(terminator, error == 0);
 
-  CopyBufferAndPad(reinterpret_cast<char *>(arg), length, *str, charLen);
+  // find first \0 in string then pad from there
+  CopyAndPad(reinterpret_cast<char *>(arg), str, length, std::strlen(str));
 }
 
 } // namespace Fortran::runtime

>From 99117e491ba0fd81dd7790bde6de91d0906b5a3b Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Wed, 6 Dec 2023 10:40:37 +0000
Subject: [PATCH 14/15] better tests and use min length for memcpy

---
 flang/include/flang/Runtime/character.h   |  5 ++-
 flang/include/flang/Runtime/extensions.h  |  1 +
 flang/runtime/extensions.cpp              |  2 +-
 flang/unittests/Runtime/CMakeLists.txt    |  1 +
 flang/unittests/Runtime/CommandTest.cpp   | 14 +------
 flang/unittests/Runtime/ExtensionTest.cpp | 48 +++++++++++++++++++++++
 6 files changed, 55 insertions(+), 16 deletions(-)
 create mode 100644 flang/unittests/Runtime/ExtensionTest.cpp

diff --git a/flang/include/flang/Runtime/character.h b/flang/include/flang/Runtime/character.h
index ec1fb02751164..52cd48f9637e6 100644
--- a/flang/include/flang/Runtime/character.h
+++ b/flang/include/flang/Runtime/character.h
@@ -10,8 +10,9 @@
 #ifndef CHARACTER_H
 #define CHARACTER_H
 
-#include <cstddef>
 #include <algorithm>
+#include <cstddef>
+#include <cstring>
 
 template <typename TO, typename FROM>
 void CopyAndPad(
@@ -27,7 +28,7 @@ void CopyAndPad(
   } else if (toChars <= fromChars) {
     std::memcpy(to, from, toChars * sizeof(TO));
   } else {
-    std::memcpy(to, from, fromChars * sizeof(TO));
+    std::memcpy(to, from, std::min(toChars, fromChars) * sizeof(TO));
     for (std::size_t j{fromChars}; j < toChars; ++j) {
       to[j] = static_cast<TO>(' ');
     }
diff --git a/flang/include/flang/Runtime/extensions.h b/flang/include/flang/Runtime/extensions.h
index d199d5e387b86..4f78e92ae677b 100644
--- a/flang/include/flang/Runtime/extensions.h
+++ b/flang/include/flang/Runtime/extensions.h
@@ -28,6 +28,7 @@ std::int32_t FORTRAN_PROCEDURE_NAME(iargc)();
 void FORTRAN_PROCEDURE_NAME(getarg)(
     std::int32_t &n, std::int8_t *arg, std::int64_t length);
 
+// GNU extension subroutine GETLOG(C).
 void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *name, std::int64_t length);
 
 } // extern "C"
diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp
index ee34eef99d4db..21d2ece06865a 100644
--- a/flang/runtime/extensions.cpp
+++ b/flang/runtime/extensions.cpp
@@ -81,7 +81,7 @@ void FORTRAN_PROCEDURE_NAME(getarg)(
 }
 
 void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *arg, std::int64_t length) {
-  const int nameMaxLen = LOGIN_NAME_MAX + 1;
+  const int nameMaxLen{LOGIN_NAME_MAX + 1};
   char str[nameMaxLen];
 
   int error{getlogin_r(str, nameMaxLen)};
diff --git a/flang/unittests/Runtime/CMakeLists.txt b/flang/unittests/Runtime/CMakeLists.txt
index 23f02aa751246..a25472409ac94 100644
--- a/flang/unittests/Runtime/CMakeLists.txt
+++ b/flang/unittests/Runtime/CMakeLists.txt
@@ -7,6 +7,7 @@ add_flang_unittest(FlangRuntimeTests
   Complex.cpp
   CrashHandlerFixture.cpp
   Derived.cpp
+  ExtensionTest.cpp
   ExternalIOTest.cpp
   Format.cpp
   Inquiry.cpp
diff --git a/flang/unittests/Runtime/CommandTest.cpp b/flang/unittests/Runtime/CommandTest.cpp
index 61209541cdb1e..d003eacd177e8 100644
--- a/flang/unittests/Runtime/CommandTest.cpp
+++ b/flang/unittests/Runtime/CommandTest.cpp
@@ -10,6 +10,7 @@
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "flang/Runtime/descriptor.h"
+#include "flang/Runtime/extensions.h"
 #include "flang/Runtime/main.h"
 #include <cstdlib>
 
@@ -225,12 +226,6 @@ TEST_F(ZeroArguments, GetCommandArgument) {
   CheckMissingArgumentValue(1);
 }
 
-TEST_F(ZeroArguments, GetLog) {
-  CheckMissingArgumentValue(-1);
-  CheckArgumentValue(commandOnlyArgv[0], 0);
-  CheckMissingArgumentValue(1);
-}
-
 TEST_F(ZeroArguments, GetCommand) { CheckCommandValue(commandOnlyArgv, 1); }
 
 static const char *oneArgArgv[]{"aProgram", "anArgumentOfLength20"};
@@ -248,13 +243,6 @@ TEST_F(OneArgument, GetCommandArgument) {
   CheckMissingArgumentValue(2);
 }
 
-TEST_F(OneArgument, GetLog) {
-  CheckMissingArgumentValue(-1);
-  CheckArgumentValue(oneArgArgv[0], 0);
-  CheckArgumentValue(oneArgArgv[1], 1);
-  CheckMissingArgumentValue(2);
-}
-
 TEST_F(OneArgument, GetCommand) { CheckCommandValue(oneArgArgv, 2); }
 
 static const char *severalArgsArgv[]{
diff --git a/flang/unittests/Runtime/ExtensionTest.cpp b/flang/unittests/Runtime/ExtensionTest.cpp
new file mode 100644
index 0000000000000..d1147f18c2159
--- /dev/null
+++ b/flang/unittests/Runtime/ExtensionTest.cpp
@@ -0,0 +1,48 @@
+//===-- flang/unittests/Runtime/ExtensionTest.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 "CrashHandlerFixture.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "flang/Runtime/descriptor.h"
+#include "flang/Runtime/extensions.h"
+#include "flang/Runtime/main.h"
+#include <cstdlib>
+
+#ifdef _WIN32
+#include <lmcons.h> // UNLEN=256
+#define LOGIN_NAME_MAX UNLEN
+#else
+#include <limits.h>
+#endif
+
+using namespace Fortran::runtime;
+
+struct ExtensionTests : CrashHandlerFixture {};
+
+TEST_F(ExtensionTests, GetlogGetName) {
+  const int charLen{3};
+  char input[charLen]{"\0\0"};
+
+  FORTRAN_PROCEDURE_NAME(getlog)
+  (reinterpret_cast<std::int8_t *>(input), charLen);
+
+  EXPECT_NE(input[0], '\0');
+}
+
+TEST_F(ExtensionTests, GetlogPadSpace) {
+  const int charLen{LOGIN_NAME_MAX +
+      2}; // guarantee 1 char longer than max, last char should be pad space
+  char input[charLen];
+
+  FORTRAN_PROCEDURE_NAME(getlog)
+  (reinterpret_cast<std::int8_t *>(input), charLen);
+
+  EXPECT_EQ(input[charLen - 1], ' ');
+}
\ No newline at end of file

>From ea2ea3b3a473e0d0092ad8865c49f7e627a03c64 Mon Sep 17 00:00:00 2001
From: Yi Wu <yi.wu2 at arm.com>
Date: Wed, 6 Dec 2023 10:57:28 +0000
Subject: [PATCH 15/15] doc change: move getlog to Library subroutine row

---
 flang/docs/Intrinsics.md | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/flang/docs/Intrinsics.md b/flang/docs/Intrinsics.md
index fd16ed3bcd6b6..4a6e68d708efb 100644
--- a/flang/docs/Intrinsics.md
+++ b/flang/docs/Intrinsics.md
@@ -657,6 +657,11 @@ CALL CO_REDUCE
 CALL CO_SUM
 ```
 
+### Library subroutine 
+```
+CALL GETLOG(USRNAME)
+```
+
 ## Non-standard intrinsics
 ### PGI
 ```
@@ -751,9 +756,10 @@ 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, GETLOG, 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_CLOCK |
 | Atomic intrinsic subroutines | ATOMIC_ADD |
 | Collective intrinsic subroutines | CO_REDUCE |
+| Library subroutines | GETLOG|
 
 
 ### Intrinsic Function Folding



More information about the cfe-commits mailing list