Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[flang] FDATE extension implementation: get date and time in ctime format #71222

Merged
merged 22 commits into from
Jan 11, 2024

Conversation

yi-wu-arm
Copy link
Contributor

@yi-wu-arm yi-wu-arm commented Nov 3, 2023

reference to gfortran fdate https://gcc.gnu.org/onlinedocs/gfortran/FDATE.html
usage:

CHARACTER(32) :: time
CALL fdate(time)
WRITE(*,*) time

fdate is used in the ECP proxy application https://proxyapps.exascaleproject.org/app/minismac2d/
https://github.com/Mantevo/miniSMAC/blob/f90446714226eeef650b78bce06ca4967792e74d/ref/smac2d.f#L1570

Copy link

github-actions bot commented Nov 3, 2023

✅ With the latest revision this PR passed the C/C++ code formatter.

reference to gfortran fdate https://gcc.gnu.org/onlinedocs/gfortran/FDATE.html
usage:
CHARACTER(32) :: time
CALL fdate(time)
WRITE(*,*) time
@yi-wu-arm yi-wu-arm marked this pull request as ready for review November 7, 2023 12:33
@llvmbot llvmbot added flang:runtime flang Flang issues not falling into any other category labels Nov 7, 2023
@llvmbot
Copy link
Collaborator

llvmbot commented Nov 7, 2023

@llvm/pr-subscribers-flang-runtime

Author: Yi Wu (PAX-12-WU)

Changes

reference to gfortran fdate https://gcc.gnu.org/onlinedocs/gfortran/FDATE.html
usage:

CHARACTER(32) :: time
CALL fdate(time)
WRITE(*,*) time

Full diff: https://github.com/llvm/llvm-project/pull/71222.diff

6 Files Affected:

  • (modified) flang/docs/Intrinsics.md (+1-1)
  • (modified) flang/include/flang/Runtime/command.h (+5)
  • (modified) flang/include/flang/Runtime/extensions.h (+2)
  • (modified) flang/runtime/command.cpp (+28)
  • (modified) flang/runtime/extensions.cpp (+5)
  • (modified) flang/unittests/Runtime/CommandTest.cpp (+14)
diff --git a/flang/docs/Intrinsics.md b/flang/docs/Intrinsics.md
index ab0a940e53e5538..982be8208164296 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, FDATE, 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 |
 
