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] GETPID runtime and lower intrinsic implementation #70442

Merged
merged 1 commit into from
Nov 13, 2023

Conversation

yi-wu-arm
Copy link
Contributor

Runtime function GetPID calls the function getpid
from unistd.h or process.h base on the OS.

@llvmbot llvmbot added flang:runtime flang Flang issues not falling into any other category flang:fir-hlfir flang:semantics labels Oct 27, 2023
@llvmbot
Copy link
Collaborator

llvmbot commented Oct 27, 2023

@llvm/pr-subscribers-flang-runtime
@llvm/pr-subscribers-flang-semantics

@llvm/pr-subscribers-flang-fir-hlfir

Author: Yi Wu (PAX-12-WU)

Changes

Runtime function GetPID calls the function getpid
from unistd.h or process.h base on the OS.


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

11 Files Affected:

  • (modified) flang/docs/Intrinsics.md (+1-1)
  • (modified) flang/include/flang/Optimizer/Builder/IntrinsicCall.h (+2)
  • (modified) flang/include/flang/Optimizer/Builder/Runtime/Command.h (+4)
  • (modified) flang/include/flang/Runtime/command.h (+5)
  • (modified) flang/lib/Evaluate/intrinsics.cpp (+7-5)
  • (modified) flang/lib/Optimizer/Builder/IntrinsicCall.cpp (+9)
  • (modified) flang/lib/Optimizer/Builder/Runtime/Command.cpp (+8)
  • (modified) flang/runtime/command.cpp (+7)
  • (added) flang/test/Lower/Intrinsics/gepid.f90 (+15)
  • (modified) flang/unittests/Optimizer/Builder/Runtime/CommandTest.cpp (+7)
  • (modified) flang/unittests/Runtime/CommandTest.cpp (+5)
