Skip to content

Commit 2276f77

Browse files
rovkajeanPerier
authored andcommitted
[flang] Add runtime support for GET_COMMAND
Implement the GET_COMMAND intrinsic. Add 2 new parameters (sourceFile and line) so we can create a terminator for RUNTIME_CHECKs. Differential Revision: https://reviews.llvm.org/D118777
1 parent 636a162 commit 2276f77

File tree

3 files changed

+298
-18
lines changed

3 files changed

+298
-18
lines changed

flang/include/flang/Runtime/command.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ std::int32_t RTNAME(ArgumentCount)();
2828
// optional.
2929
// Return a STATUS as described in the standard.
3030
std::int32_t RTNAME(GetCommand)(const Descriptor *command = nullptr,
31-
const Descriptor *length = nullptr, const Descriptor *errmsg = nullptr);
31+
const Descriptor *length = nullptr, const Descriptor *errmsg = nullptr,
32+
const char *sourceFile = nullptr, int line = 0);
3233

3334
// 16.9.83 GET_COMMAND_ARGUMENT
3435
// We're breaking up the interface into several different functions, since most

flang/runtime/command.cpp

Lines changed: 128 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "environment.h"
1111
#include "stat.h"
1212
#include "terminator.h"
13+
#include "tools.h"
1314
#include "flang/Runtime/descriptor.h"
1415
#include <cstdlib>
1516
#include <limits>
@@ -51,16 +52,32 @@ static bool IsValidCharDescriptor(const Descriptor *value) {
5152
value->rank() == 0;
5253
}
5354