diff --git a/flang/include/flang/Runtime/command.h b/flang/include/flang/Runtime/command.h
index ec6289390545479..07f6d8e169ead6c 100644
--- a/flang/include/flang/Runtime/command.h
+++ b/flang/include/flang/Runtime/command.h
@@ -23,6 +23,11 @@ extern "C" {
 // integer kind.
 std::int32_t RTNAME(ArgumentCount)();
 
+// Try to get the the current date (same format as CTIME: convert to a string)
+// Return a STATUS as described in the standard.
+std::int32_t RTNAME(FDate)(
+    const Descriptor *argument = nullptr, const Descriptor *errmsg = nullptr);
+
 // 16.9.82 GET_COMMAND
 // Try to get the value of the whole command. All of the parameters are
 // optional.
diff --git a/flang/include/flang/Runtime/extensions.h b/flang/include/flang/Runtime/extensions.h
index ad592814e5acb79..92b9907860121aa 100644
--- a/flang/include/flang/Runtime/extensions.h
+++ b/flang/include/flang/Runtime/extensions.h
@@ -24,6 +24,8 @@ void FORTRAN_PROCEDURE_NAME(flush)(const int &unit);
 // GNU Fortran 77 compatibility function IARGC.
 std::int32_t FORTRAN_PROCEDURE_NAME(iargc)();
 
+void FORTRAN_PROCEDURE_NAME(fdate)(std::int8_t *string, std::int64_t length);
+
 // GNU Fortran 77 compatibility subroutine GETARG(N, ARG).
 void FORTRAN_PROCEDURE_NAME(getarg)(
     std::int32_t &n, std::int8_t *arg, std::int64_t length);
diff --git a/flang/runtime/command.cpp b/flang/runtime/command.cpp
index b81a0791c5e571b..da0803c39f49b6d 100644
--- a/flang/runtime/command.cpp
+++ b/flang/runtime/command.cpp
@@ -14,6 +14,7 @@
 #include "flang/Runtime/descriptor.h"
 #include <cstdlib>
 #include <limits>
+#include <time.h>
 
 namespace Fortran::runtime {
 std::int32_t RTNAME(ArgumentCount)() {
@@ -125,6 +126,33 @@ static bool FitsInDescriptor(
       kind, terminator, value);
 }
 
+void removeNewLine(char *str) {
+  char *newlinePos = strchr(str, '\n');
+  if (newlinePos != NULL) {
+    *newlinePos = '\0'; // Replace with null terminator
+  }
+}
+
+std::int32_t RTNAME(FDate)(const Descriptor *value, const Descriptor *errmsg) {
+  FillWithSpaces(*value);
+
+  time_t current_time;
+  time(&current_time);
+
+  char *time_string = ctime(&current_time);
+  removeNewLine(time_string);
+  std::int64_t stringLen{StringLength(time_string)};
+  if (stringLen <= 0) {
+    return ToErrmsg(errmsg, StatMissingArgument);
+  }
+
+  if (value) {
+    return CopyToDescriptor(*value, time_string, stringLen, errmsg);
+  }
+
+  return StatOk;
+}
+
 std::int32_t RTNAME(GetCommandArgument)(std::int32_t n, const Descriptor *value,
     const Descriptor *length, const Descriptor *errmsg, const char *sourceFile,
     int line) {
diff --git a/flang/runtime/extensions.cpp b/flang/runtime/extensions.cpp
index b8e9b6eae132059..0142cac1d929d4d 100644
--- a/flang/runtime/extensions.cpp
+++ b/flang/runtime/extensions.cpp
@@ -30,6 +30,11 @@ void FORTRAN_PROCEDURE_NAME(flush)(const int &unit) {
 // RESULT = IARGC()
 std::int32_t FORTRAN_PROCEDURE_NAME(iargc)() { return RTNAME(ArgumentCount)(); }
 
+void FORTRAN_PROCEDURE_NAME(fdate)(std::int8_t *arg, std::int64_t length) {
+  Descriptor value{*Descriptor::Create(1, length, arg, 0)};
+  (void)RTNAME(FDate)(&value, nullptr);
+}
+
 // CALL GETARG(N, ARG)
 void FORTRAN_PROCEDURE_NAME(getarg)(
     std::int32_t &n, std::int8_t *arg, std::int64_t length) {
diff --git a/flang/unittests/Runtime/CommandTest.cpp b/flang/unittests/Runtime/CommandTest.cpp
index c3571c9684e4b07..091870e4baf1730 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, FDate) {
+  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, FDate) {
+  CheckMissingArgumentValue(-1);
+  CheckArgumentValue(oneArgArgv[0], 0);
+  CheckArgumentValue(oneArgArgv[1], 1);
+  CheckMissingArgumentValue(2);
+}
+
 TEST_F(OneArgument, GetCommand) { CheckCommandValue(oneArgArgv, 2); }
 
 static const char *severalArgsArgv[]{
@@ -284,6 +297,7 @@ TEST_F(SeveralArguments, ArgValueTooShort) {
   ASSERT_NE(tooShort, nullptr);
   EXPECT_EQ(RTNAME(GetCommandArgument)(1, tooShort.get()), -1);
   CheckDescriptorEqStr(tooShort.get(), severalArgsArgv[1]);
+  EXPECT_EQ(RTNAME(FDate)(tooShort.get()), -1);
 
   OwningPtr<Descriptor> length{EmptyIntDescriptor()};
   ASSERT_NE(length, nullptr);

Copy link
Member

@DavidTruby DavidTruby left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the patch; everything looks good (modulo my small comments) other than the thread safety issue. I'd prefer if we could make this thread safe but I'm not sure exactly how to approach that

flang/runtime/command.cpp Outdated Show resolved Hide resolved
flang/runtime/command.cpp Outdated Show resolved Hide resolved
flang/runtime/command.cpp Outdated Show resolved Hide resolved
ctime_s is defined in MS, ctime_r is defined in linux/macos
Copy link
Member

@DavidTruby DavidTruby left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this is looking good, I just have a couple more small comments

flang/runtime/command.cpp Outdated Show resolved Hide resolved
flang/runtime/command.cpp Outdated Show resolved Hide resolved
flang/runtime/command.cpp Outdated Show resolved Hide resolved
flang/runtime/command.cpp Outdated Show resolved Hide resolved
@yi-wu-arm yi-wu-arm changed the title FDATE extension implementation: get date and time in ctime format [flang] FDATE extension implementation: get date and time in ctime format Nov 13, 2023
Copy link
Member

@DavidTruby DavidTruby left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, maybe wait for @jeanPerier to check as well though. Thanks!

@yi-wu-arm
Copy link
Contributor Author

yi-wu-arm commented Nov 15, 2023

update based on:
#70917 (comment) Emit a Fortran runtime failure (instead of assert)
#70917 (comment) Space filling

fdate now produce the same result on flang, compare to gfortran, where
If the length is too short to fit completely, blank return.

  character(20) :: string
  call fdate(string)
  write(*, *) string, "X"
$ ../build-release/bin/flang-new test.f90 
$ ./a.out 
                      X

If length if larger than it requires(24), fill the rest of buffer space.

  character(30) :: string
  call fdate(string)
  write(*, *) string, "X"
$ ../build-release/bin/flang-new test.f90 
$ ./a.out 
 Wed Nov 15 16:59:13 2023      X

The length value is hardcoded, because:

  // Day Mon dd hh:mm:ss yyyy\n\0 is 26 characters, e.g.
  // Tue May 26 21:51:03 2015\n\0

If the length is too short to fit completely, blank return.
If length if larger than it requires(24), fill the rest of buffer space.
hange the return type of `ctime_alloc` from char * to void, because we
don't need the return value.
Take copyBufferAndPad out of anonymous namespace and declear
in header file.
Copy link
Contributor

@jeanPerier jeanPerier left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, please add the POSIX macros to use ctime_r, and looks great otherwise.

flang/runtime/extensions.cpp Outdated Show resolved Hide resolved
@yi-wu-arm
Copy link
Contributor Author

Hi @klausler , any thought of this patch? Thank in advance!

flang/include/flang/Runtime/extensions.h Outdated Show resolved Hide resolved
flang/runtime/extensions.cpp Outdated Show resolved Hide resolved
@yi-wu-arm
Copy link
Contributor Author

Hi @jeanPerier I have made some changes, mainly on test cases and use char * instead of std::int_8t for arg input. Saw your activity on GitHub, could you review the changes? Thanks in advance!

@klausler klausler removed their request for review January 9, 2024 16:47
Copy link
Contributor

@jeanPerier jeanPerier left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@yi-wu-arm yi-wu-arm merged commit 959a430 into llvm:main Jan 11, 2024
5 checks passed
justinfargnoli pushed a commit to justinfargnoli/llvm-project that referenced this pull request Jan 28, 2024
…rmat (llvm#71222)

reference to gfortran fdate
https://gcc.gnu.org/onlinedocs/gfortran/FDATE.html
usage:
```fortran
CHARACTER(32) :: time
CALL fdate(time)
WRITE(*,*) time
```

fdate is used in the ECP proxy application
https://proxyapps.exascaleproject.org/app/minismac2d/

https://github.com/Mantevo/miniSMAC/blob/f90446714226eeef650b78bce06ca4967792e74d/ref/smac2d.f#L1570

`fdate` now produce the same result on flang, compare to gfortran, where
If the length is too short to fit completely, blank return.
```fortran
  character(20) :: string
  call fdate(string)
  write(*, *) string, "X"
```
```bash
$ ../build-release/bin/flang-new test.f90 
$ ./a.out 
                      X
```
If length if larger than it requires(24), fill the rest of buffer space.
```fortran
  character(30) :: string
  call fdate(string)
  write(*, *) string, "X"
```
```bash
$ ../build-release/bin/flang-new test.f90 
$ ./a.out 
 Wed Nov 15 16:59:13 2023      X
```
The length value is hardcoded, because:
```c++
  // Day Mon dd hh:mm:ss yyyy\n\0 is 26 characters, e.g.
  // Tue May 26 21:51:03 2015\n\0
```
---------

Co-authored-by: Yi Wu <yiwu02@wdev-yiwu02.arm.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
flang:runtime flang Flang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants