Unverified Commit aeb14f78 authored by NimishMishra's avatar NimishMishra Committed by GitHub
Browse files

[flang][flang-rt] Add support for non-standard TIMEF intrinsic (#185377)

TIMEF is an Intel intrinsic that returns the number of seconds since the
first time it is called, or zero. This PR adds support for the same.

Link to Intel's documentation:
https://www.intel.com/content/www/us/en/docs/fortran-compiler/developer-guide-reference/2025-3/timef.html



---------

Co-authored-by: default avatarTarun Prabhu <tarunprabhu@gmail.com>
parent c75e1e62
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -67,6 +67,9 @@ struct ExecutionEnvironment {
      decimal::FortranRounding::RoundNearest}; // RP(==PN)
  Convert conversion{Convert::Unknown}; // FORT_CONVERT
  bool noStopMessage{false}; // NO_STOP_MESSAGE=1 inhibits "Fortran STOP"
  // FLANG_TIMEF_IN_MILLISECONDS=1 sets TIMEF resolution to milliseconds.
  // Default resolution is seconds.
  bool timefInMillisec{false};
  bool defaultUTF8{false}; // DEFAULT_UTF8
  bool checkPointerDeallocation{true}; // FORT_CHECK_POINTER_DEALLOCATION
  bool truncateStream{true}; // FORT_TRUNCATE_STREAM
+13 −0
Original line number Diff line number Diff line
@@ -166,6 +166,19 @@ void ExecutionEnvironment::Configure(int ac, const char *av[],
    }
  }

  if (auto *x{std::getenv("FLANG_TIMEF_IN_MILLISECONDS")}) {
    char *end;
    auto n{std::strtol(x, &end, 10)};
    if (n >= 0 && n <= 1 && *end == '\0') {
      timefInMillisec = n != 0;
    } else {
      std::fprintf(stderr,
          "Fortran runtime: FLANG_TIMEF_IN_MILLISECONDS=%s is invalid; "
          "ignored\n",
          x);
    }
  }

  if (auto *x{std::getenv("DEFAULT_UTF8")}) {
    char *end;
    auto n{std::strtol(x, &end, 10)};
+53 −0
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@
#include "flang/Runtime/extensions.h"
#include "unit.h"
#include "flang-rt/runtime/descriptor.h"
#include "flang-rt/runtime/environment.h"
#include "flang-rt/runtime/lock.h"
#include "flang-rt/runtime/terminator.h"
#include "flang-rt/runtime/tools.h"
@@ -55,6 +56,7 @@ inline void CtimeBuffer(char *buffer, size_t bufsize, const time_t cur_time,

#ifndef _WIN32
// posix-compliant and has getlogin_r and F_OK
#include <sys/times.h>
#include <unistd.h>
#else
#include <direct.h>
@@ -413,6 +415,57 @@ double RTNAME(Dsecnds)(double *refTime, const char *sourceFile, int line) {
// GNU extension function TIME()
std::int64_t RTNAME(time)() { return time(nullptr); }

// Extension function TIMEF().
// By default, it returns number of seconds that have elapsed since the first
// time TIMEF was called. For the first call, it returns 0.
// FLANG_TIMEF_IN_MILLISECONDS=1 sets the resolution to milliseconds.
double RTNAME(Timef)() {
#ifndef _WIN32
  // posix-compliant
  static clock_t start = static_cast<clock_t>(-1);
  static long ticks_per_sec = 0;
  static Lock timef_lock;
  static bool isInit{false};

  struct tms b;
  clock_t current;
  double duration;
  {
    CriticalSection critical{timef_lock};
    if (ticks_per_sec <= 0) {
      ticks_per_sec = sysconf(_SC_CLK_TCK);
      if (ticks_per_sec <= 0)
        return 0.0;
    }

    if (times(&b) == static_cast<clock_t>(-1)) {
      return 0.0;
    }

    current = b.tms_utime + b.tms_stime;

    if (!isInit) {
      isInit = true;
      start = current;
      return 0.0;
    }
    if (Fortran::runtime::executionEnvironment.timefInMillisec) {
      duration =
          (static_cast<double>(current - start) * 1000.0) / ticks_per_sec;
    } else {
      duration = static_cast<double>(current - start) / ticks_per_sec;
    }

    return duration;
  }
#else
  // TODO: Windows implementation. Currently, we return a dummy
  // non-zero value to prevent the `TIMEF` unittest from
  // hanging
  return 1.0;
#endif
}

// MCLOCK: returns accumulated CPU time in ticks
std::int32_t FORTRAN_PROCEDURE_NAME(mclock)() { return std::clock(); }

+20 −0
Original line number Diff line number Diff line
@@ -31,6 +31,26 @@ TEST(TimeIntrinsics, CpuTime) {
  }
}

TEST(TimeIntrinsics, Timef) {
  // We can't really test that we get the "right" result for Timef, but we
  // can have a smoke test to see that we get something reasonable on the
  // platforms where we expect to support it.
  double start{RTNAME(Timef)()}, end{0.0};
  ASSERT_GE(start, 0.0);

  // Loop until we get a different value from Timef. If we don't get one
  // before we time out, then we should probably look into an implementation
  // for Timef with a better timer resolution.
  // By default, this loop should burn for 1 second.
  for (end = start; end == start; end = RTNAME(Timef)()) {
    ASSERT_GE(end, 0.0);
    ASSERT_GE(end, start);
  }

  ASSERT_GE(end, 0.0);
  ASSERT_GE(end, start);
}

using count_t = std::int64_t;

TEST(TimeIntrinsics, SystemClock) {
+18 −0
Original line number Diff line number Diff line
@@ -1233,6 +1233,24 @@ PROGRAM example_time
END PROGRAM
```

### Non-Standard Intrinsics: TIMEF

#### Description
`TIMEF` returns the CPU time in number of seconds that have elapsed since 
the first time TIMEF was called. The first time it is called, TIMEF returns 0.

By default, the behaviour matches that of ifort and classic-flang. To match the
behaviour of XLF and nvfortran, set `FLANG_TIMEF_IN_MILLISECONDS=1` in your environment.
This will cause `TIMEF` to return the number of milliseconds elapsed since the first time 
`TIMEF` was called.

#### Usage and Info

- **Standard:** Intel/classic-flang extension
- **Class:** function
- **Syntax:** `RESULT = TIMEF()`
- **Return type**: REAL(8)

### Non-Standard Intrinsics: UNLINK

#### Description
Loading