54-
static void FillWithSpaces(const Descriptor *value) {
55-
std::memset(value->OffsetElement(), ' ', value->ElementBytes());
55+
static bool IsValidIntDescriptor(const Descriptor *length) {
56+
auto typeCode{length->type().GetCategoryAndKind()};
57+
// Check that our descriptor is allocated and is a scalar integer with
58+
// kind != 1 (i.e. with a large enough decimal exponent range).
59+
return length->IsAllocated() && length->rank() == 0 &&
60+
length->type().IsInteger() && typeCode && typeCode->second != 1;
61+
}
62+
63+
static void FillWithSpaces(const Descriptor &value, std::size_t offset = 0) {
64+
if (offset < value.ElementBytes()) {
65+
std::memset(
66+
value.OffsetElement(offset), ' ', value.ElementBytes() - offset);
67+
}
5668
}
5769

5870
static std::int32_t CopyToDescriptor(const Descriptor &value,
59-
const char *rawValue, std::int64_t rawValueLength,
60-
const Descriptor *errmsg) {
61-
std::int64_t toCopy{std::min(
62-
rawValueLength, static_cast<std::int64_t>(value.ElementBytes()))};
63-
std::memcpy(value.OffsetElement(), rawValue, toCopy);
71+
const char *rawValue, std::int64_t rawValueLength, const Descriptor *errmsg,
72+
std::size_t offset = 0) {
73+
74+
std::int64_t toCopy{std::min(rawValueLength,
75+
static_cast<std::int64_t>(value.ElementBytes() - offset))};
76+
if (toCopy < 0) {
77+
return ToErrmsg(errmsg, StatValueTooShort);
78+
}
79+
80+
std::memcpy(value.OffsetElement(offset), rawValue, toCopy);
6481

6582
if (rawValueLength > toCopy) {
6683
return ToErrmsg(errmsg, StatValueTooShort);
@@ -69,10 +86,31 @@ static std::int32_t CopyToDescriptor(const Descriptor &value,
6986
return StatOk;
7087
}
7188

89+
static std::int32_t CheckAndCopyToDescriptor(const Descriptor *value,
90+
const char *rawValue, const Descriptor *errmsg, std::size_t &offset) {
91+
bool haveValue{IsValidCharDescriptor(value)};
92+
93+
std::int64_t len{StringLength(rawValue)};
94+
if (len <= 0) {
95+
if (haveValue) {
96+
FillWithSpaces(*value);
97+
}
98+
return ToErrmsg(errmsg, StatMissingArgument);
99+
}
100+
101+
std::int32_t stat{StatOk};
102+
if (haveValue) {
103+
stat = CopyToDescriptor(*value, rawValue, len, errmsg, offset);
104+
}
105+
106+
offset += len;
107+
return stat;
108+
}
109+
72110
std::int32_t RTNAME(ArgumentValue)(
73111
std::int32_t n, const Descriptor *value, const Descriptor *errmsg) {
74112
if (IsValidCharDescriptor(value)) {
75-
FillWithSpaces(value);
113+
FillWithSpaces(*value);
76114
}
77115

78116
if (n < 0 || n >= executionEnvironment.argc) {
@@ -92,6 +130,87 @@ std::int32_t RTNAME(ArgumentValue)(
92130
return StatOk;
93131
}
94132

133+
template <int KIND> struct FitsInIntegerKind {
134+
bool operator()(std::int64_t value) {
135+
return value <= std::numeric_limits<Fortran::runtime::CppTypeFor<
136+
Fortran::common::TypeCategory::Integer, KIND>>::max();
137+
}
138+
};
139+
140+
std::int32_t RTNAME(GetCommand)(const Descriptor *value,
141+
const Descriptor *length, const Descriptor *errmsg, const char *sourceFile,
142+
int line) {
143+
Terminator terminator{sourceFile, line};
144+
145+
auto storeLength = [&](std::int64_t value) {
146+
auto typeCode{length->type().GetCategoryAndKind()};
147+
int kind{typeCode->second};
148+
Fortran::runtime::ApplyIntegerKind<Fortran::runtime::StoreIntegerAt, void>(
149+
kind, terminator, *length, /* atIndex = */ 0, value);
150+
};
151+
152+
if (value) {
153+
RUNTIME_CHECK(terminator, IsValidCharDescriptor(value));
154+
}
155+
156+
// Store 0 in case we error out later on.
157+
if (length) {
158+
RUNTIME_CHECK(terminator, IsValidIntDescriptor(length));
159+
storeLength(0);
160+
}
161+
162+
auto shouldContinue = [&](std::int32_t stat) -> bool {
163+
// We continue as long as everything is ok OR the value descriptor is
164+
// too short, but we still need to compute the length.
165+
return stat == StatOk || (length && stat == StatValueTooShort);
166+
};
167+
168+
std::size_t offset{0};
169+
170+
if (executionEnvironment.argc == 0) {
171+
return CheckAndCopyToDescriptor(value, "", errmsg, offset);
172+
}
173+
174+
// value = argv[0]
175+
std::int32_t stat{CheckAndCopyToDescriptor(
176+
value, executionEnvironment.argv[0], errmsg, offset)};
177+
if (!shouldContinue(stat)) {
178+
return stat;
179+
}
180+
181+
// value += " " + argv[1:n]
182+
for (std::int32_t i{1}; i < executionEnvironment.argc; ++i) {
183+
stat = CheckAndCopyToDescriptor(value, " ", errmsg, offset);
184+
if (!shouldContinue(stat)) {
185+
return stat;
186+
}
187+
188+
stat = CheckAndCopyToDescriptor(
189+
value, executionEnvironment.argv[i], errmsg, offset);
190+
if (!shouldContinue(stat)) {
191+
return stat;
192+
}
193+
}
194+
195+
auto fitsInLength = [&](std::int64_t value) -> bool {
196+
auto typeCode{length->type().GetCategoryAndKind()};
197+
int kind{typeCode->second};
198+
return Fortran::runtime::ApplyIntegerKind<FitsInIntegerKind, bool>(
199+
kind, terminator, value);
200+
};
201+
202+
if (length && fitsInLength(offset)) {
203+
storeLength(offset);
204+
}
205+
206+
// value += spaces for padding
207+
if (value) {
208+
FillWithSpaces(*value, offset);
209+
}
210+
211+
return stat;
212+
}
213+
95214
static std::size_t LengthWithoutTrailingSpaces(const Descriptor &d) {
96215
std::size_t s{d.ElementBytes() - 1};
97216
while (*d.OffsetElement(s) == ' ') {
@@ -118,7 +237,7 @@ std::int32_t RTNAME(EnvVariableValue)(const Descriptor &name,
118237
const Descriptor *value, bool trim_name, const Descriptor *errmsg,
119238
const char *sourceFile, int line) {
120239
if (IsValidCharDescriptor(value)) {
121-
FillWithSpaces(value);
240+
FillWithSpaces(*value);
122241
}
123242

124243
const char *rawValue{GetEnvVariableValue(name, trim_name, sourceFile, line)};

0 commit comments

Comments
 (0)