Skip to content

Commit 18af032

Browse files
yiwu0b11Yi Wukiranchandramohan
authored
[flang] add GETLOG runtime and extension implementation: get login username (#74628)
Get login username, ussage: ``` CHARACTER(32) :: login CALL getlog(login) WRITE(*,*) login ``` getlog is required for an exascale proxyapp. https://proxyapps.exascaleproject.org/app/minismac2d/ https://github.com/Mantevo/miniSMAC/blob/f90446714226eeef650b78bce06ca4967792e74d/ref/smac2d.f#L615 https://github.com/Mantevo/miniSMAC/blob/f90446714226eeef650b78bce06ca4967792e74d/ref/smac2d.f#L1570 --------- Co-authored-by: Yi Wu <[email protected]> Co-authored-by: Yi Wu <[email protected]> Co-authored-by: Kiran Chandramohan <[email protected]>
1 parent 5b9be0e commit 18af032

File tree

6 files changed

+155
-21
lines changed

6 files changed

+155
-21
lines changed

flang/docs/Intrinsics.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,11 @@ CACHESIZE, EOF, FP_CLASS, INT_PTR_KIND, ISNAN, LOC
695695
MALLOC
696696
```
697697

698+
### Library subroutine
699+
```
700+
CALL GETLOG(USRNAME)
701+
```
702+
698703
## Intrinsic Procedure Name Resolution
699704

700705
When the name of a procedure in a program is the same as the one of an intrinsic
@@ -754,6 +759,7 @@ This phase currently supports all the intrinsic procedures listed above but the
754759
| 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 |
755760
| Atomic intrinsic subroutines | ATOMIC_ADD |
756761
| Collective intrinsic subroutines | CO_REDUCE |
762+
| Library subroutines | GETLOG|
757763

758764

759765
### Intrinsic Function Folding

flang/include/flang/Runtime/extensions.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#define FORTRAN_PROCEDURE_NAME(name) name##_
1616

17+
#include <cstddef>
1718
#include <cstdint>
1819

1920
extern "C" {
@@ -28,5 +29,8 @@ std::int32_t FORTRAN_PROCEDURE_NAME(iargc)();
2829
void FORTRAN_PROCEDURE_NAME(getarg)(
2930
std::int32_t &n, std::int8_t *arg, std::int64_t length);
3031

32+
// GNU extension subroutine GETLOG(C).
33+
void FORTRAN_PROCEDURE_NAME(getlog)(std::byte *name, std::int64_t length);
34+
3135
} // extern "C"
3236
#endif // FORTRAN_RUNTIME_EXTENSIONS_H_

flang/runtime/character.cpp

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "tools.h"
1212
#include "flang/Common/bit-population-count.h"
1313
#include "flang/Common/uint128.h"
14+
#include "flang/Runtime/character.h"
1415
#include "flang/Runtime/cpp-type.h"
1516
#include "flang/Runtime/descriptor.h"
1617
#include <algorithm>
@@ -464,27 +465,6 @@ static void GeneralCharFuncKind(Descriptor &result, const Descriptor &string,
464465
}
465466
}
466467

467-
template <typename TO, typename FROM>
468-
static void CopyAndPad(
469-
TO *to, const FROM *from, std::size_t toChars, std::size_t fromChars) {
470-
if constexpr (sizeof(TO) != sizeof(FROM)) {
471-
std::size_t copyChars{std::min(toChars, fromChars)};
472-
for (std::size_t j{0}; j < copyChars; ++j) {
473-
to[j] = from[j];
474-
}
475-
for (std::size_t j{copyChars}; j < toChars; ++j) {
476-
to[j] = static_cast<TO>(' ');
477-
}
478-
} else if (toChars <= fromChars) {
479-
std::memcpy(to, from, toChars * sizeof(TO));
480-
} else {
481-
std::memcpy(to, from, fromChars * sizeof(TO));
482-
for (std::size_t j{fromChars}; j < toChars; ++j) {
483-
to[j] = static_cast<TO>(' ');
484-
}
485-
}
486-
}
487-
488468
template <typename CHAR, bool ISMIN>
489469
static void MaxMinHelper(Descriptor &accumulator, const Descriptor &x,
490470
const Terminator &terminator) {

flang/runtime/extensions.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,29 @@
1010
// extensions that will eventually be implemented in Fortran.
1111

1212
#include "flang/Runtime/extensions.h"
13+
#include "tools.h"
1314
#include "flang/Runtime/command.h"
1415
#include "flang/Runtime/descriptor.h"
1516
#include "flang/Runtime/io-api.h"
1617

18+
#if _REENTRANT || _POSIX_C_SOURCE >= 199506L
19+
// System is posix-compliant and has getlogin_r
20+
#include <unistd.h>
21+
#endif
22+
1723
extern "C" {
1824

1925
namespace Fortran::runtime {
26+
27+
void GetUsernameEnvVar(
28+
const char *envName, std::byte *arg, std::int64_t length) {
29+
Descriptor name{*Descriptor::Create(
30+
1, std::strlen(envName) + 1, const_cast<char *>(envName), 0)};
31+
Descriptor value{*Descriptor::Create(1, length, arg, 0)};
32+
33+
RTNAME(GetEnvVariable)
34+
(name, &value, nullptr, false, nullptr, __FILE__, __LINE__);
35+
}
2036
namespace io {
2137
// SUBROUTINE FLUSH(N)
2238
// FLUSH N
@@ -37,5 +53,28 @@ void FORTRAN_PROCEDURE_NAME(getarg)(
3753
(void)RTNAME(GetCommandArgument)(
3854
n, &value, nullptr, nullptr, __FILE__, __LINE__);
3955
}
56+
57+
// CALL GETLOG(USRNAME)
58+
void FORTRAN_PROCEDURE_NAME(getlog)(std::byte *arg, std::int64_t length) {
59+
#if _REENTRANT || _POSIX_C_SOURCE >= 199506L
60+
const int nameMaxLen{LOGIN_NAME_MAX + 1};
61+
char str[nameMaxLen];
62+
63+
int error{getlogin_r(str, nameMaxLen)};
64+
if (error == 0) {
65+
// no error: find first \0 in string then pad from there
66+
CopyAndPad(reinterpret_cast<char *>(arg), str, length, std::strlen(str));
67+
} else {
68+
// error occur: get username from environment variable
69+
GetUsernameEnvVar("LOGNAME", arg, length);
70+
}
71+
#elif _WIN32
72+
// Get username from environment to avoid link to Advapi32.lib
73+
GetUsernameEnvVar("USERNAME", arg, length);
74+
#else
75+
GetUsernameEnvVar("LOGNAME", arg, length);
76+
#endif
77+
}
78+
4079
} // namespace Fortran::runtime
4180
} // extern "C"

flang/runtime/tools.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,5 +411,27 @@ RT_API_ATTRS void ShallowCopy(const Descriptor &to, const Descriptor &from,
411411
bool toIsContiguous, bool fromIsContiguous);
412412
RT_API_ATTRS void ShallowCopy(const Descriptor &to, const Descriptor &from);
413413

414+
// Defines a utility function for copying and padding characters
415+
template <typename TO, typename FROM>
416+
RT_API_ATTRS void CopyAndPad(
417+
TO *to, const FROM *from, std::size_t toChars, std::size_t fromChars) {
418+
if constexpr (sizeof(TO) != sizeof(FROM)) {
419+
std::size_t copyChars{std::min(toChars, fromChars)};
420+
for (std::size_t j{0}; j < copyChars; ++j) {
421+
to[j] = from[j];
422+
}
423+
for (std::size_t j{copyChars}; j < toChars; ++j) {
424+
to[j] = static_cast<TO>(' ');
425+
}
426+
} else if (toChars <= fromChars) {
427+
std::memcpy(to, from, toChars * sizeof(TO));
428+
} else {
429+
std::memcpy(to, from, std::min(toChars, fromChars) * sizeof(TO));
430+
for (std::size_t j{fromChars}; j < toChars; ++j) {
431+
to[j] = static_cast<TO>(' ');
432+
}
433+
}
434+
}
435+
414436
} // namespace Fortran::runtime
415437
#endif // FORTRAN_RUNTIME_TOOLS_H_

flang/unittests/Runtime/CommandTest.cpp

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,15 @@
1010
#include "gmock/gmock.h"
1111
#include "gtest/gtest.h"
1212
#include "flang/Runtime/descriptor.h"
13+
#include "flang/Runtime/extensions.h"
1314
#include "flang/Runtime/main.h"
15+
#include <cstddef>
1416
#include <cstdlib>
1517

18+
#if _REENTRANT || _POSIX_C_SOURCE >= 199506L
19+
#include <limits.h> // LOGIN_NAME_MAX used in getlog test
20+
#endif
21+
1622
using namespace Fortran::runtime;
1723

1824
template <std::size_t n = 64>
@@ -59,6 +65,13 @@ class CommandFixture : public ::testing::Test {
5965
return res;
6066
}
6167

68+
void CheckCharEqStr(const char *value, const std::string &expected) const {
69+
ASSERT_NE(value, nullptr);
70+
EXPECT_EQ(std::strncmp(value, expected.c_str(), expected.size()), 0)
71+
<< "expected: " << expected << "\n"
72+
<< "value: " << value;
73+
}
74+
6275
void CheckDescriptorEqStr(
6376
const Descriptor *value, const std::string &expected) const {
6477
ASSERT_NE(value, nullptr);
@@ -397,6 +410,11 @@ class EnvironmentVariables : public CommandFixture {
397410
protected:
398411
EnvironmentVariables() : CommandFixture(0, nullptr) {
399412
SetEnv("NAME", "VALUE");
413+
#ifdef _WIN32
414+
SetEnv("USERNAME", "loginName");
415+
#else
416+
SetEnv("LOGNAME", "loginName");
417+
#endif
400418
SetEnv("EMPTY", "");
401419
}
402420

@@ -494,3 +512,68 @@ TEST_F(EnvironmentVariables, ErrMsgTooShort) {
494512
1);
495513
CheckDescriptorEqStr(errMsg.get(), "Mis");
496514
}
515+
516+
// username first char must not be null
517+
TEST_F(EnvironmentVariables, GetlogGetName) {
518+
const int charLen{3};
519+
char input[charLen]{"\0\0"};
520+
521+
FORTRAN_PROCEDURE_NAME(getlog)
522+
(reinterpret_cast<std::byte *>(input), charLen);
523+
524+
EXPECT_NE(input[0], '\0');
525+
}
526+
527+
#if _REENTRANT || _POSIX_C_SOURCE >= 199506L
528+
TEST_F(EnvironmentVariables, GetlogPadSpace) {
529+
// guarantee 1 char longer than max, last char should be pad space
530+
const int charLen{LOGIN_NAME_MAX + 2};
531+
char input[charLen];
532+
533+
FORTRAN_PROCEDURE_NAME(getlog)
534+
(reinterpret_cast<std::byte *>(input), charLen);
535+
536+
EXPECT_EQ(input[charLen - 1], ' ');
537+
}
538+
#endif
539+
540+
#ifdef _WIN32 // Test ability to get name from environment variable
541+
TEST_F(EnvironmentVariables, GetlogEnvGetName) {
542+
if (EnableFineGrainedTests()) {
543+
ASSERT_NE(std::getenv("USERNAME"), nullptr)
544+
<< "Environment variable USERNAME does not exist";
545+
546+
char input[]{"XXXXXXXXX"};
547+
FORTRAN_PROCEDURE_NAME(getlog)
548+
(reinterpret_cast<std::byte *>(input), sizeof(input));
549+
550+
CheckCharEqStr(input, "loginName");
551+
}
552+
}
553+
554+
TEST_F(EnvironmentVariables, GetlogEnvBufferShort) {
555+
if (EnableFineGrainedTests()) {
556+
ASSERT_NE(std::getenv("USERNAME"), nullptr)
557+
<< "Environment variable USERNAME does not exist";
558+
559+
char input[]{"XXXXXX"};
560+
FORTRAN_PROCEDURE_NAME(getlog)
561+
(reinterpret_cast<std::byte *>(input), sizeof(input));
562+
563+
CheckCharEqStr(input, "loginN");
564+
}
565+
}
566+
567+
TEST_F(EnvironmentVariables, GetlogEnvPadSpace) {
568+
if (EnableFineGrainedTests()) {
569+
ASSERT_NE(std::getenv("USERNAME"), nullptr)
570+
<< "Environment variable USERNAME does not exist";
571+
572+
char input[]{"XXXXXXXXXX"};
573+
FORTRAN_PROCEDURE_NAME(getlog)
574+
(reinterpret_cast<std::byte *>(input), sizeof(input));
575+
576+
CheckCharEqStr(input, "loginName ");
577+
}
578+
}
579+
#endif

0 commit comments

Comments
 (0)