Skip to content

Commit 5a34e6f

Browse files
authored
[flang] Implement CHDIR intrinsic (#124280)
This intrinsic is a gnu extension (https://gcc.gnu.org/onlinedocs/gfortran/CHDIR.html) and is used in FLEUR (https://github.com/JuDFTteam/FLEUR).
1 parent e811cb0 commit 5a34e6f

File tree

9 files changed

+200
-4
lines changed

9 files changed

+200
-4
lines changed

flang/docs/Intrinsics.md

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -767,7 +767,7 @@ This phase currently supports all the intrinsic procedures listed above but the
767767
| Object characteristic inquiry functions | ALLOCATED, ASSOCIATED, EXTENDS_TYPE_OF, IS_CONTIGUOUS, PRESENT, RANK, SAME_TYPE, STORAGE_SIZE |
768768
| Type inquiry intrinsic functions | BIT_SIZE, DIGITS, EPSILON, HUGE, KIND, MAXEXPONENT, MINEXPONENT, NEW_LINE, PRECISION, RADIX, RANGE, TINY|
769769
| 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, GETPID, NUMARG, BADDRESS, IADDR, CACHESIZE, EOF, FP_CLASS, INT_PTR_KIND, ISNAN, MALLOC, FREE, GETUID, GETGID |
770-
| 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, SIGNAL, SLEEP, SYSTEM, SYSTEM_CLOCK |
770+
| Intrinsic subroutines |MVBITS (elemental), CHDIR, 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, SIGNAL, SLEEP, SYSTEM, SYSTEM_CLOCK |
771771
| Atomic intrinsic subroutines | ATOMIC_ADD |
772772
| Collective intrinsic subroutines | CO_REDUCE |
773773
| Library subroutines | BACKTRACE, FDATE, GETLOG, GETENV |
@@ -1064,3 +1064,34 @@ This intrinsic is an alias for `LEN_TRIM`, without the optional KIND argument.
10641064
- **Arguments:** `TIME` - a REAL value into which the elapsed CPU time in
10651065
seconds is written
10661066
- **RETURN value:** same as TIME argument
1067+
1068+
### Non-Standard Intrinsics: CHDIR
1069+
1070+
#### Description
1071+
`CHDIR(NAME[, STATUS])` Change current working directory to a specified path.
1072+
1073+
This intrinsic is provided in both subroutine and function forms; however, only one form can be used in any given program unit.
1074+
*STATUS* is `INTENT(OUT)` and provide the following:
1075+
1076+
| | |
1077+
|------------|---------------------------------------------------------------------------------------------------|
1078+
| `NAME` | The type shall be `CHARACTER` of default kind and shall specify a valid path within the file system. |
1079+
| `STATUS` | (Optional) Status flag. Returns 0 on success, a system specific and nonzero error code otherwise. The type shall be `INTEGER` and of the default kind. |
1080+
1081+
#### Usage and Info
1082+
1083+
- **Standard:** GNU extension
1084+
- **Class:** Subroutine, function
1085+
- **Syntax:** `CALL CHDIR(NAME[, STATUS])` and `STATUS = CHDIR(NAME)`
1086+
1087+
#### Example
1088+
```Fortran
1089+
program chdir_func
1090+
character(len=) :: path
1091+
integer :: status
1092+
1093+
call chdir("/tmp")
1094+
status = chdir("..")
1095+
print *, "status: ", status
1096+
end program chdir_func
1097+
```

flang/include/flang/Optimizer/Builder/IntrinsicCall.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,8 @@ struct IntrinsicLibrary {
210210
mlir::Value genBtest(mlir::Type, llvm::ArrayRef<mlir::Value>);
211211
mlir::Value genCeiling(mlir::Type, llvm::ArrayRef<mlir::Value>);
212212
fir::ExtendedValue genChar(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
213+
fir::ExtendedValue genChdir(std::optional<mlir::Type> resultType,
214+
llvm::ArrayRef<fir::ExtendedValue>);
213215
template <mlir::arith::CmpIPredicate pred>
214216
fir::ExtendedValue genCharacterCompare(mlir::Type,
215217
llvm::ArrayRef<fir::ExtendedValue>);

flang/include/flang/Optimizer/Builder/Runtime/Intrinsics.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ void genSignal(fir::FirOpBuilder &builder, mlir::Location loc,
9090
void genSleep(fir::FirOpBuilder &builder, mlir::Location loc,
9191
mlir::Value seconds);
9292

93+
/// generate chdir runtime call
94+
mlir::Value genChdir(fir::FirOpBuilder &builder, mlir::Location loc,
95+
mlir::Value name);
96+
9397
} // namespace runtime
9498
} // namespace fir
9599

flang/include/flang/Runtime/extensions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,5 +69,8 @@ std::int64_t FORTRAN_PROCEDURE_NAME(access)(const char *name,
6969
std::int64_t nameLength, const char *mode, std::int64_t modeLength);
7070
#endif
7171

72+
// GNU extension subroutine CHDIR(NAME, [STATUS])
73+
int RTNAME(Chdir)(const char *name);
74+
7275
} // extern "C"
7376
#endif // FORTRAN_RUNTIME_EXTENSIONS_H_

flang/lib/Evaluate/intrinsics.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,8 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
406406
DefaultLogical},
407407
{"ceiling", {{"a", AnyReal}, DefaultingKIND}, KINDInt},
408408
{"char", {{"i", AnyInt, Rank::elementalOrBOZ}, DefaultingKIND}, KINDChar},
409+
{"chdir", {{"name", DefaultChar, Rank::scalar, Optionality::required}},
410+
DefaultInt},
409411
{"cmplx", {{"x", AnyComplex}, DefaultingKIND}, KINDComplex},
410412
{"cmplx",
411413
{{"x", AnyIntUnsignedOrReal, Rank::elementalOrBOZ},
@@ -1413,6 +1415,11 @@ static const IntrinsicInterface intrinsicSubroutine[]{
14131415
{"stat", AnyInt, Rank::scalar, Optionality::optional,
14141416
common::Intent::Out}},
14151417
{}, Rank::elemental, IntrinsicClass::atomicSubroutine},
1418+
{"chdir",
1419+
{{"name", DefaultChar, Rank::scalar, Optionality::required},
1420+
{"status", AnyInt, Rank::scalar, Optionality::optional,
1421+
common::Intent::Out}},
1422+
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
14161423
{"co_broadcast",
14171424
{{"a", AnyData, Rank::anyOrAssumedRank, Optionality::required,
14181425
common::Intent::InOut},
@@ -2742,8 +2749,8 @@ bool IntrinsicProcTable::Implementation::IsDualIntrinsic(
27422749
const std::string &name) const {
27432750
// Collection for some intrinsics with function and subroutine form,
27442751
// in order to pass the semantic check.
2745-
static const std::string dualIntrinsic[]{
2746-
{"etime"s}, {"getcwd"s}, {"rename"s}, {"second"s}, {"system"s}};
2752+
static const std::string dualIntrinsic[]{{"chdir"s}, {"etime"s}, {"getcwd"s},
2753+
{"rename"s}, {"second"s}, {"system"s}};
27472754

27482755
return llvm::is_contained(dualIntrinsic, name);
27492756
}

flang/lib/Optimizer/Builder/IntrinsicCall.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,10 @@ static constexpr IntrinsicHandler handlers[]{
205205
{"c_ptr_ne", &I::genCPtrCompare<mlir::arith::CmpIPredicate::ne>},
206206
{"ceiling", &I::genCeiling},
207207
{"char", &I::genChar},
208+
{"chdir",
209+
&I::genChdir,
210+
{{{"name", asAddr}, {"status", asAddr, handleDynamicOptional}}},
211+
/*isElemental=*/false},
208212
{"cmplx",
209213
&I::genCmplx,
210214
{{{"x", asValue}, {"y", asValue, handleDynamicOptional}}}},
@@ -3186,6 +3190,35 @@ IntrinsicLibrary::genChar(mlir::Type type,
31863190
return fir::CharBoxValue{cast, len};
31873191
}
31883192

3193+
// CHDIR
3194+
fir::ExtendedValue
3195+
IntrinsicLibrary::genChdir(std::optional<mlir::Type> resultType,
3196+
llvm::ArrayRef<fir::ExtendedValue> args) {
3197+
assert((args.size() == 1 && resultType.has_value()) ||
3198+
(args.size() >= 1 && !resultType.has_value()));
3199+
mlir::Value name = fir::getBase(args[0]);
3200+
mlir::Value status = fir::runtime::genChdir(builder, loc, name);
3201+
3202+
if (resultType.has_value()) {
3203+
return status;
3204+
} else {
3205+
// Subroutine form, store status and return none.
3206+
if (!isStaticallyAbsent(args[1])) {
3207+
mlir::Value statusAddr = fir::getBase(args[1]);
3208+
statusAddr.dump();
3209+
mlir::Value statusIsPresentAtRuntime =
3210+
builder.genIsNotNullAddr(loc, statusAddr);
3211+
builder.genIfThen(loc, statusIsPresentAtRuntime)
3212+
.genThen([&]() {
3213+
builder.createStoreWithConvert(loc, status, statusAddr);
3214+
})
3215+
.end();
3216+
}
3217+
}
3218+
3219+
return {};
3220+
}
3221+
31893222
// CMPLX
31903223
mlir::Value IntrinsicLibrary::genCmplx(mlir::Type resultType,
31913224
llvm::ArrayRef<mlir::Value> args) {

flang/lib/Optimizer/Builder/Runtime/Intrinsics.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,3 +384,13 @@ void fir::runtime::genSleep(fir::FirOpBuilder &builder, mlir::Location loc,
384384
fir::runtime::getRuntimeFunc<mkRTKey(Sleep)>(loc, builder)};
385385
builder.create<fir::CallOp>(loc, func, seconds);
386386
}
387+
388+
/// generate chdir runtime call
389+
mlir::Value fir::runtime::genChdir(fir::FirOpBuilder &builder,
390+
mlir::Location loc, mlir::Value name) {
391+
mlir::func::FuncOp func{
392+
fir::runtime::getRuntimeFunc<mkRTKey(Chdir)>(loc, builder)};
393+
llvm::SmallVector<mlir::Value> args =
394+
fir::runtime::createArguments(builder, loc, func.getFunctionType(), name);
395+
return builder.create<fir::CallOp>(loc, func, args).getResult(0);
396+
}

flang/runtime/extensions.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ inline void CtimeBuffer(char *buffer, size_t bufsize, const time_t cur_time,
5151

5252
#ifndef _WIN32
5353
// posix-compliant and has getlogin_r and F_OK
54-
#include <unistd.h>
54+
#include <unistd.h>
55+
#else
56+
#include <direct.h>
5557
#endif
5658

5759
extern "C" {
@@ -248,5 +250,15 @@ std::int64_t FORTRAN_PROCEDURE_NAME(access)(const char *name,
248250
}
249251
#endif
250252

253+
// CHDIR(DIR)
254+
int RTNAME(Chdir)(const char *name) {
255+
// chdir alias seems to be deprecated on Windows.
256+
#ifndef _WIN32
257+
return chdir(name);
258+
#else
259+
return _chdir(name);
260+
#endif
261+
}
262+
251263
} // namespace Fortran::runtime
252264
} // extern "C"

flang/test/Lower/Intrinsics/chdir.f90

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
! RUN: bbc -emit-hlfir %s -o - | FileCheck %s
2+
3+
subroutine test_chdir()
4+
implicit none
5+
! CHECK-LABEL: func.func @_QPtest_chdir() {
6+
7+
call chdir("..")
8+
! CHECK: %[[VAL_0:.*]] = fir.address_of(@_QQclX2E2E) : !fir.ref<!fir.char<1,2>>
9+
! CHECK: %[[C_2:.*]] = arith.constant 2 : index
10+
! CHECK: %[[VAL_1:.*]]:2 = hlfir.declare %[[VAL_0]] typeparams %[[C_2]] {fortran_attrs = #fir.var_attrs<parameter>, uniq_name = "_QQclX2E2E"} : (!fir.ref<!fir.char<1,2>>, index) -> (!fir.ref<!fir.char<1,2>>, !fir.ref<!fir.char<1,2>>)
11+
! CHECK: %[[VAL_2:.*]] = fir.convert %[[VAL_1]]#1 : (!fir.ref<!fir.char<1,2>>) -> !fir.ref<i8>
12+
! CHECK: %[[VAL_3:.*]] = fir.call @_FortranAChdir(%[[VAL_2]]) fastmath<contract> : (!fir.ref<i8>) -> i32
13+
end subroutine
14+
15+
subroutine test_chdir_subroutine_status_i4()
16+
implicit none
17+
integer(4) :: stat
18+
! CHECK-LABEL: func.func @_QPtest_chdir_subroutine_status_i4() {
19+
20+
call chdir("..", STATUS=stat)
21+
! CHECK: %[[VAL_0:.*]] = fir.alloca i32 {bindc_name = "stat", uniq_name = "_QFtest_chdir_subroutine_status_i4Estat"}
22+
! CHECK: %[[VAL_1:.*]]:2 = hlfir.declare %[[VAL_0]] {uniq_name = "_QFtest_chdir_subroutine_status_i4Estat"} : (!fir.ref<i32>) ->
23+
! (!fir.ref<i32>, !fir.ref<i32>)
24+
! CHECK: %[[VAL_2:.*]] = fir.address_of(@_QQclX2E2E) : !fir.ref<!fir.char<1,2>>
25+
! CHECK: %[[C_2:.*]] = arith.constant 2 : index
26+
! CHECK: %[[VAL_3:.*]]:2 = hlfir.declare %[[VAL_2]] typeparams %[[C_2]] {fortran_attrs = #fir.var_attrs<parameter>, uniq_name = {{.*}} : (!fir.ref<!fir.char<1,2>>, index) -> (!fir.ref<!fir.char<1,2>>, !fir.ref<!fir.char<1,2>>)
27+
! CHECK: %[[VAL_4:.*]] = fir.convert %[[VAL_3]]#1 : (!fir.ref<!fir.char<1,2>>) -> !fir.ref<i8>
28+
! CHECK: %[[VAL_5:.*]] = fir.call @_FortranAChdir(%[[VAL_4]]) fastmath<contract> : (!fir.ref<i8>) -> i32
29+
! CHECK: %[[VAL_6:.*]] = fir.convert %{{.*}} : (!fir.ref<i32>) -> i64
30+
! CHECK: %[[C_0_I64:.*]] = arith.constant 0 : i64
31+
! CHECK: %[[VAL_7:.*]] = arith.cmpi ne, %[[VAL_6]], %[[C_0_I64]] : i64
32+
! CHECK: fir.if %[[VAL_7]] {
33+
! CHECK: fir.store %[[VAL_5]] to %[[VAL_1]]#1 : !fir.ref<i32>
34+
! CHECK: }
35+
end subroutine
36+
37+
subroutine test_chdir_function_status_i4()
38+
implicit none
39+
integer(4) :: stat
40+
! CHECK-LABEL: func.func @_QPtest_chdir_function_status_i4() {
41+
42+
stat = chdir("..")
43+
! CHECK: %[[VAL_0:.*]] = fir.alloca i32 {bindc_name = "stat", uniq_name = "_QFtest_chdir_function_status_i4Estat"}
44+
! CHECK: %[[VAL_1:.*]]:2 = hlfir.declare %[[VAL_0]] {uniq_name = "_QFtest_chdir_function_status_i4Estat"} : (!fir.ref<i32>) ->
45+
! (!fir.ref<i32>, !fir.ref<i32>)
46+
! CHECK: %[[VAL_2:.*]] = fir.address_of(@_QQclX2E2E) : !fir.ref<!fir.char<1,2>>
47+
! CHECK: %[[C_2:.*]] = arith.constant 2 : index
48+
! CHECK: %[[VAL_3:.*]]:2 = hlfir.declare %[[VAL_2]] typeparams %[[C_2]] {fortran_attrs = #fir.var_attrs<parameter>, uniq_name = {{.*}} : (!fir.ref<!fir.char<1,2>>, index) -> (!fir.ref<!fir.char<1,2>>, !fir.ref<!fir.char<1,2>>)
49+
! CHECK: %[[VAL_4:.*]] = fir.convert %[[VAL_3]]#1 : (!fir.ref<!fir.char<1,2>>) -> !fir.ref<i8>
50+
! CHECK: %[[VAL_5:.*]] = fir.call @_FortranAChdir(%[[VAL_4]]) fastmath<contract> : (!fir.ref<i8>) -> i32
51+
! CHECK: hlfir.assign %[[VAL_5]] to %[[VAL_1]]#0 : i32, !fir.ref<i32>
52+
end subroutine
53+
54+
subroutine test_chdir_subroutine_status_i8()
55+
implicit none
56+
integer(8) :: stat
57+
! CHECK-LABEL: func.func @_QPtest_chdir_subroutine_status_i8() {
58+
59+
call chdir("..", STATUS=stat)
60+
! CHECK: %[[VAL_0:.*]] = fir.alloca i64 {bindc_name = "stat", uniq_name = "_QFtest_chdir_subroutine_status_i8Estat"}
61+
! CHECK: %[[VAL_1:.*]]:2 = hlfir.declare %[[VAL_0]] {uniq_name = "_QFtest_chdir_subroutine_status_i8Estat"} : (!fir.ref<i64>) ->
62+
! (!fir.ref<i64>, !fir.ref<i64>)
63+
! CHECK: %[[VAL_2:.*]] = fir.address_of(@_QQclX2E2E) : !fir.ref<!fir.char<1,2>>
64+
! CHECK: %[[C_2:.*]] = arith.constant 2 : index
65+
! CHECK: %[[VAL_3:.*]]:2 = hlfir.declare %[[VAL_2]] typeparams %[[C_2]] {fortran_attrs = #fir.var_attrs<parameter>, uniq_name = {{.*}} : (!fir.ref<!fir.char<1,2>>, index) -> (!fir.ref<!fir.char<1,2>>, !fir.ref<!fir.char<1,2>>)
66+
! CHECK: %[[VAL_4:.*]] = fir.convert %{{.*}} : (!fir.ref<!fir.char<1,2>>) -> !fir.ref<i8>
67+
! CHECK: %[[VAL_5:.*]] = fir.call @_FortranAChdir(%[[VAL_4]]) fastmath<contract> : (!fir.ref<i8>) -> i32
68+
! CHECK: %[[VAL_6:.*]] = fir.convert %[[VAL_1]]#1 : (!fir.ref<i64>) -> i64
69+
! CHECK: %[[C_0_I64:.*]] = arith.constant 0 : i64
70+
! CHECK: %[[VAL_7:.*]] = arith.cmpi ne, %[[VAL_6]], %[[C_0_I64]] : i64
71+
! CHECK: fir.if %[[VAL_7]] {
72+
! CHECK: %[[VAL_8:.*]] = fir.convert %[[VAL_5]] : (i32) -> i64
73+
! CHECK: fir.store %[[VAL_8]] to %[[VAL_1]]#1 : !fir.ref<i64>
74+
! CHECK: }
75+
end subroutine
76+
77+
subroutine test_chdir_function_status_i8()
78+
implicit none
79+
integer(8) :: stat
80+
! CHECK-LABEL: func.func @_QPtest_chdir_function_status_i8() {
81+
82+
stat = chdir("..")
83+
! CHECK: %[[VAL_0:.*]] = fir.alloca i64 {bindc_name = "stat", uniq_name = "_QFtest_chdir_function_status_i8Estat"}
84+
! CHECK: %[[VAL_1:.*]]:2 = hlfir.declare %[[VAL_0]] {uniq_name = "_QFtest_chdir_function_status_i8Estat"} : (!fir.ref<i64>) ->
85+
! (!fir.ref<i64>, !fir.ref<i64>)
86+
! CHECK: %[[VAL_2:.*]] = fir.address_of(@_QQclX2E2E) : !fir.ref<!fir.char<1,2>>
87+
! CHECK: %[[C_2:.*]] = arith.constant 2 : index
88+
! CHECK: %[[VAL_3:.*]]:2 = hlfir.declare %[[VAL_2]] typeparams %[[C_2]] {fortran_attrs = #fir.var_attrs<parameter>, uniq_name = {{.*}} : (!fir.ref<!fir.char<1,2>>, index) -> (!fir.ref<!fir.char<1,2>>, !fir.ref<!fir.char<1,2>>)
89+
! CHECK: %[[VAL_4:.*]] = fir.convert %[[VAL_3]]#1 : (!fir.ref<!fir.char<1,2>>) -> !fir.ref<i8>
90+
! CHECK: %[[VAL_5:.*]] = fir.call @_FortranAChdir(%[[VAL_4]]) fastmath<contract> : (!fir.ref<i8>) -> i32
91+
! CHECK: %[[VAL_6:.*]] = fir.convert %[[VAL_5]] : (i32) -> i64
92+
! CHECK: hlfir.assign %[[VAL_6]] to %[[VAL_1]]#0 : i64, !fir.ref<i64>
93+
end subroutine
94+

0 commit comments

Comments
 (0)