Skip to content

Commit a5a29a2

Browse files
authored
[Flang] Implement RENAME intrinsic (code-gen + runtime entry point) (llvm#98359)
This PR implements the RENAME intrinsic, which is a GFortran extension (see https://gcc.gnu.org/onlinedocs/gfortran/intrinsic-procedures/rename.html).
1 parent 587308c commit a5a29a2

File tree

10 files changed

+206
-4
lines changed

10 files changed

+206
-4
lines changed

flang/docs/Intrinsics.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1001,3 +1001,49 @@ PROGRAM example_getcwd
10011001
PRINT *, status
10021002
END PROGRAM
10031003
```
1004+
1005+
### Non-standard Intrinsics: RENAME
1006+
`RENAME(OLD, NEW[, STATUS])` renames/moves a file on the filesystem.
1007+
1008+
This intrinsic is provided in both subroutine and function form; however, only one form can be used in any given program unit.
1009+
1010+
#### Usage and Info
1011+
1012+
- **Standard:** GNU extension
1013+
- **Class:** Subroutine, function
1014+
- **Syntax:** `CALL RENAME(SRC, DST[, STATUS])`
1015+
- **Arguments:**
1016+
- **Return value** status code (0: success, non-zero for errors)
1017+
1018+
| Argument | Description |
1019+
|----------|-----------------------------------|
1020+
| `SRC` | Source path |
1021+
| `DST` | Destination path |
1022+
| `STATUS` | Status code (for subroutine form) |
1023+
1024+
The status code returned by both the subroutine and function form corresponds to the value of `errno` if the invocation of `rename(2)` was not successful.
1025+
1026+
#### Example
1027+
1028+
Function form:
1029+
```
1030+
program rename_func
1031+
implicit none
1032+
integer :: status
1033+
status = rename('src', 'dst')
1034+
print *, 'status:', status
1035+
status = rename('dst', 'src')
1036+
print *, 'status:', status
1037+
end program rename_func
1038+
```
1039+
1040+
Subroutine form:
1041+
```
1042+
program rename_proc
1043+
implicit none
1044+
integer :: status
1045+
call rename('src', 'dst', status)
1046+
print *, 'status:', status
1047+
call rename('dst', 'src')
1048+
end program rename_proc
1049+
```

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,8 @@ struct IntrinsicLibrary {
347347
fir::ExtendedValue genReduce(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
348348
fir::ExtendedValue genReduceDim(mlir::Type,
349349
llvm::ArrayRef<fir::ExtendedValue>);
350+
fir::ExtendedValue genRename(std::optional<mlir::Type>,
351+
mlir::ArrayRef<fir::ExtendedValue>);
350352
fir::ExtendedValue genRepeat(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
351353
fir::ExtendedValue genReshape(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
352354
mlir::Value genRRSpacing(mlir::Type resultType,

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ void genRandomNumber(fir::FirOpBuilder &, mlir::Location, mlir::Value harvest);
5353
void genRandomSeed(fir::FirOpBuilder &, mlir::Location, mlir::Value size,
5454
mlir::Value put, mlir::Value get);
5555

56+
/// generate rename runtime call
57+
void genRename(fir::FirOpBuilder &builder, mlir::Location loc,
58+
mlir::Value path1, mlir::Value path2, mlir::Value status);
59+
5660
/// generate runtime call to transfer intrinsic with no size argument
5761
void genTransfer(fir::FirOpBuilder &builder, mlir::Location loc,
5862
mlir::Value resultBox, mlir::Value sourceBox,

flang/include/flang/Runtime/misc-intrinsic.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ namespace Fortran::runtime {
1919
class Descriptor;
2020

2121
extern "C" {
22+
void RTDECL(Rename)(const Descriptor &path1, const Descriptor &path2,
23+
const Descriptor *status, const char *sourceFile, int line);
2224
void RTDECL(Transfer)(Descriptor &result, const Descriptor &source,
2325
const Descriptor &mold, const char *sourceFile, int line);
2426
void RTDECL(TransferSize)(Descriptor &result, const Descriptor &source,

flang/lib/Evaluate/intrinsics.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -795,6 +795,10 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
795795
{"identity", SameType, Rank::scalar, Optionality::optional},
796796
{"ordered", AnyLogical, Rank::scalar, Optionality::optional}},
797797
SameType, Rank::scalar, IntrinsicClass::transformationalFunction},
798+
{"rename",
799+
{{"path1", DefaultChar, Rank::scalar},
800+
{"path2", DefaultChar, Rank::scalar}},
801+
DefaultInt, Rank::scalar},
798802
{"repeat",
799803
{{"string", SameCharNoLen, Rank::scalar},
800804
{"ncopies", AnyInt, Rank::scalar}},
@@ -1464,6 +1468,12 @@ static const IntrinsicInterface intrinsicSubroutine[]{
14641468
{"get", DefaultInt, Rank::vector, Optionality::optional,
14651469
common::Intent::Out}},
14661470
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
1471+
{"rename",
1472+
{{"path1", DefaultChar, Rank::scalar},
1473+
{"path2", DefaultChar, Rank::scalar},
1474+
{"status", DefaultInt, Rank::scalar, Optionality::optional,
1475+
common::Intent::Out}},
1476+
{}, Rank::scalar, IntrinsicClass::impureSubroutine},
14671477
{"system",
14681478
{{"command", DefaultChar, Rank::scalar},
14691479
{"exitstat", DefaultInt, Rank::scalar, Optionality::optional,
@@ -2612,7 +2622,8 @@ bool IntrinsicProcTable::Implementation::IsDualIntrinsic(
26122622
const std::string &name) const {
26132623
// Collection for some intrinsics with function and subroutine form,
26142624
// in order to pass the semantic check.
2615-
static const std::string dualIntrinsic[]{{"etime"}, {"getcwd"}};
2625+
static const std::string dualIntrinsic[]{
2626+
{"etime"s}, {"getcwd"s}, {"rename"s}};
26162627

26172628
return std::find_if(std::begin(dualIntrinsic), std::end(dualIntrinsic),
26182629
[&name](const std::string &dualName) {

flang/lib/Optimizer/Builder/IntrinsicCall.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,12 @@ static constexpr IntrinsicHandler handlers[]{
550550
{"identity", asAddr, handleDynamicOptional},
551551
{"ordered", asValue, handleDynamicOptional}}},
552552
/*isElemental=*/false},
553+
{"rename",
554+
&I::genRename,
555+
{{{"path1", asBox},
556+
{"path2", asBox},
557+
{"status", asBox, handleDynamicOptional}}},
558+
/*isElemental=*/false},
553559
{"repeat",
554560
&I::genRepeat,
555561
{{{"string", asAddr}, {"ncopies", asValue}}},
@@ -5917,6 +5923,37 @@ IntrinsicLibrary::genReduce(mlir::Type resultType,
59175923
return readAndAddCleanUp(resultMutableBox, resultType, "REDUCE");
59185924
}
59195925

5926+
// RENAME
5927+
fir::ExtendedValue
5928+
IntrinsicLibrary::genRename(std::optional<mlir::Type> resultType,
5929+
mlir::ArrayRef<fir::ExtendedValue> args) {
5930+
assert((args.size() == 3 && !resultType.has_value()) ||
5931+
(args.size() == 2 && resultType.has_value()));
5932+
5933+
mlir::Value path1 = fir::getBase(args[0]);
5934+
mlir::Value path2 = fir::getBase(args[1]);
5935+
if (!path1 || !path2)
5936+
fir::emitFatalError(loc, "Expected at least two dummy arguments");
5937+
5938+
if (resultType.has_value()) {
5939+
// code-gen for the function form of RENAME
5940+
auto statusAddr = builder.createTemporary(loc, *resultType);
5941+
auto statusBox = builder.createBox(loc, statusAddr);
5942+
fir::runtime::genRename(builder, loc, path1, path2, statusBox);
5943+
return builder.create<fir::LoadOp>(loc, statusAddr);
5944+
} else {
5945+
// code-gen for the procedure form of RENAME
5946+
mlir::Type boxNoneTy = fir::BoxType::get(builder.getNoneType());
5947+
auto status = args[2];
5948+
mlir::Value statusBox =
5949+
isStaticallyPresent(status)
5950+
? fir::getBase(status)
5951+
: builder.create<fir::AbsentOp>(loc, boxNoneTy).getResult();
5952+
fir::runtime::genRename(builder, loc, path1, path2, statusBox);
5953+
return {};
5954+
}
5955+
}
5956+
59205957
// REPEAT
59215958
fir::ExtendedValue
59225959
IntrinsicLibrary::genRepeat(mlir::Type resultType,

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,24 @@ void fir::runtime::genRandomSeed(fir::FirOpBuilder &builder, mlir::Location loc,
199199
builder.create<fir::CallOp>(loc, func, args);
200200
}
201201

202+
/// generate rename runtime call
203+
void fir::runtime::genRename(fir::FirOpBuilder &builder, mlir::Location loc,
204+
mlir::Value path1, mlir::Value path2,
205+
mlir::Value status) {
206+
auto runtimeFunc =
207+
fir::runtime::getRuntimeFunc<mkRTKey(Rename)>(loc, builder);
208+
mlir::FunctionType runtimeFuncTy = runtimeFunc.getFunctionType();
209+
210+
mlir::Value sourceFile = fir::factory::locationToFilename(builder, loc);
211+
mlir::Value sourceLine =
212+
fir::factory::locationToLineNo(builder, loc, runtimeFuncTy.getInput(4));
213+
214+
llvm::SmallVector<mlir::Value> args =
215+
fir::runtime::createArguments(builder, loc, runtimeFuncTy, path1, path2,
216+
status, sourceFile, sourceLine);
217+
builder.create<fir::CallOp>(loc, runtimeFunc, args);
218+
}
219+
202220
/// generate runtime call to transfer intrinsic with no size argument
203221
void fir::runtime::genTransfer(fir::FirOpBuilder &builder, mlir::Location loc,
204222
mlir::Value resultBox, mlir::Value sourceBox,

flang/runtime/misc-intrinsic.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "flang/Common/optional.h"
1313
#include "flang/Runtime/descriptor.h"
1414
#include <algorithm>
15+
#include <cstdio>
1516
#include <cstring>
1617

1718
namespace Fortran::runtime {
@@ -55,6 +56,36 @@ static RT_API_ATTRS void TransferImpl(Descriptor &result,
5556
extern "C" {
5657
RT_EXT_API_GROUP_BEGIN
5758

59+
void RTDECL(Rename)(const Descriptor &path1, const Descriptor &path2,
60+
const Descriptor *status, const char *sourceFile, int line) {
61+
Terminator terminator{sourceFile, line};
62+
63+
char *pathSrc{EnsureNullTerminated(
64+
path1.OffsetElement(), path1.ElementBytes(), terminator)};
65+
char *pathDst{EnsureNullTerminated(
66+
path2.OffsetElement(), path2.ElementBytes(), terminator)};
67+
68+
// We simply call rename(2) from POSIX
69+
int result{rename(pathSrc, pathDst)};
70+
if (status) {
71+
// When an error has happened,
72+
int errorCode{0}; // Assume success
73+
if (result != 0) {
74+
// The rename operation has failed, so return the error code as status.
75+
errorCode = errno;
76+
}
77+
StoreIntToDescriptor(status, errorCode, terminator);
78+
}
79+
80+
// Deallocate memory if EnsureNullTerminated dynamically allocated memory
81+
if (pathSrc != path1.OffsetElement()) {
82+
FreeMemory(pathSrc);
83+
}
84+
if (pathDst != path2.OffsetElement()) {
85+
FreeMemory(pathDst);
86+
}
87+
}
88+
5889
void RTDEF(Transfer)(Descriptor &result, const Descriptor &source,
5990
const Descriptor &mold, const char *sourceFile, int line) {
6091
Fortran::common::optional<std::int64_t> elements;
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
!RUN: %flang_fc1 -emit-hlfir %s -o - | FileCheck %s
2+
3+
!CHECK-LABEL: func.func @_QPtest_rename
4+
!CHECK-SAME: %[[dummySrc:.*]]: !fir.boxchar<1> {fir.bindc_name = "src"},
5+
!CHECK-SAME: %[[dummyDst:.*]]: !fir.boxchar<1> {fir.bindc_name = "dst"}) {
6+
subroutine test_rename(src, dst)
7+
implicit none
8+
character(*) :: src, dst
9+
10+
call rename(src, dst)
11+
!CHECK: %[[dstUnbox:.*]]:2 = fir.unboxchar %[[dummyDst]] : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
12+
!CHECK-NEXT: %[[dstDecl:.*]]:2 = hlfir.declare %[[dstUnbox]]#0 typeparams %[[dstUnbox]]#1 dummy_scope %0 {uniq_name = "_QFtest_renameEdst"} : (!fir.ref<!fir.char<1,?>>, index, !fir.dscope) -> (!fir.boxchar<1>, !fir.ref<!fir.char<1,?>>)
13+
!CHECK-NEXT: %[[srcUnbox:.*]]:2 = fir.unboxchar %[[dummySrc]] : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
14+
!CHECK-NEXT: %[[srcDecl:.*]]:2 = hlfir.declare %3#0 typeparams %[[srcUnbox]]#1 dummy_scope %0 {uniq_name = "_QFtest_renameEsrc"} : (!fir.ref<!fir.char<1,?>>, index, !fir.dscope) -> (!fir.boxchar<1>, !fir.ref<!fir.char<1,?>>)
15+
!CHECK-NEXT: %[[srcBox:.*]] = fir.embox %[[srcDecl]]#1 typeparams %[[srcUnbox]]#1 : (!fir.ref<!fir.char<1,?>>, index) -> !fir.box<!fir.char<1,?>>
16+
!CHECK-NEXT: %[[dstBox:.*]] = fir.embox %[[dstDecl]]#1 typeparams %[[dstUnbox]]#1 : (!fir.ref<!fir.char<1,?>>, index) -> !fir.box<!fir.char<1,?>>
17+
!CHECK-NEXT: %[[statusBox:.*]] = fir.absent !fir.box<none>
18+
!CHECK-NEXT: %[[sourceFile:.*]] = fir.address_of(@[[someString:.*]]) : !fir.ref<!fir.char<1,[[len:.*]]>>
19+
!CHECK-NEXT: %[[c10_i32:.*]] = arith.constant [[line:.*]] : i32
20+
!CHECK-NEXT: %[[src:.*]] = fir.convert %[[srcBox]] : (!fir.box<!fir.char<1,?>>) -> !fir.box<none>
21+
!CHECK-NEXT: %[[dst:.*]] = fir.convert %[[dstBox]] : (!fir.box<!fir.char<1,?>>) -> !fir.box<none>
22+
!CHECK-NEXT: %[[loc:.*]] = fir.convert %[[sourceFileConv:.*]]: (!fir.ref<!fir.char<1,[[len:.*]]>>) -> !fir.ref<i8>
23+
!CHECK-NEXT: %[[result:.*]] = fir.call @_FortranARename(%[[src]], %[[dst]], %[[statusBox]], %[[loc]], %[[c10_i32]]) fastmath<contract> : (!fir.box<none>, !fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> none
24+
end subroutine test_rename
25+
26+
!CHECK-LABEL: func.func @_QPtest_rename_status
27+
!CHECK-SAME: %[[dummySrc:.*]]: !fir.boxchar<1> {fir.bindc_name = "src"},
28+
!CHECK-SAME: %[[dummyDst:.*]]: !fir.boxchar<1> {fir.bindc_name = "dst"}) {
29+
subroutine test_rename_status(src, dst)
30+
implicit none
31+
character(*) :: src, dst
32+
integer :: status
33+
34+
call rename(src, dst, status)
35+
!CHECK: %[[dstUnbox:.*]]:2 = fir.unboxchar %[[dummyDst]] : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
36+
!CHECK-NEXT: %[[dstDecl:.*]]:2 = hlfir.declare %[[dstUnbox]]#0 typeparams %[[dstUnbox]]#1 dummy_scope %0 {uniq_name = "_QFtest_rename_statusEdst"} : (!fir.ref<!fir.char<1,?>>, index, !fir.dscope) -> (!fir.boxchar<1>, !fir.ref<!fir.char<1,?>>)
37+
!CHECK-NEXT: %[[srcUnbox:.*]]:2 = fir.unboxchar %[[dummySrc]] : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
38+
!CHECK-NEXT: %[[srcDecl:.*]]:2 = hlfir.declare %3#0 typeparams %[[srcUnbox]]#1 dummy_scope %0 {uniq_name = "_QFtest_rename_statusEsrc"} : (!fir.ref<!fir.char<1,?>>, index, !fir.dscope) -> (!fir.boxchar<1>, !fir.ref<!fir.char<1,?>>)
39+
!CHECK-NEXT: %[[statusAlloc:.*]] = fir.alloca i32 {bindc_name = "status", uniq_name = "_QFtest_rename_statusEstatus"}
40+
!CHECK-NEXT: %[[statusDecl:.*]]:2 = hlfir.declare %[[statusAlloc]] {uniq_name = "_QFtest_rename_statusEstatus"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
41+
!CHECK-NEXT: %[[srcBox:.*]] = fir.embox %[[srcDecl]]#1 typeparams %[[srcUnbox]]#1 : (!fir.ref<!fir.char<1,?>>, index) -> !fir.box<!fir.char<1,?>>
42+
!CHECK-NEXT: %[[dstBox:.*]] = fir.embox %[[dstDecl]]#1 typeparams %[[dstUnbox]]#1 : (!fir.ref<!fir.char<1,?>>, index) -> !fir.box<!fir.char<1,?>>
43+
!CHECK-NEXT: %[[statusBox:.*]] = fir.embox %[[statusDecl]]#1 : (!fir.ref<i32>) -> !fir.box<i32>
44+
!CHECK-NEXT: %[[sourceFile:.*]] = fir.address_of(@[[someString:.*]]) : !fir.ref<!fir.char<1,[[len:.*]]>>
45+
!CHECK-NEXT: %[[c10_i32:.*]] = arith.constant [[line:.*]] : i32
46+
!CHECK-NEXT: %[[src:.*]] = fir.convert %[[srcBox]] : (!fir.box<!fir.char<1,?>>) -> !fir.box<none>
47+
!CHECK-NEXT: %[[dst:.*]] = fir.convert %[[dstBox]] : (!fir.box<!fir.char<1,?>>) -> !fir.box<none>
48+
!CHECK-NEXT: %[[status:.*]] = fir.convert %[[statusBox]] : (!fir.box<i32>) -> !fir.box<none>
49+
!CHECK-NEXT: %[[loc:.*]] = fir.convert %[[sourceFileConv:.*]]: (!fir.ref<!fir.char<1,[[len:.*]]>>) -> !fir.ref<i8>
50+
!CHECK-NEXT: %[[result:.*]] = fir.call @_FortranARename(%[[src]], %[[dst]], %[[status]], %[[loc]], %[[c10_i32]]) fastmath<contract> : (!fir.box<none>, !fir.box<none>, !fir.box<none>, !fir.ref<i8>, i32) -> none
51+
end subroutine test_rename_status

flang/test/Lower/namelist.f90

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ program p
7171
! CHECK: %[[V_70:[0-9]+]] = fir.call @_FortranAioEndIoStatement(%[[V_58]]) fastmath<contract> : (!fir.ref<i8>) -> i32
7272
write(*, nnn)
7373

74-
call rename
74+
call rename_sub
7575
end
7676

7777
! CHECK-LABEL: c.func @_QPsss
@@ -128,8 +128,8 @@ module mmm
128128
namelist /aaa/ rrr
129129
end
130130

131-
! CHECK-LABEL: c.func @_QPrename
132-
subroutine rename
131+
! CHECK-LABEL: c.func @_QPrename_sub
132+
subroutine rename_sub
133133
use mmm, bbb => aaa
134134
rrr = 3.
135135
! CHECK: %[[V_4:[0-9]+]] = fir.call @_FortranAioBeginExternalListOutput

0 commit comments

Comments
 (0)