Skip to content

Commit df63023

Browse files
committed
[flang] GET_COMMAND_ARGUMENT(VALUE) runtime implementation
Partial implementation for the second entry point for GET_COMMAND_ARGUMENT. It handles the VALUE and STATUS arguments, and doesn't touch ERRMSG. Differential Revision: https://reviews.llvm.org/D109813
1 parent af63d17 commit df63023

File tree

3 files changed

+129
-5
lines changed

3 files changed

+129
-5
lines changed

flang/runtime/command.cpp

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "flang/Runtime/command.h"
1010
#include "environment.h"
11+
#include "flang/Runtime/descriptor.h"
1112
#include <limits>
1213

1314
namespace Fortran::runtime {
@@ -20,11 +21,8 @@ std::int32_t RTNAME(ArgumentCount)() {
2021
return 0;
2122
}
2223

23-
std::int64_t RTNAME(ArgumentLength)(std::int32_t n) {
24-
if (n < 0 || n >= executionEnvironment.argc) {
25-
return 0;
26-
}
27-
24+
// Returns the length of the \p n'th argument. Assumes \p n is valid.
25+
static std::int64_t ArgumentLength(std::int32_t n) {
2826
std::size_t length{std::strlen(executionEnvironment.argv[n])};
2927
if constexpr (sizeof(std::size_t) <= sizeof(std::int64_t)) {
3028
return static_cast<std::int64_t>(length);
@@ -34,4 +32,49 @@ std::int64_t RTNAME(ArgumentLength)(std::int32_t n) {
3432
: static_cast<std::int64_t>(length);
3533
}
3634
}
35+
36+
std::int64_t RTNAME(ArgumentLength)(std::int32_t n) {
37+
if (n < 0 || n >= executionEnvironment.argc) {
38+
return 0;
39+
}
40+
41+
return ArgumentLength(n);
42+
}
43+
44+
static bool IsValidCharDescriptor(const Descriptor *value) {
45+
return value && value->IsAllocated() &&
46+
value->type() == TypeCode(TypeCategory::Character, 1) &&
47+
value->rank() == 0;
48+
}
49+
50+
static void FillWithSpaces(const Descriptor *value) {
51+
std::memset(value->OffsetElement(), ' ', value->ElementBytes());
52+
}
53+
54+
std::int32_t RTNAME(ArgumentValue)(
55+
std::int32_t n, const Descriptor *value, const Descriptor *errmsg) {
56+
if (IsValidCharDescriptor(value)) {
57+
FillWithSpaces(value);
58+
}
59+
60+
if (n < 0 || n >= executionEnvironment.argc) {
61+
return 1;
62+
}
63+
64+
if (IsValidCharDescriptor(value)) {
65+
std::int64_t argLen{ArgumentLength(n)};
66+
if (argLen <= 0) {
67+
return 2;
68+
}
69+
70+
std::int64_t toCopy{
71+
std::min(argLen, static_cast<std::int64_t>(value->ElementBytes()))};
72+
std::strncpy(value->OffsetElement(), executionEnvironment.argv[n], toCopy);
73+
74+
if (argLen > toCopy) {
75+
return -1;
76+
}
77+
}
78+
return 0;
79+
}
3780
} // namespace Fortran::runtime

flang/test/Runtime/no-cpp-dep.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,15 @@ double RTNAME(CpuTime)();
2222

2323
void RTNAME(ProgramStart)(int, const char *[], const char *[]);
2424
int32_t RTNAME(ArgumentCount)();
25+
int32_t RTNAME(ArgumentValue)(
26+
int32_t, const struct Descriptor *, const struct Descriptor *);
2527
int64_t RTNAME(ArgumentLength)(int32_t);
2628

2729
int main() {
2830
double x = RTNAME(CpuTime)();
2931
RTNAME(ProgramStart)(0, 0, 0);
3032
int32_t c = RTNAME(ArgumentCount)();
33+
int32_t v = RTNAME(ArgumentValue)(0, 0, 0);
3134
int32_t l = RTNAME(ArgumentLength)(0);
3235
return x + c + l;
3336
}

flang/unittests/Runtime/CommandTest.cpp

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,55 @@
1313

1414
using namespace Fortran::runtime;
1515

16+
template <std::size_t n = 64>
17+
static OwningPtr<Descriptor> CreateEmptyCharDescriptor() {
18+
OwningPtr<Descriptor> descriptor{Descriptor::Create(
19+
sizeof(char), n, nullptr, 0, nullptr, CFI_attribute_allocatable)};
20+
if (descriptor->Allocate() != 0) {
21+
return nullptr;
22+
}
23+
return descriptor;
24+
}
25+
1626
class CommandFixture : public ::testing::Test {
1727
protected:
1828
CommandFixture(int argc, const char *argv[]) {
1929
RTNAME(ProgramStart)(argc, argv, {});
2030
}
31+
32+
std::string GetPaddedStr(const char *text, std::size_t len) const {
33+
std::string res{text};
34+
assert(res.length() <= len && "No room to pad");
35+
res.append(len - res.length(), ' ');
36+
return res;
37+
}
38+
39+
void CheckDescriptorEqStr(
40+
const Descriptor *value, const std::string &expected) const {
41+
EXPECT_EQ(std::strncmp(value->OffsetElement(), expected.c_str(),
42+
value->ElementBytes()),
43+
0);
44+
}
45+
46+
void CheckArgumentValue(int n, const char *argv) const {
47+
OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()};
48+
ASSERT_NE(value, nullptr);
49+
50+
std::string expected{GetPaddedStr(argv, value->ElementBytes())};
51+
52+
EXPECT_EQ(RTNAME(ArgumentValue)(n, value.get(), nullptr), 0);
53+
CheckDescriptorEqStr(value.get(), expected);
54+
}
55+
56+
void CheckMissingArgumentValue(int n) const {
57+
OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()};
58+
ASSERT_NE(value, nullptr);
59+
60+
EXPECT_GT(RTNAME(ArgumentValue)(n, value.get(), nullptr), 0);
61+
62+
std::string spaces(value->ElementBytes(), ' ');
63+
CheckDescriptorEqStr(value.get(), spaces);
64+
}
2165
};
2266

2367
static const char *commandOnlyArgv[]{"aProgram"};
@@ -34,6 +78,10 @@ TEST_F(ZeroArguments, ArgumentLength) {
3478
EXPECT_EQ(0, RTNAME(ArgumentLength)(1));
3579
}
3680

81+
TEST_F(ZeroArguments, ArgumentValue) {
82+
CheckArgumentValue(0, commandOnlyArgv[0]);
83+
}
84+
3785
static const char *oneArgArgv[]{"aProgram", "anArgumentOfLength20"};
3886
class OneArgument : public CommandFixture {
3987
protected:
@@ -49,6 +97,11 @@ TEST_F(OneArgument, ArgumentLength) {
4997
EXPECT_EQ(0, RTNAME(ArgumentLength)(2));
5098
}
5199

100+
TEST_F(OneArgument, ArgumentValue) {
101+
CheckArgumentValue(0, oneArgArgv[0]);
102+
CheckArgumentValue(1, oneArgArgv[1]);
103+
}
104+
52105
static const char *severalArgsArgv[]{
53106
"aProgram", "16-char-long-arg", "", "-22-character-long-arg", "o"};
54107
class SeveralArguments : public CommandFixture {
@@ -71,3 +124,28 @@ TEST_F(SeveralArguments, ArgumentLength) {
71124
EXPECT_EQ(1, RTNAME(ArgumentLength)(4));
72125
EXPECT_EQ(0, RTNAME(ArgumentLength)(5));
73126
}
127+
128+
TEST_F(SeveralArguments, ArgumentValue) {
129+
CheckArgumentValue(0, severalArgsArgv[0]);
130+
CheckArgumentValue(1, severalArgsArgv[1]);
131+
CheckMissingArgumentValue(2);
132+
CheckArgumentValue(3, severalArgsArgv[3]);
133+
CheckArgumentValue(4, severalArgsArgv[4]);
134+
}
135+
136+
TEST_F(SeveralArguments, NoArgumentValue) {
137+
// Make sure we don't crash if the 'value' and 'error' parameters aren't
138+
// passed.
139+
EXPECT_EQ(RTNAME(ArgumentValue)(2, nullptr, nullptr), 0);
140+
EXPECT_GT(RTNAME(ArgumentValue)(-1, nullptr, nullptr), 0);
141+
}
142+
143+
TEST_F(SeveralArguments, ArgumentValueErrors) {
144+
CheckMissingArgumentValue(-1);
145+
CheckMissingArgumentValue(5);
146+
147+
OwningPtr<Descriptor> tooShort{CreateEmptyCharDescriptor<15>()};
148+
ASSERT_NE(tooShort, nullptr);
149+
EXPECT_EQ(RTNAME(ArgumentValue)(1, tooShort.get(), nullptr), -1);
150+
CheckDescriptorEqStr(tooShort.get(), severalArgsArgv[1]);
151+
}

0 commit comments

Comments
 (0)