https://github.com/yi-wu-arm updated https://github.com/llvm/llvm-project/pull/74628
>From 0e98aa7ca15b05b91813eaeeb6ae1305e5f5384d Mon Sep 17 00:00:00 2001 From: Yi Wu <yi....@arm.com> Date: Mon, 6 Nov 2023 19:49:13 +0000 Subject: [PATCH 01/23] 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....@arm.com> Date: Tue, 7 Nov 2023 15:09:23 +0000 Subject: [PATCH 02/23] 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....@arm.com> Date: Fri, 10 Nov 2023 10:44:12 +0000 Subject: [PATCH 03/23] 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....@arm.com> Date: Fri, 10 Nov 2023 15:06:08 +0000 Subject: [PATCH 04/23] 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 <yiw...@wdev-yiwu02.arm.com> Date: Mon, 13 Nov 2023 16:03:19 +0000 Subject: [PATCH 05/23] 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....@arm.com> Date: Wed, 15 Nov 2023 11:06:01 +0000 Subject: [PATCH 06/23] 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....@arm.com> Date: Wed, 22 Nov 2023 13:40:38 +0000 Subject: [PATCH 07/23] 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-...@users.noreply.github.com> Date: Wed, 22 Nov 2023 14:04:46 +0000 Subject: [PATCH 08/23] Update flang/runtime/extensions.cpp: brace initialization Co-authored-by: Kiran Chandramohan <kiranchandramo...@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-...@users.noreply.github.com> Date: Wed, 22 Nov 2023 14:06:08 +0000 Subject: [PATCH 09/23] Update flang/runtime/extensions.cpp: brace initialization Co-authored-by: Kiran Chandramohan <kiranchandramo...@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....@arm.com> Date: Wed, 22 Nov 2023 15:07:15 +0000 Subject: [PATCH 10/23] 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....@arm.com> Date: Mon, 27 Nov 2023 17:24:18 +0000 Subject: [PATCH 11/23] 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....@arm.com> Date: Tue, 5 Dec 2023 13:14:58 +0000 Subject: [PATCH 12/23] 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....@arm.com> Date: Tue, 5 Dec 2023 16:39:57 +0000 Subject: [PATCH 13/23] 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....@arm.com> Date: Wed, 6 Dec 2023 10:40:37 +0000 Subject: [PATCH 14/23] 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....@arm.com> Date: Wed, 6 Dec 2023 10:57:28 +0000 Subject: [PATCH 15/23] 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 >From 67d404585846786455310b55b44b2d1ac41caf7c Mon Sep 17 00:00:00 2001 From: Yi Wu <yi....@arm.com> Date: Wed, 6 Dec 2023 12:42:37 +0000 Subject: [PATCH 16/23] small fixes to tests --- flang/runtime/extensions.cpp | 5 ++++- flang/unittests/Runtime/ExtensionTest.cpp | 9 +++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp index 21d2ece06865a..2457731ba7b92 100644 --- a/flang/runtime/extensions.cpp +++ b/flang/runtime/extensions.cpp @@ -80,13 +80,16 @@ void FORTRAN_PROCEDURE_NAME(getarg)( n, &value, nullptr, nullptr, __FILE__, __LINE__); } +// CALL GETLOG(USRNAME) void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *arg, std::int64_t length) { const int nameMaxLen{LOGIN_NAME_MAX + 1}; char str[nameMaxLen]; int error{getlogin_r(str, nameMaxLen)}; Terminator terminator{__FILE__, __LINE__}; - RUNTIME_CHECK(terminator, error == 0); + if (error != 0) { + terminator.Crash("getlogin_r fail with a nonzero value: %d.\n", error); + } // find first \0 in string then pad from there CopyAndPad(reinterpret_cast<char *>(arg), str, length, std::strlen(str)); diff --git a/flang/unittests/Runtime/ExtensionTest.cpp b/flang/unittests/Runtime/ExtensionTest.cpp index d1147f18c2159..a74da4e7005ba 100644 --- a/flang/unittests/Runtime/ExtensionTest.cpp +++ b/flang/unittests/Runtime/ExtensionTest.cpp @@ -16,10 +16,10 @@ #include <cstdlib> #ifdef _WIN32 -#include <lmcons.h> // UNLEN=256 +#include <lmcons.h> // UNLEN #define LOGIN_NAME_MAX UNLEN #else -#include <limits.h> +#include <limits.h> // LOGIN_NAME_MAX #endif using namespace Fortran::runtime; @@ -37,8 +37,9 @@ TEST_F(ExtensionTests, GetlogGetName) { } TEST_F(ExtensionTests, GetlogPadSpace) { - const int charLen{LOGIN_NAME_MAX + - 2}; // guarantee 1 char longer than max, last char should be pad space + // guarantee 1 char longer than max, last char should be pad with space + const int charLen{LOGIN_NAME_MAX + 2}; + char input[charLen]; FORTRAN_PROCEDURE_NAME(getlog) >From 3ac03a67ea06185f8cdbe1f0cbb8c88405f3492b Mon Sep 17 00:00:00 2001 From: Yi Wu <yi....@arm.com> Date: Wed, 6 Dec 2023 13:11:23 +0000 Subject: [PATCH 17/23] use env to set and test user name --- flang/unittests/Runtime/CMakeLists.txt | 1 - flang/unittests/Runtime/CommandTest.cpp | 31 ++++++++++++++ flang/unittests/Runtime/ExtensionTest.cpp | 49 ----------------------- 3 files changed, 31 insertions(+), 50 deletions(-) delete mode 100644 flang/unittests/Runtime/ExtensionTest.cpp diff --git a/flang/unittests/Runtime/CMakeLists.txt b/flang/unittests/Runtime/CMakeLists.txt index a25472409ac94..23f02aa751246 100644 --- a/flang/unittests/Runtime/CMakeLists.txt +++ b/flang/unittests/Runtime/CMakeLists.txt @@ -7,7 +7,6 @@ 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 d003eacd177e8..6287e6a40c681 100644 --- a/flang/unittests/Runtime/CommandTest.cpp +++ b/flang/unittests/Runtime/CommandTest.cpp @@ -398,6 +398,7 @@ class EnvironmentVariables : public CommandFixture { protected: EnvironmentVariables() : CommandFixture(0, nullptr) { SetEnv("NAME", "VALUE"); + SetEnv("LOGNAME", "loginName"); SetEnv("EMPTY", ""); } @@ -495,3 +496,33 @@ TEST_F(EnvironmentVariables, ErrMsgTooShort) { 1); CheckDescriptorEqStr(errMsg.get(), "Mis"); } + +TEST_F(EnvironmentVariables, GetlogGetName) { + const int charLen{11}; + char input[charLen]{"XXXXXXXXX"}; + + FORTRAN_PROCEDURE_NAME(getlog) + (reinterpret_cast<std::int8_t *>(input), charLen); + + EXPECT_NE(input, "loginName"); +} + +TEST_F(EnvironmentVariables, GetlogBufferShort) { + const int charLen{7}; + char input[charLen]{"XXXXXX"}; + + FORTRAN_PROCEDURE_NAME(getlog) + (reinterpret_cast<std::int8_t *>(input), charLen); + + EXPECT_NE(input, "loginN"); +} + +TEST_F(EnvironmentVariables, GetlogPadSpace) { + const int charLen{12}; + char input[charLen]{"XXXXXXXXXX"}; + + FORTRAN_PROCEDURE_NAME(getlog) + (reinterpret_cast<std::int8_t *>(input), charLen); + + EXPECT_NE(input, "loginName "); +} diff --git a/flang/unittests/Runtime/ExtensionTest.cpp b/flang/unittests/Runtime/ExtensionTest.cpp deleted file mode 100644 index a74da4e7005ba..0000000000000 --- a/flang/unittests/Runtime/ExtensionTest.cpp +++ /dev/null @@ -1,49 +0,0 @@ -//===-- 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 -#define LOGIN_NAME_MAX UNLEN -#else -#include <limits.h> // LOGIN_NAME_MAX -#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) { - // guarantee 1 char longer than max, last char should be pad with space - const int charLen{LOGIN_NAME_MAX + 2}; - - 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 266a2695f14bdc292bd1f4249c1255520aa1c9f3 Mon Sep 17 00:00:00 2001 From: Yi Wu <yi....@arm.com> Date: Wed, 6 Dec 2023 15:28:41 +0000 Subject: [PATCH 18/23] get logname from environment variable LOGNAME not from getlog_r, and restore CopyAndPad Suggested: https://man.archlinux.org/man/getlogin_r.3.en For most purposes, it is more useful to use the environment variable LOGNAME to find out who the user is. This is more flexible precisely because the user can set LOGNAME arbitrarily. POSIX specification: LOGNAME The system shall initialize this variable at the time of login to be the user's login name. Note that at this point, this implementation might not work on Windows --- flang/include/flang/Runtime/character.h | 31 ------------ flang/runtime/character.cpp | 21 +++++++++ flang/runtime/extensions.cpp | 63 +++++-------------------- flang/unittests/Runtime/CommandTest.cpp | 13 +++-- 4 files changed, 44 insertions(+), 84 deletions(-) diff --git a/flang/include/flang/Runtime/character.h b/flang/include/flang/Runtime/character.h index 52cd48f9637e6..24f26920bdd2c 100644 --- a/flang/include/flang/Runtime/character.h +++ b/flang/include/flang/Runtime/character.h @@ -6,37 +6,6 @@ // //===----------------------------------------------------------------------===// -// character.h -#ifndef CHARACTER_H -#define CHARACTER_H - -#include <algorithm> -#include <cstddef> -#include <cstring> - -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, std::min(toChars, 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 2a62a60874e74..99528ce837f46 100644 --- a/flang/runtime/character.cpp +++ b/flang/runtime/character.cpp @@ -462,6 +462,27 @@ 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 2457731ba7b92..e2b290c262a11 100644 --- a/flang/runtime/extensions.cpp +++ b/flang/runtime/extensions.cpp @@ -10,52 +10,10 @@ // extensions that will eventually be implemented in Fortran. #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" -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#define NOMINMAX -#include <windows.h> - -#include <cstdlib> // wcstombs_s -#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{UNLEN + 1}; - - if (GetUserName(w_username, &nameLen)) { - // Convert the wchar_t string to a regular C string using wcstombs_s - if (wcstombs_s(nullptr, buf, bufSize, w_username, _TRUNCATE) != 0) { - // 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) { - std::memset(buf, ' ', bufSize - 1); - buf[bufSize - 1] = '\0'; - return 0; -} -#endif - extern "C" { namespace Fortran::runtime { @@ -82,17 +40,22 @@ void FORTRAN_PROCEDURE_NAME(getarg)( // CALL GETLOG(USRNAME) void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *arg, std::int64_t length) { - const int nameMaxLen{LOGIN_NAME_MAX + 1}; - char str[nameMaxLen]; + //Get environment variable LOGNAME + const int charLen = 8; + char envName[charLen] = "LOGNAME"; + std::size_t n{std::strlen(envName)}; + Descriptor name{*Descriptor::Create(1, n, envName, 0)}; + std::memcpy(name.OffsetElement(), envName, n); + + Descriptor value{*Descriptor::Create(1, length, arg, 0)}; + + RTNAME(GetEnvVariable) + (name, &value, nullptr, false, nullptr, __FILE__, __LINE__); - int error{getlogin_r(str, nameMaxLen)}; - Terminator terminator{__FILE__, __LINE__}; - if (error != 0) { - terminator.Crash("getlogin_r fail with a nonzero value: %d.\n", error); - } + std::memcpy(reinterpret_cast<char *>(arg), value.OffsetElement(), length); // find first \0 in string then pad from there - CopyAndPad(reinterpret_cast<char *>(arg), str, length, std::strlen(str)); + // CopyAndPad(reinterpret_cast<char *>(arg), value.OffsetElement(), length, value.ElementBytes()); } } // namespace Fortran::runtime diff --git a/flang/unittests/Runtime/CommandTest.cpp b/flang/unittests/Runtime/CommandTest.cpp index 6287e6a40c681..f0958f58474db 100644 --- a/flang/unittests/Runtime/CommandTest.cpp +++ b/flang/unittests/Runtime/CommandTest.cpp @@ -71,6 +71,13 @@ class CommandFixture : public ::testing::Test { << std::string{value->OffsetElement(), value->ElementBytes()}; } + void CheckCharEqStr(const char *value, const std::string &expected) const { + ASSERT_NE(value, nullptr); + EXPECT_EQ(std::strncmp(value, expected.c_str(), expected.size()), 0) + << "expected: " << expected << "\n" + << "value: " << value; + } + template <typename INT_T = std::int64_t> void CheckDescriptorEqInt( const Descriptor *value, const INT_T expected) const { @@ -504,7 +511,7 @@ TEST_F(EnvironmentVariables, GetlogGetName) { FORTRAN_PROCEDURE_NAME(getlog) (reinterpret_cast<std::int8_t *>(input), charLen); - EXPECT_NE(input, "loginName"); + CheckCharEqStr(input, "loginName"); } TEST_F(EnvironmentVariables, GetlogBufferShort) { @@ -514,7 +521,7 @@ TEST_F(EnvironmentVariables, GetlogBufferShort) { FORTRAN_PROCEDURE_NAME(getlog) (reinterpret_cast<std::int8_t *>(input), charLen); - EXPECT_NE(input, "loginN"); + CheckCharEqStr(input, "loginN"); } TEST_F(EnvironmentVariables, GetlogPadSpace) { @@ -524,5 +531,5 @@ TEST_F(EnvironmentVariables, GetlogPadSpace) { FORTRAN_PROCEDURE_NAME(getlog) (reinterpret_cast<std::int8_t *>(input), charLen); - EXPECT_NE(input, "loginName "); + CheckCharEqStr(input, "loginName "); } >From 40ff3730c06c92a60d30b41b3e797860598bd814 Mon Sep 17 00:00:00 2001 From: Yi Wu <yiw...@wdev-yiwu02.arm.com> Date: Wed, 6 Dec 2023 17:08:18 +0000 Subject: [PATCH 19/23] windows use USERNAME environment variable --- flang/runtime/extensions.cpp | 14 ++++++-------- flang/unittests/Runtime/CommandTest.cpp | 4 ++++ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp index e2b290c262a11..893d3a7de0424 100644 --- a/flang/runtime/extensions.cpp +++ b/flang/runtime/extensions.cpp @@ -40,22 +40,20 @@ void FORTRAN_PROCEDURE_NAME(getarg)( // CALL GETLOG(USRNAME) void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *arg, std::int64_t length) { - //Get environment variable LOGNAME + // get username from environment variable +#ifdef _WIN32 + const int charLen = 9; + char envName[charLen] = "USERNAME"; +#else const int charLen = 8; char envName[charLen] = "LOGNAME"; +#endif std::size_t n{std::strlen(envName)}; Descriptor name{*Descriptor::Create(1, n, envName, 0)}; - std::memcpy(name.OffsetElement(), envName, n); - Descriptor value{*Descriptor::Create(1, length, arg, 0)}; RTNAME(GetEnvVariable) (name, &value, nullptr, false, nullptr, __FILE__, __LINE__); - - std::memcpy(reinterpret_cast<char *>(arg), value.OffsetElement(), length); - - // find first \0 in string then pad from there - // CopyAndPad(reinterpret_cast<char *>(arg), value.OffsetElement(), length, value.ElementBytes()); } } // namespace Fortran::runtime diff --git a/flang/unittests/Runtime/CommandTest.cpp b/flang/unittests/Runtime/CommandTest.cpp index f0958f58474db..0163dada41b3f 100644 --- a/flang/unittests/Runtime/CommandTest.cpp +++ b/flang/unittests/Runtime/CommandTest.cpp @@ -405,7 +405,11 @@ class EnvironmentVariables : public CommandFixture { protected: EnvironmentVariables() : CommandFixture(0, nullptr) { SetEnv("NAME", "VALUE"); +#ifdef _WIN32 + SetEnv("USERNAME", "loginName"); +#else SetEnv("LOGNAME", "loginName"); +#endif SetEnv("EMPTY", ""); } >From 2cdaa2d6fbb040b9777ca03447c37ccc94a000df Mon Sep 17 00:00:00 2001 From: Yi Wu <yi....@arm.com> Date: Wed, 6 Dec 2023 17:54:36 +0000 Subject: [PATCH 20/23] Revert "get logname from environment variable LOGNAME not from getlog_r, and restore CopyAndPad" This reverts commit 266a2695f14bdc292bd1f4249c1255520aa1c9f3. --- flang/include/flang/Runtime/character.h | 31 ++++++++++++ flang/runtime/character.cpp | 21 -------- flang/runtime/extensions.cpp | 65 ++++++++++++++++++++----- flang/unittests/Runtime/CommandTest.cpp | 13 ++--- 4 files changed, 86 insertions(+), 44 deletions(-) diff --git a/flang/include/flang/Runtime/character.h b/flang/include/flang/Runtime/character.h index 24f26920bdd2c..52cd48f9637e6 100644 --- a/flang/include/flang/Runtime/character.h +++ b/flang/include/flang/Runtime/character.h @@ -6,6 +6,37 @@ // //===----------------------------------------------------------------------===// +// character.h +#ifndef CHARACTER_H +#define CHARACTER_H + +#include <algorithm> +#include <cstddef> +#include <cstring> + +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, std::min(toChars, 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 99528ce837f46..2a62a60874e74 100644 --- a/flang/runtime/character.cpp +++ b/flang/runtime/character.cpp @@ -462,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 893d3a7de0424..2457731ba7b92 100644 --- a/flang/runtime/extensions.cpp +++ b/flang/runtime/extensions.cpp @@ -10,10 +10,52 @@ // extensions that will eventually be implemented in Fortran. #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" +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include <windows.h> + +#include <cstdlib> // wcstombs_s +#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{UNLEN + 1}; + + if (GetUserName(w_username, &nameLen)) { + // Convert the wchar_t string to a regular C string using wcstombs_s + if (wcstombs_s(nullptr, buf, bufSize, w_username, _TRUNCATE) != 0) { + // 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) { + std::memset(buf, ' ', bufSize - 1); + buf[bufSize - 1] = '\0'; + return 0; +} +#endif + extern "C" { namespace Fortran::runtime { @@ -40,20 +82,17 @@ void FORTRAN_PROCEDURE_NAME(getarg)( // CALL GETLOG(USRNAME) void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *arg, std::int64_t length) { - // get username from environment variable -#ifdef _WIN32 - const int charLen = 9; - char envName[charLen] = "USERNAME"; -#else - const int charLen = 8; - char envName[charLen] = "LOGNAME"; -#endif - std::size_t n{std::strlen(envName)}; - Descriptor name{*Descriptor::Create(1, n, envName, 0)}; - Descriptor value{*Descriptor::Create(1, length, arg, 0)}; + const int nameMaxLen{LOGIN_NAME_MAX + 1}; + char str[nameMaxLen]; + + int error{getlogin_r(str, nameMaxLen)}; + Terminator terminator{__FILE__, __LINE__}; + if (error != 0) { + terminator.Crash("getlogin_r fail with a nonzero value: %d.\n", error); + } - RTNAME(GetEnvVariable) - (name, &value, nullptr, false, nullptr, __FILE__, __LINE__); + // find first \0 in string then pad from there + CopyAndPad(reinterpret_cast<char *>(arg), str, length, std::strlen(str)); } } // namespace Fortran::runtime diff --git a/flang/unittests/Runtime/CommandTest.cpp b/flang/unittests/Runtime/CommandTest.cpp index 0163dada41b3f..471a83143bde1 100644 --- a/flang/unittests/Runtime/CommandTest.cpp +++ b/flang/unittests/Runtime/CommandTest.cpp @@ -71,13 +71,6 @@ class CommandFixture : public ::testing::Test { << std::string{value->OffsetElement(), value->ElementBytes()}; } - void CheckCharEqStr(const char *value, const std::string &expected) const { - ASSERT_NE(value, nullptr); - EXPECT_EQ(std::strncmp(value, expected.c_str(), expected.size()), 0) - << "expected: " << expected << "\n" - << "value: " << value; - } - template <typename INT_T = std::int64_t> void CheckDescriptorEqInt( const Descriptor *value, const INT_T expected) const { @@ -515,7 +508,7 @@ TEST_F(EnvironmentVariables, GetlogGetName) { FORTRAN_PROCEDURE_NAME(getlog) (reinterpret_cast<std::int8_t *>(input), charLen); - CheckCharEqStr(input, "loginName"); + EXPECT_NE(input, "loginName"); } TEST_F(EnvironmentVariables, GetlogBufferShort) { @@ -525,7 +518,7 @@ TEST_F(EnvironmentVariables, GetlogBufferShort) { FORTRAN_PROCEDURE_NAME(getlog) (reinterpret_cast<std::int8_t *>(input), charLen); - CheckCharEqStr(input, "loginN"); + EXPECT_NE(input, "loginN"); } TEST_F(EnvironmentVariables, GetlogPadSpace) { @@ -535,5 +528,5 @@ TEST_F(EnvironmentVariables, GetlogPadSpace) { FORTRAN_PROCEDURE_NAME(getlog) (reinterpret_cast<std::int8_t *>(input), charLen); - CheckCharEqStr(input, "loginName "); + EXPECT_NE(input, "loginName "); } >From ee6c27ee93da55367e931ebb17e34db9d43d6b7a Mon Sep 17 00:00:00 2001 From: Yi Wu <yi....@arm.com> Date: Wed, 6 Dec 2023 18:01:35 +0000 Subject: [PATCH 21/23] if getlogin returns an error, then get name from environment variable --- flang/runtime/extensions.cpp | 21 +++++++++++++---- flang/unittests/Runtime/CommandTest.cpp | 31 ++++++++++++------------- 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp index 2457731ba7b92..a1afa139c3fc5 100644 --- a/flang/runtime/extensions.cpp +++ b/flang/runtime/extensions.cpp @@ -88,11 +88,24 @@ void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *arg, std::int64_t length) { int error{getlogin_r(str, nameMaxLen)}; Terminator terminator{__FILE__, __LINE__}; if (error != 0) { - terminator.Crash("getlogin_r fail with a nonzero value: %d.\n", error); - } + // if there is error, then get username from environment variable +#ifdef _WIN32 + const int charLen = 9; + char envName[charLen] = "USERNAME"; +#else + const int charLen = 8; + char envName[charLen] = "LOGNAME"; +#endif + std::size_t n{std::strlen(envName)}; + Descriptor name{*Descriptor::Create(1, n, envName, 0)}; + Descriptor value{*Descriptor::Create(1, length, arg, 0)}; - // find first \0 in string then pad from there - CopyAndPad(reinterpret_cast<char *>(arg), str, length, std::strlen(str)); + RTNAME(GetEnvVariable) + (name, &value, nullptr, false, nullptr, __FILE__, __LINE__); + } else { + // find first \0 in string then pad from there + CopyAndPad(reinterpret_cast<char *>(arg), str, length, std::strlen(str)); + } } } // namespace Fortran::runtime diff --git a/flang/unittests/Runtime/CommandTest.cpp b/flang/unittests/Runtime/CommandTest.cpp index 471a83143bde1..a368b70f7c696 100644 --- a/flang/unittests/Runtime/CommandTest.cpp +++ b/flang/unittests/Runtime/CommandTest.cpp @@ -14,6 +14,13 @@ #include "flang/Runtime/main.h" #include <cstdlib> +#ifdef _WIN32 +#include <lmcons.h> // UNLEN=256 +#define LOGIN_NAME_MAX UNLEN +#else +#include <limits.h> // LOGIN_NAME_MAX +#endif + using namespace Fortran::runtime; template <std::size_t n = 64> @@ -501,32 +508,24 @@ TEST_F(EnvironmentVariables, ErrMsgTooShort) { CheckDescriptorEqStr(errMsg.get(), "Mis"); } +// username first char must not be null TEST_F(EnvironmentVariables, GetlogGetName) { - const int charLen{11}; - char input[charLen]{"XXXXXXXXX"}; - - FORTRAN_PROCEDURE_NAME(getlog) - (reinterpret_cast<std::int8_t *>(input), charLen); - - EXPECT_NE(input, "loginName"); -} - -TEST_F(EnvironmentVariables, GetlogBufferShort) { - const int charLen{7}; - char input[charLen]{"XXXXXX"}; + const int charLen{3}; + char input[charLen]{"\0\0"}; FORTRAN_PROCEDURE_NAME(getlog) (reinterpret_cast<std::int8_t *>(input), charLen); - EXPECT_NE(input, "loginN"); + EXPECT_NE(input[0], '\0'); } TEST_F(EnvironmentVariables, GetlogPadSpace) { - const int charLen{12}; - char input[charLen]{"XXXXXXXXXX"}; + // guarantee 1 char longer than max, last char should be pad space + const int charLen{LOGIN_NAME_MAX + 2}; + char input[charLen]; FORTRAN_PROCEDURE_NAME(getlog) (reinterpret_cast<std::int8_t *>(input), charLen); - EXPECT_NE(input, "loginName "); + EXPECT_EQ(input[charLen - 1], ' '); } >From 2a24b4342af206e5455c9b4953214e77e105abac Mon Sep 17 00:00:00 2001 From: Yi Wu <yi....@arm.com> Date: Wed, 6 Dec 2023 18:55:58 +0000 Subject: [PATCH 22/23] brace-initialization, doc text moved, reorder if-else for clarity and some minor changes --- flang/docs/Intrinsics.md | 10 +++++----- flang/runtime/extensions.cpp | 26 +++++++++++--------------- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/flang/docs/Intrinsics.md b/flang/docs/Intrinsics.md index 4a6e68d708efb..189920a0881b2 100644 --- a/flang/docs/Intrinsics.md +++ b/flang/docs/Intrinsics.md @@ -657,11 +657,6 @@ CALL CO_REDUCE CALL CO_SUM ``` -### Library subroutine -``` -CALL GETLOG(USRNAME) -``` - ## Non-standard intrinsics ### PGI ``` @@ -700,6 +695,11 @@ CACHESIZE, EOF, FP_CLASS, INT_PTR_KIND, ISNAN, LOC MALLOC ``` +### Library subroutine +``` +CALL GETLOG(USRNAME) +``` + ## Intrinsic Procedure Name Resolution When the name of a procedure in a program is the same as the one of an intrinsic diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp index a1afa139c3fc5..c2fa485531942 100644 --- a/flang/runtime/extensions.cpp +++ b/flang/runtime/extensions.cpp @@ -10,7 +10,6 @@ // extensions that will eventually be implemented in Fortran. #include "flang/Runtime/extensions.h" -#include "terminator.h" #include "flang/Runtime/character.h" #include "flang/Runtime/command.h" #include "flang/Runtime/descriptor.h" @@ -37,11 +36,10 @@ inline int getlogin_r(char *buf, size_t bufSize) { // Conversion failed return -1; } - return (buf[0] == 0 ? -1 : 0); + return (buf[0] == '\0' ? -1 : 0); } else { return -1; } - return -1; } #elif _REENTRANT || _POSIX_C_SOURCE >= 199506L @@ -86,25 +84,23 @@ void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *arg, std::int64_t length) { char str[nameMaxLen]; int error{getlogin_r(str, nameMaxLen)}; - Terminator terminator{__FILE__, __LINE__}; - if (error != 0) { - // if there is error, then get username from environment variable + if (error == 0) { + // no error: find first \0 in string then pad from there + CopyAndPad(reinterpret_cast<char *>(arg), str, length, std::strlen(str)); + } else { + // error occur: get username from environment variable #ifdef _WIN32 - const int charLen = 9; - char envName[charLen] = "USERNAME"; + const int charLen{9}; + char envName[charLen]{"USERNAME"}; #else - const int charLen = 8; - char envName[charLen] = "LOGNAME"; + const int charLen{8}; + char envName[charLen]{"LOGNAME"}; #endif - std::size_t n{std::strlen(envName)}; - Descriptor name{*Descriptor::Create(1, n, envName, 0)}; + Descriptor name{*Descriptor::Create(1, charLen, envName, 0)}; Descriptor value{*Descriptor::Create(1, length, arg, 0)}; RTNAME(GetEnvVariable) (name, &value, nullptr, false, nullptr, __FILE__, __LINE__); - } else { - // find first \0 in string then pad from there - CopyAndPad(reinterpret_cast<char *>(arg), str, length, std::strlen(str)); } } >From 499a1622c8eab1cdd37a9476bfd284fc30e44034 Mon Sep 17 00:00:00 2001 From: Yi Wu <yi....@arm.com> Date: Wed, 6 Dec 2023 19:05:04 +0000 Subject: [PATCH 23/23] use sizeof as length, remove charLen variable --- flang/runtime/extensions.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp index c2fa485531942..f2cb8e5e567f4 100644 --- a/flang/runtime/extensions.cpp +++ b/flang/runtime/extensions.cpp @@ -90,13 +90,11 @@ void FORTRAN_PROCEDURE_NAME(getlog)(std::int8_t *arg, std::int64_t length) { } else { // error occur: get username from environment variable #ifdef _WIN32 - const int charLen{9}; - char envName[charLen]{"USERNAME"}; + char envName[]{"USERNAME"}; #else - const int charLen{8}; - char envName[charLen]{"LOGNAME"}; + char envName[]{"LOGNAME"}; #endif - Descriptor name{*Descriptor::Create(1, charLen, envName, 0)}; + Descriptor name{*Descriptor::Create(1, sizeof(envName), envName, 0)}; Descriptor value{*Descriptor::Create(1, length, arg, 0)}; RTNAME(GetEnvVariable) _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits