-
Notifications
You must be signed in to change notification settings - Fork 15.3k
[flang] Register and lower SECNDS (stubbed implementation) #151878
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
Conversation
|
Thank you for submitting a Pull Request (PR) to the LLVM Project! This PR will be automatically labeled and the relevant teams will be notified. If you wish to, you can add reviewers by using the "Reviewers" section on this page. If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers. If you have further questions, they may be answered by the LLVM GitHub User Guide. You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums. |
|
@llvm/pr-subscribers-flang-fir-hlfir @llvm/pr-subscribers-flang-semantics Author: Šárka Holendová (mlir-maiden) ChangesThis patch registers and lowers the GNU extension intrinsic
TODOs:
CC @eugeneepshteyn @klausler Full diff: https://github.com/llvm/llvm-project/pull/151878.diff 4 Files Affected:
diff --git a/flang/include/flang/Optimizer/Builder/IntrinsicCall.h b/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
index d38c5b6d09a82..62ecd6c8a53f1 100644
--- a/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
+++ b/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
@@ -401,6 +401,8 @@ struct IntrinsicLibrary {
llvm::ArrayRef<fir::ExtendedValue>);
mlir::Value genScale(mlir::Type, llvm::ArrayRef<mlir::Value>);
fir::ExtendedValue genScan(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
+ fir::ExtendedValue genSecnds(mlir::Type resultType,
+ llvm::ArrayRef<fir::ExtendedValue> args);
fir::ExtendedValue genSecond(std::optional<mlir::Type>,
mlir::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genSelectedCharKind(mlir::Type,
diff --git a/flang/lib/Evaluate/intrinsics.cpp b/flang/lib/Evaluate/intrinsics.cpp
index 4773e136c41cb..634154d994c25 100644
--- a/flang/lib/Evaluate/intrinsics.cpp
+++ b/flang/lib/Evaluate/intrinsics.cpp
@@ -918,6 +918,11 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
{"back", AnyLogical, Rank::elemental, Optionality::optional},
DefaultingKIND},
KINDInt},
+ {"secnds",
+ {{"x", TypePattern{RealType, KindCode::exactKind, 4}, Rank::scalar,
+ Optionality::required, common::Intent::In}},
+ TypePattern{RealType, KindCode::exactKind, 4}, Rank::scalar,
+ IntrinsicClass::impureSubroutine},
{"second", {}, DefaultReal, Rank::scalar},
{"selected_char_kind", {{"name", DefaultChar, Rank::scalar}}, DefaultInt,
Rank::scalar, IntrinsicClass::transformationalFunction},
diff --git a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
index 8d0a511744e25..9b037cc66c59e 100644
--- a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
+++ b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
@@ -852,6 +852,10 @@ static constexpr IntrinsicHandler handlers[]{
{"back", asValue, handleDynamicOptional},
{"kind", asValue}}},
/*isElemental=*/true},
+ {"secnds",
+ &I::genSecnds,
+ {{{"x", asValue}}},
+ /*isElemental=*/false},
{"second",
&I::genSecond,
{{{"time", asAddr}}},
@@ -7722,6 +7726,14 @@ IntrinsicLibrary::genScan(mlir::Type resultType,
return readAndAddCleanUp(resultMutableBox, resultType, "SCAN");
}
+// SECNDS
+// Lowering is registered, runtime not yet implemented
+fir::ExtendedValue
+IntrinsicLibrary::genSecnds(mlir::Type resultType,
+ llvm::ArrayRef<fir::ExtendedValue> args) {
+ TODO(loc, "not yet implemented: SECNDS");
+}
+
// SECOND
fir::ExtendedValue
IntrinsicLibrary::genSecond(std::optional<mlir::Type> resultType,
diff --git a/flang/test/Lower/Intrinsics/secnds.f90 b/flang/test/Lower/Intrinsics/secnds.f90
new file mode 100644
index 0000000000000..105e93ad9301e
--- /dev/null
+++ b/flang/test/Lower/Intrinsics/secnds.f90
@@ -0,0 +1,12 @@
+!---------------------------------------------------------------------
+! RUN: %flang_fc1 -emit-fir %s -o - 2>&1 | FileCheck %s
+! XFAIL: *
+!---------------------------------------------------------------------
+
+program test_secnds
+ real :: x
+ x = secnds(1.0)
+end program
+
+! CHECK: not yet implemented: SECNDS
+
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hello, thank you for the patch!
I happen to know how to add secnds() to the runtime part, so I can quickly do that part.
genSecnds() though, is not part of runtime, it's part of flang compiler that generates MLIR/FIR/HLFIR code to set up the runtime call. Would you like to try to do that? That's probably going to be similar (but easier) to genEtime(), where the input arg is always one REAL.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hello Eugene,
Thank you for the review and the offer to handle the runtime. I’ll gladly take you up on that. Since the registration and lowering are confirmed, I’d like to take it a step further and wire genSecnds() to a stubbed RTNAME(Secnds) in time-intrinsic.cpp, so the full path is in place. I know how to do that. I’m on it and will follow up with a patch shortly.
Best,
Sarka
|
Also, could you please document this new intrinsic in https://github.com/llvm/llvm-project/blob/main/flang/docs/Intrinsics.md ? |
flang/lib/Evaluate/intrinsics.cpp
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure that the reference argument is always REAL(4), or is it the default REAL kind? Are the semantics in GNU Fortran affected when the default REAL kind is changed on the command line?
Optionality::required and Intent::In are the default settings, so you don't need to specify them again here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you, Peter. And nice to e-meet you.
I tested this with gfortran using -fdefault-real-8 to check whether SECNDS follows the default REAL kind. It turns out SECNDS requires REAL(4) explicitly. When compiled with
-fdefault-real-8, it produces a compile-time error:
'x' argument of 'secnds' intrinsic ... must be of kind 4
So the use of KindCode::exactKind, 4 matches gfortran's behavior.
Also noted on Optionality::required and Intent::In. I will drop those for clarity.
flang/lib/Evaluate/intrinsics.cpp
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that SECNDS is a function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went back and forth on the classification quite a bit.
SECNDS is a non-standard intrinsic, and GNU Fortran documents it simply as a function. But when mapping it to Flang’s IntrinsicClass, none of the available categories seem to fit perfectly. It is impure (as confirmed by GNU rejecting its use in PURE procedures), and while it has a scalar argument and a scalar return value, it cannot be classified as an elementalFunction. According to ISO/IEC 1539-1:2023 §15.9.1, “An elemental subprogram is a pure subprogram unless it has the prefix-spec IMPURE.” Since SECNDS has no ELEMENTAL or IMPURE prefix and is not pure, it doesn't qualify as elemental. I am assuming here that GNU Fortran would explicitly document those attributes if they were part of the intrinsic’s definition. ETIME is another non-standard intrinsic and is left unclassified in Flang. Based on that precedent and the lack of a defined category for impure functions, it seems best to leave SECNDS unclassified as well. I am open to suggestions if there is a more appropriate fit. Thank you :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi Peter, quick follow-up. I realized that leaving intrinsicClass blank defaults to elementalFunction, which would misclassify SECNDS. I also misspoke earlier when I said ETIME was left unclassified. I know it has two entries: one in genericIntrinsicFunction[] (unclassified, so it defaults to elementalFunction), and another in the intrinsicSubroutine table, where it’s correctly marked.
It also raises a question, since ETIME is impure and reads the system clock, should ETIME’s function form really default to elementalFunction? I am not sure what the best fit is for SECNDS. Thank you
|
Runtime PR: #152021 |
9a2a009 to
8c6c34a
Compare
…DS() (#152021) Until the compiler part is fully hooked up via #151878, tested this using `external`: ``` external secnds real s1, s2 s1 = secnds(0.0) print *, "Seconds from midnight:", s1 call sleep(2) s2 = secnds(s1) print *, "Seconds from s1", s2 print *, "Seconds from midnight:", secnds(0.0) end ```
|
@mlir-maiden , I submitted runtime implementation for In my implementation I defined runtime wrapper function for Feel free to modify, if that doesn't work for you. Peter also asked me to make the core implementation of |
…nction SECNDS() (#152021) Until the compiler part is fully hooked up via llvm/llvm-project#151878, tested this using `external`: ``` external secnds real s1, s2 s1 = secnds(0.0) print *, "Seconds from midnight:", s1 call sleep(2) s2 = secnds(s1) print *, "Seconds from s1", s2 print *, "Seconds from midnight:", secnds(0.0) end ```
It's |
Just to close on this, Peter and I confirmed that the extra intrinsic is |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that in Fortran the args are generally passed by reference, hence in my PR this is float *refTime.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be the case for routines called by Fortran, but it need not be so for runtime library entry points called only by lowered code, like this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Roger that, thank you
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
float *refTime
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one actually does not need to be a pointer or reference, and it would make lowering a little easier if it wasn't.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't it be something like return fir::CallOp::create(builder, loc, runtimeFunc, args).getResult(0); ? And need to fir::runtime::createArguments() to create args for refTime.
flang/lib/Evaluate/intrinsics.cpp
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This space between * and arg seems to be wrong. Did you run your changes through clang-format?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks Eugene! I did run clang-format, and you're right it looks like a space between * and arg got introduced here. I likely did that unintentionally. It's interesting that Clang-format did not enforce this spacing style. I will fix that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Eugene, I tracked this down. On my setup, a full clang-format pass reintroduces two issues in this file: (1) it inserts a space between * and the variable name (one occurrence, as in the screenshot above), and (2) it re-indents ++specIter in roughly three places. To avoid that churn, I pushed a small follow-up commit that hand-fixes just these spots - pointer spacing (no space after *) and the ++specIter indentation. I didn’t run clang-format over the whole file so these lines stay as intended.
flang/lib/Evaluate/intrinsics.cpp
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make sure that clang-format is happy with it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here, commented above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now you should be able to check for actual MLIR.
- Updated SECNDS handler to pass `refTime` asAddr instead of asValue, matching the backend runtime contract (`float*`). - Addressed review feedback in the lowering hook: * Built the argument list using fir::runtime::createArguments * Replaced previous call emission with fir::CallOp::create(...) - Updated the regression test for SECNDS lowering to check argument passing, call signature, and return type. - Added documentation for SECNDS to Intrinsics.md, including description, usage, and example.
8c6c34a to
c787475
Compare
mlir-maiden
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So nice to e-meet you, Peter and Eugene. What an introduction - you won’t believe what happened! I have been quickly replying to your review comments as they came in over the past week (and was so proud of it!), only to find out today that, due to a GitHub review workflow quirk, they were never actually sent. I’m still getting the hang of GitHub, so my apologies.
I’m submitting them now so they’re finally visible. I do want to work on extending DSECNDS, and thank you, Eugene, for the tip+template. It’s very helpful. Glad to finally be “online” with you both now.
@eugeneepshteyn @klausler
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hello Eugene,
Thank you for the review and the offer to handle the runtime. I’ll gladly take you up on that. Since the registration and lowering are confirmed, I’d like to take it a step further and wire genSecnds() to a stubbed RTNAME(Secnds) in time-intrinsic.cpp, so the full path is in place. I know how to do that. I’m on it and will follow up with a patch shortly.
Best,
Sarka
flang/lib/Evaluate/intrinsics.cpp
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you, Peter. And nice to e-meet you.
I tested this with gfortran using -fdefault-real-8 to check whether SECNDS follows the default REAL kind. It turns out SECNDS requires REAL(4) explicitly. When compiled with
-fdefault-real-8, it produces a compile-time error:
'x' argument of 'secnds' intrinsic ... must be of kind 4
So the use of KindCode::exactKind, 4 matches gfortran's behavior.
Also noted on Optionality::required and Intent::In. I will drop those for clarity.
flang/lib/Evaluate/intrinsics.cpp
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went back and forth on the classification quite a bit.
SECNDS is a non-standard intrinsic, and GNU Fortran documents it simply as a function. But when mapping it to Flang’s IntrinsicClass, none of the available categories seem to fit perfectly. It is impure (as confirmed by GNU rejecting its use in PURE procedures), and while it has a scalar argument and a scalar return value, it cannot be classified as an elementalFunction. According to ISO/IEC 1539-1:2023 §15.9.1, “An elemental subprogram is a pure subprogram unless it has the prefix-spec IMPURE.” Since SECNDS has no ELEMENTAL or IMPURE prefix and is not pure, it doesn't qualify as elemental. I am assuming here that GNU Fortran would explicitly document those attributes if they were part of the intrinsic’s definition. ETIME is another non-standard intrinsic and is left unclassified in Flang. Based on that precedent and the lack of a defined category for impure functions, it seems best to leave SECNDS unclassified as well. I am open to suggestions if there is a more appropriate fit. Thank you :)
flang/lib/Evaluate/intrinsics.cpp
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi Peter, quick follow-up. I realized that leaving intrinsicClass blank defaults to elementalFunction, which would misclassify SECNDS. I also misspoke earlier when I said ETIME was left unclassified. I know it has two entries: one in genericIntrinsicFunction[] (unclassified, so it defaults to elementalFunction), and another in the intrinsicSubroutine table, where it’s correctly marked.
It also raises a question, since ETIME is impure and reads the system clock, should ETIME’s function form really default to elementalFunction? I am not sure what the best fit is for SECNDS. Thank you
flang/lib/Evaluate/intrinsics.cpp
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks Eugene! I did run clang-format, and you're right it looks like a space between * and arg got introduced here. I likely did that unintentionally. It's interesting that Clang-format did not enforce this spacing style. I will fix that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Roger that, thank you
flang/lib/Evaluate/intrinsics.cpp
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Eugene, I tracked this down. On my setup, a full clang-format pass reintroduces two issues in this file: (1) it inserts a space between * and the variable name (one occurrence, as in the screenshot above), and (2) it re-indents ++specIter in roughly three places. To avoid that churn, I pushed a small follow-up commit that hand-fixes just these spots - pointer spacing (no space after *) and the ++specIter indentation. I didn’t run clang-format over the whole file so these lines stay as intended.
flang/lib/Evaluate/intrinsics.cpp
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here, commented above.
|
Feel free to extend |
|
@klausler Good idea. I’ll land SECNDS as-is, then follow up with a new PR to add impureFunction and reclassify it. |
|
@klausler @eugeneepshteyn |
I checked with Peter and he's ok with this going in. I'm going to check again today to ensure this still works with the latest upstream, and will then approve this PR. I don't think you have privileges to merge this in, so I'll do the merge myself. |
|
@mlir-maiden Congratulations on having your first Pull Request (PR) merged into the LLVM Project! Your changes will be combined with recent changes from other authors, then tested by our build bots. If there is a problem with a build, you may receive a report in an email or a comment on this PR. Please check whether problems have been caused by your change specifically, as the builds can include changes from many authors. It is not uncommon for your change to be included in a build that fails due to someone else's changes, or infrastructure issues. How to do this, and the rest of the post-merge process, is covered in detail here. If your change does cause a problem, it may be reverted, or you can revert it yourself. This is a normal part of LLVM development. You can fix your changes and open a new PR to merge them again. If you don't get any reports, no action is required from you. Your changes are working as expected, well done! |
This patch registers and lowers the GNU extension intrinsic
SECNDSin Flang.Fixes #58728
CC @eugeneepshteyn @klausler