[flang-commits] [flang] 37089ba - Reland "[flang] GET_COMMAND_ARGUMENT runtime implementation"
Diana Picus via flang-commits
flang-commits at lists.llvm.org
Tue Sep 28 05:34:17 PDT 2021
Author: Diana Picus
Date: 2021-09-28T12:34:07Z
New Revision: 37089bae29a92b97e16be4795553b68a6b04ff91
URL: https://github.com/llvm/llvm-project/commit/37089bae29a92b97e16be4795553b68a6b04ff91
DIFF: https://github.com/llvm/llvm-project/commit/37089bae29a92b97e16be4795553b68a6b04ff91.diff
LOG: Reland "[flang] GET_COMMAND_ARGUMENT runtime implementation"
Recommit https://reviews.llvm.org/D109813 and
https://reviews.llvm.org/D109814.
This implements the second and final entry point for GET_COMMAND_ARGUMENT,
handling the VALUE, STATUS and ERRMSG parameters.
It has a small fix in that we're now using memcpy instead of strncpy
(which was a bad idea to begin with, since we're not actually interested
in a string copy).
Added:
Modified:
flang/include/flang/Runtime/magic-numbers.h
flang/runtime/command.cpp
flang/runtime/stat.cpp
flang/runtime/stat.h
flang/test/Runtime/no-cpp-dep.c
flang/unittests/Runtime/CommandTest.cpp
Removed:
################################################################################
diff --git a/flang/include/flang/Runtime/magic-numbers.h b/flang/include/flang/Runtime/magic-numbers.h
index f84b7ecf08376..0f1957b4648b7 100644
--- a/flang/include/flang/Runtime/magic-numbers.h
+++ b/flang/include/flang/Runtime/magic-numbers.h
@@ -39,4 +39,12 @@ start at 100 so as to never conflict with those codes.
#define FORTRAN_RUNTIME_STAT_STOPPED_IMAGE 104
#define FORTRAN_RUNTIME_STAT_UNLOCKED 105
#define FORTRAN_RUNTIME_STAT_UNLOCKED_FAILED_IMAGE 106
+
+#if 0
+Status codes for GET_COMMAND_ARGUMENT. The status for 'value too short' needs
+to be -1, the others must be positive.
+#endif
+#define FORTRAN_RUNTIME_STAT_INVALID_ARG_NUMBER 107
+#define FORTRAN_RUNTIME_STAT_MISSING_ARG 108
+#define FORTRAN_RUNTIME_STAT_VALUE_TOO_SHORT -1
#endif
diff --git a/flang/runtime/command.cpp b/flang/runtime/command.cpp
index 1a55061749a1c..5fc2d28305ffb 100644
--- a/flang/runtime/command.cpp
+++ b/flang/runtime/command.cpp
@@ -8,6 +8,8 @@
#include "flang/Runtime/command.h"
#include "environment.h"
+#include "stat.h"
+#include "flang/Runtime/descriptor.h"
#include <limits>
namespace Fortran::runtime {
@@ -20,11 +22,8 @@ std::int32_t RTNAME(ArgumentCount)() {
return 0;
}
-std::int64_t RTNAME(ArgumentLength)(std::int32_t n) {
- if (n < 0 || n >= executionEnvironment.argc) {
- return 0;
- }
-
+// Returns the length of the \p n'th argument. Assumes \p n is valid.
+static std::int64_t ArgumentLength(std::int32_t n) {
std::size_t length{std::strlen(executionEnvironment.argv[n])};
if constexpr (sizeof(std::size_t) <= sizeof(std::int64_t)) {
return static_cast<std::int64_t>(length);
@@ -34,4 +33,50 @@ std::int64_t RTNAME(ArgumentLength)(std::int32_t n) {
: static_cast<std::int64_t>(length);
}
}
+
+std::int64_t RTNAME(ArgumentLength)(std::int32_t n) {
+ if (n < 0 || n >= executionEnvironment.argc) {
+ return 0;
+ }
+
+ return ArgumentLength(n);
+}
+
+static bool IsValidCharDescriptor(const Descriptor *value) {
+ return value && value->IsAllocated() &&
+ value->type() == TypeCode(TypeCategory::Character, 1) &&
+ value->rank() == 0;
+}
+
+static void FillWithSpaces(const Descriptor *value) {
+ std::memset(value->OffsetElement(), ' ', value->ElementBytes());
+}
+
+std::int32_t RTNAME(ArgumentValue)(
+ std::int32_t n, const Descriptor *value, const Descriptor *errmsg) {
+ if (IsValidCharDescriptor(value)) {
+ FillWithSpaces(value);
+ }
+
+ if (n < 0 || n >= executionEnvironment.argc) {
+ return ToErrmsg(errmsg, StatInvalidArgumentNumber);
+ }
+
+ if (IsValidCharDescriptor(value)) {
+ std::int64_t argLen{ArgumentLength(n)};
+ if (argLen <= 0) {
+ return ToErrmsg(errmsg, StatMissingArgument);
+ }
+
+ std::int64_t toCopy{
+ std::min(argLen, static_cast<std::int64_t>(value->ElementBytes()))};
+ std::memcpy(value->OffsetElement(), executionEnvironment.argv[n], toCopy);
+
+ if (argLen > toCopy) {
+ return ToErrmsg(errmsg, StatValueTooShort);
+ }
+ }
+
+ return StatOk;
+}
} // namespace Fortran::runtime
diff --git a/flang/runtime/stat.cpp b/flang/runtime/stat.cpp
index c0620f4c23e33..d28187c1a4264 100644
--- a/flang/runtime/stat.cpp
+++ b/flang/runtime/stat.cpp
@@ -50,6 +50,13 @@ const char *StatErrorString(int stat) {
case StatUnlockedFailedImage:
return "Failed image unlocked";
+ case StatInvalidArgumentNumber:
+ return "Invalid argument number";
+ case StatMissingArgument:
+ return "Missing argument";
+ case StatValueTooShort:
+ return "Value too short";
+
default:
return nullptr;
}
diff --git a/flang/runtime/stat.h b/flang/runtime/stat.h
index 5c1828e918bfe..5042f4bc4263c 100644
--- a/flang/runtime/stat.h
+++ b/flang/runtime/stat.h
@@ -44,6 +44,9 @@ enum Stat {
StatUnlockedFailedImage = FORTRAN_RUNTIME_STAT_UNLOCKED_FAILED_IMAGE,
// Additional "processor-defined" STAT= values
+ StatInvalidArgumentNumber = FORTRAN_RUNTIME_STAT_INVALID_ARG_NUMBER,
+ StatMissingArgument = FORTRAN_RUNTIME_STAT_MISSING_ARG,
+ StatValueTooShort = FORTRAN_RUNTIME_STAT_VALUE_TOO_SHORT,
};
const char *StatErrorString(int);
diff --git a/flang/test/Runtime/no-cpp-dep.c b/flang/test/Runtime/no-cpp-dep.c
index 9970e098c34ff..0f99ccc95e60c 100644
--- a/flang/test/Runtime/no-cpp-dep.c
+++ b/flang/test/Runtime/no-cpp-dep.c
@@ -22,12 +22,15 @@ double RTNAME(CpuTime)();
void RTNAME(ProgramStart)(int, const char *[], const char *[]);
int32_t RTNAME(ArgumentCount)();
+int32_t RTNAME(ArgumentValue)(
+ int32_t, const struct Descriptor *, const struct Descriptor *);
int64_t RTNAME(ArgumentLength)(int32_t);
int main() {
double x = RTNAME(CpuTime)();
RTNAME(ProgramStart)(0, 0, 0);
int32_t c = RTNAME(ArgumentCount)();
+ int32_t v = RTNAME(ArgumentValue)(0, 0, 0);
int32_t l = RTNAME(ArgumentLength)(0);
return x + c + l;
}
diff --git a/flang/unittests/Runtime/CommandTest.cpp b/flang/unittests/Runtime/CommandTest.cpp
index 3841bbe0ed3ba..a13addd75a214 100644
--- a/flang/unittests/Runtime/CommandTest.cpp
+++ b/flang/unittests/Runtime/CommandTest.cpp
@@ -7,17 +7,69 @@
//===----------------------------------------------------------------------===//
#include "flang/Runtime/command.h"
+#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "flang/Runtime/descriptor.h"
#include "flang/Runtime/main.h"
using namespace Fortran::runtime;
+template <std::size_t n = 64>
+static OwningPtr<Descriptor> CreateEmptyCharDescriptor() {
+ OwningPtr<Descriptor> descriptor{Descriptor::Create(
+ sizeof(char), n, nullptr, 0, nullptr, CFI_attribute_allocatable)};
+ if (descriptor->Allocate() != 0) {
+ return nullptr;
+ }
+ return descriptor;
+}
+
class CommandFixture : public ::testing::Test {
protected:
CommandFixture(int argc, const char *argv[]) {
RTNAME(ProgramStart)(argc, argv, {});
}
+
+ std::string GetPaddedStr(const char *text, std::size_t len) const {
+ std::string res{text};
+ assert(res.length() <= len && "No room to pad");
+ res.append(len - res.length(), ' ');
+ return res;
+ }
+
+ void CheckDescriptorEqStr(
+ const Descriptor *value, const std::string &expected) const {
+ EXPECT_EQ(std::strncmp(value->OffsetElement(), expected.c_str(),
+ value->ElementBytes()),
+ 0);
+ }
+
+ void CheckArgumentValue(int n, const char *argv) const {
+ OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()};
+ ASSERT_NE(value, nullptr);
+
+ std::string expected{GetPaddedStr(argv, value->ElementBytes())};
+
+ EXPECT_EQ(RTNAME(ArgumentValue)(n, value.get(), nullptr), 0);
+ CheckDescriptorEqStr(value.get(), expected);
+ }
+
+ void CheckMissingArgumentValue(int n, const char *errStr = nullptr) const {
+ OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()};
+ ASSERT_NE(value, nullptr);
+
+ OwningPtr<Descriptor> err{errStr ? CreateEmptyCharDescriptor() : nullptr};
+
+ EXPECT_GT(RTNAME(ArgumentValue)(n, value.get(), err.get()), 0);
+
+ std::string spaces(value->ElementBytes(), ' ');
+ CheckDescriptorEqStr(value.get(), spaces);
+
+ if (errStr) {
+ std::string paddedErrStr(GetPaddedStr(errStr, err->ElementBytes()));
+ CheckDescriptorEqStr(err.get(), paddedErrStr);
+ }
+ }
};
static const char *commandOnlyArgv[]{"aProgram"};
@@ -34,6 +86,10 @@ TEST_F(ZeroArguments, ArgumentLength) {
EXPECT_EQ(0, RTNAME(ArgumentLength)(1));
}
+TEST_F(ZeroArguments, ArgumentValue) {
+ CheckArgumentValue(0, commandOnlyArgv[0]);
+}
+
static const char *oneArgArgv[]{"aProgram", "anArgumentOfLength20"};
class OneArgument : public CommandFixture {
protected:
@@ -49,6 +105,11 @@ TEST_F(OneArgument, ArgumentLength) {
EXPECT_EQ(0, RTNAME(ArgumentLength)(2));
}
+TEST_F(OneArgument, ArgumentValue) {
+ CheckArgumentValue(0, oneArgArgv[0]);
+ CheckArgumentValue(1, oneArgArgv[1]);
+}
+
static const char *severalArgsArgv[]{
"aProgram", "16-char-long-arg", "", "-22-character-long-arg", "o"};
class SeveralArguments : public CommandFixture {
@@ -71,3 +132,46 @@ TEST_F(SeveralArguments, ArgumentLength) {
EXPECT_EQ(1, RTNAME(ArgumentLength)(4));
EXPECT_EQ(0, RTNAME(ArgumentLength)(5));
}
+
+TEST_F(SeveralArguments, ArgumentValue) {
+ CheckArgumentValue(0, severalArgsArgv[0]);
+ CheckArgumentValue(1, severalArgsArgv[1]);
+ CheckArgumentValue(3, severalArgsArgv[3]);
+ CheckArgumentValue(4, severalArgsArgv[4]);
+}
+
+TEST_F(SeveralArguments, NoArgumentValue) {
+ // Make sure we don't crash if the 'value' and 'error' parameters aren't
+ // passed.
+ EXPECT_EQ(RTNAME(ArgumentValue)(2, nullptr, nullptr), 0);
+ EXPECT_GT(RTNAME(ArgumentValue)(-1, nullptr, nullptr), 0);
+}
+
+TEST_F(SeveralArguments, MissingArguments) {
+ CheckMissingArgumentValue(-1, "Invalid argument number");
+ CheckMissingArgumentValue(2, "Missing argument");
+ CheckMissingArgumentValue(5, "Invalid argument number");
+ CheckMissingArgumentValue(5);
+}
+
+TEST_F(SeveralArguments, ValueTooShort) {
+ OwningPtr<Descriptor> tooShort{CreateEmptyCharDescriptor<15>()};
+ ASSERT_NE(tooShort, nullptr);
+ EXPECT_EQ(RTNAME(ArgumentValue)(1, tooShort.get(), nullptr), -1);
+ CheckDescriptorEqStr(tooShort.get(), severalArgsArgv[1]);
+
+ OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()};
+ ASSERT_NE(errMsg, nullptr);
+
+ EXPECT_EQ(RTNAME(ArgumentValue)(1, tooShort.get(), errMsg.get()), -1);
+
+ std::string expectedErrMsg{
+ GetPaddedStr("Value too short", errMsg->ElementBytes())};
+ CheckDescriptorEqStr(errMsg.get(), expectedErrMsg);
+}
+
+TEST_F(SeveralArguments, ErrMsgTooShort) {
+ OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor<3>()};
+ EXPECT_GT(RTNAME(ArgumentValue)(-1, nullptr, errMsg.get()), 0);
+ CheckDescriptorEqStr(errMsg.get(), "Inv");
+}
More information about the flang-commits
mailing list