diff --git a/flang/docs/Intrinsics.md b/flang/docs/Intrinsics.md
index ab0a940e53e5538..3726659edabf5d8 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, GET_COMMAND, GET_COMMAND_ARGUMENT, GET_ENVIRONMENT_VARIABLE, GETPID, 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/Optimizer/Builder/IntrinsicCall.h b/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
index 06db8cf9e9dc923..5065f11ae9e7264 100644
--- a/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
+++ b/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
@@ -223,6 +223,8 @@ struct IntrinsicLibrary {
   mlir::Value genFraction(mlir::Type resultType,
                           mlir::ArrayRef<mlir::Value> args);
   void genGetCommand(mlir::ArrayRef<fir::ExtendedValue> args);
+  mlir::Value genGetPID(mlir::Type resultType,
+                        llvm::ArrayRef<mlir::Value> args);
   void genGetCommandArgument(mlir::ArrayRef<fir::ExtendedValue> args);
   void genGetEnvironmentVariable(llvm::ArrayRef<fir::ExtendedValue>);
   fir::ExtendedValue genIall(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
diff --git a/flang/include/flang/Optimizer/Builder/Runtime/Command.h b/flang/include/flang/Optimizer/Builder/Runtime/Command.h
index 9ecdba2c995b713..976fb3aa0b6fbb7 100644
--- a/flang/include/flang/Optimizer/Builder/Runtime/Command.h
+++ b/flang/include/flang/Optimizer/Builder/Runtime/Command.h
@@ -31,6 +31,10 @@ mlir::Value genGetCommand(fir::FirOpBuilder &, mlir::Location,
                           mlir::Value command, mlir::Value length,
                           mlir::Value errmsg);
 
+/// Generate a call to the GetPID runtime function which implements the
+/// GETPID intrinsic.
+mlir::Value genGetPID(fir::FirOpBuilder &, mlir::Location);
+
 /// Generate a call to the GetCommandArgument runtime function which implements
 /// the GET_COMMAND_ARGUMENT intrinsic.
 /// \p value, \p length and \p errmsg must be fir.box that can be absent (but
diff --git a/flang/include/flang/Runtime/command.h b/flang/include/flang/Runtime/command.h
index ec6289390545479..711cca58240aa8f 100644
--- a/flang/include/flang/Runtime/command.h
+++ b/flang/include/flang/Runtime/command.h
@@ -11,6 +11,7 @@
 
 #include "flang/Runtime/entry-names.h"
 
+#include "sys/types.h" //pid_t
 #include <cstdint>
 
 namespace Fortran::runtime {
@@ -23,6 +24,10 @@ extern "C" {
 // integer kind.
 std::int32_t RTNAME(ArgumentCount)();
 
+// ??.?.??
+// Calls getpid()
+pid_t RTNAME(GetPID)();
+
 // 16.9.82 GET_COMMAND
 // Try to get the value of the whole command. All of the parameters are
 // optional.
diff --git a/flang/lib/Evaluate/intrinsics.cpp b/flang/lib/Evaluate/intrinsics.cpp
index e4125f6572aa983..ac67663aec3a08a 100644
--- a/flang/lib/Evaluate/intrinsics.cpp
+++ b/flang/lib/Evaluate/intrinsics.cpp
@@ -499,6 +499,7 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
     {"gamma", {{"x", SameReal}}, SameReal},
     {"get_team", {{"level", DefaultInt, Rank::scalar, Optionality::optional}},
         TeamType, Rank::scalar, IntrinsicClass::transformationalFunction},
+    {"getpid", {}, DefaultInt},
     {"huge",
         {{"x", SameIntOrReal, Rank::anyOrAssumedRank, Optionality::required,
             common::Intent::In, {ArgFlag::canBeNull}}},
@@ -1880,7 +1881,7 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
   int elementalRank{0};
   for (std::size_t j{0}; j < dummies; ++j) {
     const IntrinsicDummyArgument &d{dummy[std::min(j, dummyArgPatterns - 1)]};
-    if (const ActualArgument *arg{actualForDummy[j]}) {
+    if (const ActualArgument * arg{actualForDummy[j]}) {
       bool isAssumedRank{IsAssumedRank(*arg)};
       if (isAssumedRank && d.rank != Rank::anyOrAssumedRank &&
           d.rank != Rank::arrayOrAssumedRank) {
@@ -2217,7 +2218,8 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
     case Rank::locReduced:
     case Rank::scalarIfDim:
       if (dummy[*dimArg].optionality == Optionality::required) {
-        if (const Symbol *whole{
+        if (const Symbol *
+            whole{
                 UnwrapWholeSymbolOrComponentDataRef(actualForDummy[*dimArg])}) {
           if (IsOptional(*whole) || IsAllocatableOrObjectPointer(whole)) {
             if (rank == Rank::scalarIfDim || arrayRank.value_or(-1) == 1) {
@@ -2292,7 +2294,7 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
   // Rearrange the actual arguments into dummy argument order.
   ActualArguments rearranged(dummies);
   for (std::size_t j{0}; j < dummies; ++j) {
-    if (ActualArgument *arg{actualForDummy[j]}) {
+    if (ActualArgument * arg{actualForDummy[j]}) {
       rearranged[j] = std::move(*arg);
     }
   }
@@ -2929,7 +2931,7 @@ static bool ApplySpecificChecks(SpecificCall &call, FoldingContext &context) {
     ok &= CheckForCoindexedObject(context, call.arguments[3], name, "errmsg");
     if (call.arguments[0] && call.arguments[1]) {
       for (int j{0}; j < 2; ++j) {
-        if (const Symbol *last{GetLastSymbol(call.arguments[j])};
+        if (const Symbol * last{GetLastSymbol(call.arguments[j])};
             last && !IsAllocatable(last->GetUltimate())) {
           context.messages().Say(call.arguments[j]->sourceLocation(),
               "Argument #%d to MOVE_ALLOC must be allocatable"_err_en_US,
@@ -2949,7 +2951,7 @@ static bool ApplySpecificChecks(SpecificCall &call, FoldingContext &context) {
     const auto &arg{call.arguments[0]};
     if (arg) {
       if (const auto *expr{arg->UnwrapExpr()}) {
-        if (const Symbol *symbol{UnwrapWholeSymbolDataRef(*expr)}) {
+        if (const Symbol * symbol{UnwrapWholeSymbolDataRef(*expr)}) {
           ok = symbol->attrs().test(semantics::Attr::OPTIONAL);
         }
       }
diff --git a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
index 0a023bc6b21ea03..454d38592a8bc4f 100644
--- a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
+++ b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
@@ -253,6 +253,7 @@ static constexpr IntrinsicHandler handlers[]{
        {"trim_name", asAddr, handleDynamicOptional},
        {"errmsg", asBox, handleDynamicOptional}}},
      /*isElemental=*/false},
+    {"getpid", &I::genGetPID},
     {"iachar", &I::genIchar},
     {"iall",
      &I::genIall,
@@ -2936,6 +2937,14 @@ void IntrinsicLibrary::genGetCommand(llvm::ArrayRef<fir::ExtendedValue> args) {
   }
 }
 
+// GETPID
+mlir::Value IntrinsicLibrary::genGetPID(mlir::Type resultType,
+                                        llvm::ArrayRef<mlir::Value> args) {
+  assert(args.size() == 0 && "getpid takes no input");
+  return builder.createConvert(loc, resultType,
+                               fir::runtime::genGetPID(builder, loc));
+}
+
 // GET_COMMAND_ARGUMENT
 void IntrinsicLibrary::genGetCommandArgument(
     llvm::ArrayRef<fir::ExtendedValue> args) {
diff --git a/flang/lib/Optimizer/Builder/Runtime/Command.cpp b/flang/lib/Optimizer/Builder/Runtime/Command.cpp
index f56475a97487835..1d719e7bbd9a2d1 100644
--- a/flang/lib/Optimizer/Builder/Runtime/Command.cpp
+++ b/flang/lib/Optimizer/Builder/Runtime/Command.cpp
@@ -48,6 +48,14 @@ mlir::Value fir::runtime::genGetCommand(fir::FirOpBuilder &builder,
   return builder.create<fir::CallOp>(loc, runtimeFunc, args).getResult(0);
 }
 
+mlir::Value fir::runtime::genGetPID(fir::FirOpBuilder &builder,
+                                    mlir::Location loc) {
+  auto runtimeFunc =
+      fir::runtime::getRuntimeFunc<mkRTKey(GetPID)>(loc, builder);
+
+  return builder.create<fir::CallOp>(loc, runtimeFunc).getResult(0);
+}
+
 mlir::Value fir::runtime::genGetCommandArgument(
     fir::FirOpBuilder &builder, mlir::Location loc, mlir::Value number,
     mlir::Value value, mlir::Value length, mlir::Value errmsg) {
diff --git a/flang/runtime/command.cpp b/flang/runtime/command.cpp
index b81a0791c5e571b..3f719e80bba2662 100644
--- a/flang/runtime/command.cpp
+++ b/flang/runtime/command.cpp
@@ -14,6 +14,11 @@
 #include "flang/Runtime/descriptor.h"
 #include <cstdlib>
 #include <limits>
+#ifdef _WIN32
+#include <process.h> //getpid()
+#else
+#include <unistd.h> //getpid()
+#endif
 
 namespace Fortran::runtime {
 std::int32_t RTNAME(ArgumentCount)() {
@@ -25,6 +30,8 @@ std::int32_t RTNAME(ArgumentCount)() {
   return 0;
 }
 
+pid_t RTNAME(GetPID)() { return getpid(); }
+
 // Returns the length of the \p string. Assumes \p string is valid.
 static std::int64_t StringLength(const char *string) {
   std::size_t length{std::strlen(string)};
diff --git a/flang/test/Lower/Intrinsics/gepid.f90 b/flang/test/Lower/Intrinsics/gepid.f90
new file mode 100644
index 000000000000000..ca5b2ddec50214d
--- /dev/null
+++ b/flang/test/Lower/Intrinsics/gepid.f90
@@ -0,0 +1,15 @@
+! RUN: bbc -emit-fir %s -o - | FileCheck %s
+
+! CHECK-LABEL: func.func @_QPall_args() {
+! CHECK:         %[[VAL_0:.*]] = fir.alloca i32 {bindc_name = "pid", uniq_name = "_QFall_argsEpid"}
+! CHECK:         %[[VAL_1:.*]] = fir.call @_FortranAGetPID() fastmath<contract> : () -> i32
+! CHECK:         fir.store %[[VAL_1:.*]] to %[[VAL_0:.*]] : !fir.ref<i32>
+! CHECK:         return
+! CHECK:       }
+
+subroutine all_args()
+  integer :: pid
+  pid = getpid()
+end
+
+
diff --git a/flang/unittests/Optimizer/Builder/Runtime/CommandTest.cpp b/flang/unittests/Optimizer/Builder/Runtime/CommandTest.cpp
index acc79ae63e9f698..58a151447d5b4f7 100644
--- a/flang/unittests/Optimizer/Builder/Runtime/CommandTest.cpp
+++ b/flang/unittests/Optimizer/Builder/Runtime/CommandTest.cpp
@@ -44,3 +44,10 @@ TEST_F(RuntimeCallTest, genGetEnvVariable) {
   checkCallOp(result.getDefiningOp(), "_FortranAGetEnvVariable", /*nbArgs=*/5,
       /*addLocArgs=*/true);
 }
+
+TEST_F(RuntimeCallTest, genGetPID) {
+  mlir::Location loc = firBuilder->getUnknownLoc();
+  mlir::Value result = fir::runtime::genGetPID(*firBuilder, loc);
+  checkCallOp(result.getDefiningOp(), "_FortranAGetPID", /*nbArgs=*/0,
+      /*addLocArgs=*/false);
+}
\ No newline at end of file
diff --git a/flang/unittests/Runtime/CommandTest.cpp b/flang/unittests/Runtime/CommandTest.cpp
index c3571c9684e4b07..9f66c7924c86e3c 100644
--- a/flang/unittests/Runtime/CommandTest.cpp
+++ b/flang/unittests/Runtime/CommandTest.cpp
@@ -388,6 +388,11 @@ TEST_F(OnlyValidArguments, GetCommandShortLength) {
   CheckDescriptorEqInt<short>(length.get(), 51);
 }
 
+TEST_F(ZeroArguments, GetPID) {
+  // pid should always greater than 0, in both linux and windows
+  EXPECT_GT(RTNAME(GetPID)(), 0);
+}
+
 class EnvironmentVariables : public CommandFixture {
 protected:
   EnvironmentVariables() : CommandFixture(0, nullptr) {

@yi-wu-arm
Copy link
Contributor Author

I have tried to implement it in flang/runtime/extension

// RESULT = GETPID()
pid_t FORTRAN_PROCEDURE_NAME(getpid)() { return RTNAME(GetPID)(); }

However, when testing getpid in a file, getpid would return a float rather than an i32, the same applies even if I change the return type from pid_t -> std::int32.
There is a workaround, by renaming the function from getpid -> igetpid

... Before After
fuc name getpid igetpid
-S -emit-llvm %3 = call contract float @getpid_() %3 = call i32 @igetpid_()

@kiranchandramohan
Copy link
Contributor

Looks OK. Please wait for @klausler.

@klausler
Copy link
Contributor

getpid needs to be defined as an intrinsic function in order to get its result type correct.

@yi-wu-arm
Copy link
Contributor Author

yi-wu-arm commented Oct 31, 2023

getpid needs to be defined as an intrinsic function in order to get its result type correct.

How can I define it as intrinsic function to get a correct result type? I have tried renaming iargc to argc in extension.cpp, it will be called as call contract float @argc_(). @klausler
I've defined getpid in flang/lib/intrinsics.cpp and flang/lib/optimizer/builder/intrinsicsCall.cpp. However, if it's defined in extension.cpp, which only calls a runtime function in flang/runtime/command.cpp, how does the intrinsic function affect the return type in extension.cpp?


I initially tried in extension.cpp but it didn't work due to reason mention above, so I switched to do in it intrinsics and instrinsicsCall.cpp. I still don't understand why adding/remove an i would have such effect in estension.cpp

@klausler
Copy link
Contributor

getpid needs to be defined as an intrinsic function in order to get its result type correct.

How can I define it as intrinsic function to get a correct result type? I have tried renaming iargc to argc in extension.cpp, it will be called as call contract float @argc_(). @klausler I've defined getpid in flang/lib/intrinsics.cpp and flang/lib/optimizer/builder/intrinsicsCall.cpp. However, if it's defined in extension.cpp, which only calls a runtime function in flang/runtime/command.cpp, how does the intrinsic function affect the return type in extension.cpp?

I initially tried in extension.cpp but it didn't work due to reason mention above, so I switched to do in it intrinsics and instrinsicsCall.cpp. I still don't understand why adding/remove an i would have such effect in estension.cpp

Types are determined in semantics. By default, in Fortran, a name with no explicit type is integer if its first letter is in the range I through N, and real otherwise.

@yi-wu-arm
Copy link
Contributor Author

getpid needs to be defined as an intrinsic function in order to get its result type correct.

How can I define it as intrinsic function to get a correct result type? I have tried renaming iargc to argc in extension.cpp, it will be called as call contract float @argc_(). @klausler I've defined getpid in flang/lib/intrinsics.cpp and flang/lib/optimizer/builder/intrinsicsCall.cpp. However, if it's defined in extension.cpp, which only calls a runtime function in flang/runtime/command.cpp, how does the intrinsic function affect the return type in extension.cpp?
I initially tried in extension.cpp but it didn't work due to reason mention above, so I switched to do in it intrinsics and instrinsicsCall.cpp. I still don't understand why adding/remove an i would have such effect in estension.cpp

Types are determined in semantics. By default, in Fortran, a name with no explicit type is integer if its first letter is in the range I through N, and real otherwise.

Thanks a lot, if I have understood correctly, I have it defined as a lowering intrinsic function under intrinsic.cpp and intrinsicCalls.cpp. This gives a correct output.
It is just that I haven't found a way to do it in extension.cpp, due to the result type is not correct, and it is excluded in this change.

flang/runtime/command.cpp Outdated Show resolved Hide resolved
@yi-wu-arm yi-wu-arm force-pushed the getpid branch 5 times, most recently from 930b62b to 9aaf5ae Compare November 1, 2023 14:21
@yi-wu-arm yi-wu-arm force-pushed the getpid branch 3 times, most recently from ac211f4 to 109a072 Compare November 1, 2023 18:30
@yi-wu-arm yi-wu-arm force-pushed the getpid branch 5 times, most recently from 174d304 to b4d44c2 Compare November 3, 2023 10:19
@yi-wu-arm yi-wu-arm force-pushed the getpid branch 2 times, most recently from 35d9c4c to 7cdffc2 Compare November 3, 2023 16:42
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 thanks

Runtime function GetPID calls the function getpid
from unistd.h or processthreadsapi.h base on the OS.

Processthreadsapi.g is supported since Windows XP:
https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getcurrentprocessid
and GetCurrentProcessId returns a DWORD, an unsigned 32-bit int:
https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/262627d8-3418-4627-9218-4ffe110850b2
@yi-wu-arm
Copy link
Contributor Author

getpid needs to be defined as an intrinsic function in order to get its result type correct.

Thanks @klausler for the input. I am assuming you meant to add an entry to genericIntrinsicFunction in flang/lib/Evaluate/intrinsics.cpp for defining an intrinsic function. If so, that is the approach I have chosen in this patch. If that looks OK, could you please review.

@kiranchandramohan kiranchandramohan merged commit de58aa8 into llvm:main Nov 13, 2023
4 checks passed
zahiraam pushed a commit to zahiraam/llvm-project that referenced this pull request Nov 20, 2023
Runtime function GetPID calls the function getpid
from unistd.h or process.h base on the OS.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
flang:fir-hlfir flang:runtime flang:semantics flang Flang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants