Skip to content

Commit 85fdab3

Browse files
authored
[flang][intrinsic] add nonstandard intrinsic unlink (#134162)
This PR adds the intrinsic `unlink` to flang. ## Test plan - Added two codegen unit tests and ensured flang-check continues to pass. - Manually compiled and ran the example from the documentation.
1 parent fb6f60d commit 85fdab3

File tree

10 files changed

+213
-3
lines changed

10 files changed

+213
-3
lines changed

flang-rt/lib/runtime/command.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@
1212
#include "flang-rt/runtime/stat.h"
1313
#include "flang-rt/runtime/terminator.h"
1414
#include "flang-rt/runtime/tools.h"
15+
#include <cerrno>
1516
#include <cstdlib>
1617
#include <limits>
1718

1819
#ifdef _WIN32
1920
#include "flang/Common/windows-include.h"
2021
#include <direct.h>
2122
#define getcwd _getcwd
23+
#define unlink _unlink
2224
#define PATH_MAX MAX_PATH
2325

2426
#ifdef _MSC_VER
@@ -27,7 +29,7 @@
2729
inline pid_t getpid() { return GetCurrentProcessId(); }
2830
#endif
2931
#else
30-
#include <unistd.h> //getpid()
32+
#include <unistd.h> //getpid() unlink()
3133

3234
#ifndef PATH_MAX
3335
#define PATH_MAX 4096
@@ -307,4 +309,19 @@ std::int32_t RTNAME(Hostnm)(
307309
return status;
308310
}
309311

312+
std::int32_t RTNAME(Unlink)(
313+
const char *str, size_t strLength, const char *sourceFile, int line) {
314+
Terminator terminator{sourceFile, line};
315+
316+
auto pathLength = TrimTrailingSpaces(str, strLength);
317+
auto path = SaveDefaultCharacter(str, pathLength, terminator);
318+
319+
std::int32_t status{0};
320+
321+
if (unlink(path.get()) != 0) {
322+
status = errno;
323+
}
324+
325+
return status;
326+
}
310327
} // namespace Fortran::runtime

flang/docs/Intrinsics.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,6 +1091,48 @@ end program rename_proc
10911091
This intrinsic is an alias for `CPU_TIME`: supporting both a subroutine and a
10921092
function form.
10931093

1094+
### Non-Standard Intrinsics: UNLINK
1095+
1096+
#### Description
1097+
`UNLINK(PATH [, STATUS])` deletes a link to a file.
1098+
1099+
This intrinsic is provided in both subroutine and function forms; however, only
1100+
one form can be used in any given program unit.
1101+
1102+
| ARGUMENT | INTENT | TYPE | KIND | Description |
1103+
|----------|--------|-------------|---------|---------------------------------|
1104+
| `PATH` | `IN` | `CHARACTER` | default | The path of the file to unlink. |
1105+
| `STATUS` | `OUT` | `INTEGER` | default | Optional. Returns 0 on success, C's `errno` on failure. |
1106+
1107+
#### Usage and Info
1108+
1109+
- **Standard:** GNU extension
1110+
- **Class:** Subroutine, function
1111+
- **Syntax:** `CALL UNLINK(PATH [, STATUS])`, `STATUS = UNLINK(PATH)`
1112+
1113+
#### Example
1114+
The following example just prints "hello.txt doesn't exist".
1115+
```Fortran
1116+
SUBROUTINE try_unlink_hello_again()
1117+
INTEGER :: status
1118+
CALL UNLINK("hello.txt", status)
1119+
IF (status .NE. 0) PRINT *, "hello.txt doesn't exist"
1120+
END SUBROUTINE
1121+
1122+
PROGRAM example_unlink
1123+
INTEGER :: hello
1124+
! Create ./hello.txt
1125+
OPEN(newunit=hello, file="hello.txt")
1126+
WRITE (hello, *), "Hello!"
1127+
CLOSE(hello)
1128+
1129+
! Delete ./hello.txt
1130+
IF (UNLINK("hello.txt") .NE. 0) PRINT *, "didn't create a file"
1131+
1132+
CALL try_unlink_hello_again()
1133+
END PROGRAM
1134+
```
1135+
10941136
### Non-standard Intrinsics: LNBLNK
10951137
This intrinsic is an alias for `LEN_TRIM`, without the optional KIND argument.
10961138

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,8 @@ struct IntrinsicLibrary {
439439
void genThreadFenceSystem(llvm::ArrayRef<fir::ExtendedValue>);
440440
fir::ExtendedValue genTrim(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
441441
fir::ExtendedValue genUbound(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
442+
fir::ExtendedValue genUnlink(std::optional<mlir::Type> resultType,
443+
llvm::ArrayRef<fir::ExtendedValue> args);
442444
fir::ExtendedValue genUnpack(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
443445
fir::ExtendedValue genVerify(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
444446
mlir::Value genVoteAllSync(mlir::Type, llvm::ArrayRef<mlir::Value>);

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,5 +68,10 @@ mlir::Value genHostnm(fir::FirOpBuilder &builder, mlir::Location loc,
6868
void genPerror(fir::FirOpBuilder &builder, mlir::Location loc,
6969
mlir::Value string);
7070

71+
/// Generate a call to the Unlink runtime function which implements
72+
/// the UNLINK intrinsic.
73+
mlir::Value genUnlink(fir::FirOpBuilder &builder, mlir::Location loc,
74+
mlir::Value path, mlir::Value pathLength);
75+
7176
} // namespace fir::runtime
7277
#endif // FORTRAN_OPTIMIZER_BUILDER_RUNTIME_COMMAND_H

flang/include/flang/Runtime/command.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,12 @@ std::int32_t RTNAME(GetCwd)(
6363
// Calls hostnm()
6464
std::int32_t RTNAME(Hostnm)(
6565
const Descriptor &res, const char *sourceFile, int line);
66-
}
66+
67+
// Calls unlink()
68+
std::int32_t RTNAME(Unlink)(
69+
const char *path, size_t pathLength, const char *sourceFile, int line);
70+
71+
} // extern "C"
6772
} // namespace Fortran::runtime
6873

6974
#endif // FORTRAN_RUNTIME_COMMAND_H_

flang/lib/Evaluate/intrinsics.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1010,6 +1010,8 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
10101010
KINDUnsigned},
10111011
{"umaskl", {{"i", AnyInt}, DefaultingKIND}, KINDUnsigned},
10121012
{"umaskr", {{"i", AnyInt}, DefaultingKIND}, KINDUnsigned},
1013+
{"unlink", {{"path", DefaultChar, Rank::scalar}}, DefaultInt, Rank::scalar,
1014+
IntrinsicClass::transformationalFunction},
10131015
{"unpack",
10141016
{{"vector", SameType, Rank::vector}, {"mask", AnyLogical, Rank::array},
10151017
{"field", SameType, Rank::conformable}},
@@ -1319,6 +1321,8 @@ static const SpecificIntrinsicInterface specificIntrinsicFunction[]{
13191321
"abs"},
13201322
};
13211323

1324+
// Must be sorted by name. The rank of the return value is ignored since
1325+
// subroutines are do not have a return value.
13221326
static const IntrinsicInterface intrinsicSubroutine[]{
13231327
{"abort", {}, {}, Rank::elemental, IntrinsicClass::impureSubroutine},
13241328
{"atomic_add",
@@ -1631,6 +1635,12 @@ static const IntrinsicInterface intrinsicSubroutine[]{
16311635
{{"seconds", AnyInt, Rank::scalar, Optionality::required,
16321636
common::Intent::In}},
16331637
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
1638+
{"unlink",
1639+
{{"path", DefaultChar, Rank::scalar, Optionality::required,
1640+
common::Intent::In},
1641+
{"status", DefaultInt, Rank::scalar, Optionality::optional,
1642+
common::Intent::Out}},
1643+
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
16341644
};
16351645

16361646
// Finds a built-in derived type and returns it as a DynamicType.
@@ -2800,7 +2810,7 @@ bool IntrinsicProcTable::Implementation::IsDualIntrinsic(
28002810
// Collection for some intrinsics with function and subroutine form,
28012811
// in order to pass the semantic check.
28022812
static const std::string dualIntrinsic[]{{"chdir"s}, {"etime"s}, {"getcwd"s},
2803-
{"hostnm"s}, {"rename"s}, {"second"s}, {"system"s}};
2813+
{"hostnm"s}, {"rename"s}, {"second"s}, {"system"s}, {"unlink"s}};
28042814

28052815
return llvm::is_contained(dualIntrinsic, name);
28062816
}

flang/lib/Optimizer/Builder/IntrinsicCall.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -937,6 +937,10 @@ static constexpr IntrinsicHandler handlers[]{
937937
/*isElemental=*/false},
938938
{"umaskl", &I::genMask<mlir::arith::ShLIOp>},
939939
{"umaskr", &I::genMask<mlir::arith::ShRUIOp>},
940+
{"unlink",
941+
&I::genUnlink,
942+
{{{"path", asAddr}, {"status", asAddr, handleDynamicOptional}}},
943+
/*isElemental=*/false},
940944
{"unpack",
941945
&I::genUnpack,
942946
{{{"vector", asBox}, {"mask", asBox}, {"field", asBox}}},
@@ -8513,6 +8517,39 @@ static mlir::Value createExtremumCompare(mlir::Location loc,
85138517
return result;
85148518
}
85158519

8520+
// UNLINK
8521+
fir::ExtendedValue
8522+
IntrinsicLibrary::genUnlink(std::optional<mlir::Type> resultType,
8523+
llvm::ArrayRef<fir::ExtendedValue> args) {
8524+
assert((resultType.has_value() && args.size() == 1) ||
8525+
(!resultType.has_value() && args.size() >= 1 && args.size() <= 2));
8526+
8527+
mlir::Value path = fir::getBase(args[0]);
8528+
mlir::Value pathLength = fir::getLen(args[0]);
8529+
mlir::Value statusValue =
8530+
fir::runtime::genUnlink(builder, loc, path, pathLength);
8531+
8532+
if (resultType.has_value()) {
8533+
// Function form, return status.
8534+
return builder.createConvert(loc, *resultType, statusValue);
8535+
}
8536+
8537+
// Subroutine form, store status and return none.
8538+
const fir::ExtendedValue &status = args[1];
8539+
if (!isStaticallyAbsent(status)) {
8540+
mlir::Value statusAddr = fir::getBase(status);
8541+
mlir::Value statusIsPresentAtRuntime =
8542+
builder.genIsNotNullAddr(loc, statusAddr);
8543+
builder.genIfThen(loc, statusIsPresentAtRuntime)
8544+
.genThen([&]() {
8545+
builder.createStoreWithConvert(loc, statusValue, statusAddr);
8546+
})
8547+
.end();
8548+
}
8549+
8550+
return {};
8551+
}
8552+
85168553
// UNPACK
85178554
fir::ExtendedValue
85188555
IntrinsicLibrary::genUnpack(mlir::Type resultType,

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,3 +125,17 @@ void fir::runtime::genPerror(fir::FirOpBuilder &builder, mlir::Location loc,
125125
fir::runtime::createArguments(builder, loc, runtimeFuncTy, string);
126126
builder.create<fir::CallOp>(loc, runtimeFunc, args);
127127
}
128+
129+
mlir::Value fir::runtime::genUnlink(fir::FirOpBuilder &builder,
130+
mlir::Location loc, mlir::Value path,
131+
mlir::Value pathLength) {
132+
mlir::func::FuncOp func =
133+
fir::runtime::getRuntimeFunc<mkRTKey(Unlink)>(loc, builder);
134+
auto runtimeFuncTy = func.getFunctionType();
135+
mlir::Value sourceFile = fir::factory::locationToFilename(builder, loc);
136+
mlir::Value sourceLine =
137+
fir::factory::locationToLineNo(builder, loc, runtimeFuncTy.getInput(1));
138+
llvm::SmallVector<mlir::Value> args = fir::runtime::createArguments(
139+
builder, loc, runtimeFuncTy, path, pathLength, sourceFile, sourceLine);
140+
return builder.create<fir::CallOp>(loc, func, args).getResult(0);
141+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
!RUN: %flang_fc1 -emit-hlfir %s -o - | FileCheck %s
2+
3+
!CHECK-LABEL: func.func @_QPunlink_test
4+
!CHECK-SAME: %[[dummyPath:.*]]: !fir.boxchar<1> {fir.bindc_name = "path"}) -> i32 {
5+
integer function unlink_test(path)
6+
CHARACTER(len=255) :: path
7+
8+
!CHECK-DAG: %[[func_result:.*]] = fir.alloca i32 {bindc_name = "unlink_test", uniq_name = "_QFunlink_testEunlink_test"}
9+
!CHECK-DAG: %[[func_result_decl:.*]]:{{.*}} = hlfir.declare %[[func_result]] {uniq_name = "_QFunlink_testEunlink_test"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
10+
!CHECK-DAG: %[[src_path_addr:.*]] = fir.address_of(@_{{.*}}) : !fir.ref<!fir.char<1,{{.*}}>
11+
!CHECK-DAG: %[[line_value:.*]] = arith.constant {{.*}} : i64
12+
!CHECK-DAG: %[[path:.*]] = fir.convert {{.*}} (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
13+
!CHECK-DAG: %[[path_len:.*]] = fir.convert {{.*}} : (index) -> i64
14+
!CHECK-DAG: %[[src_path:.*]] = fir.convert %[[src_path_addr]] : (!fir.ref<!fir.char<1,{{.*}}>) -> !fir.ref<i8>
15+
!CHECK-DAG: %[[line:.*]] = fir.convert %[[line_value]] : (i64) -> i32
16+
!CHECK: %[[unlink_result:.*]] = fir.call @_FortranAUnlink(%[[path]], %[[path_len]], %[[src_path]], %[[line]])
17+
!CHECK-SAME: -> i32
18+
19+
! Check _FortranAUnlink result code handling
20+
!CHECK-DAG: hlfir.assign %[[unlink_result]] to %[[func_result_decl]]#0 : i32, !fir.ref<i32>
21+
!CHECK-DAG: %[[load_result:.*]] = fir.load %[[func_result_decl]]#0 : !fir.ref<i32>
22+
!CHECK: return %[[load_result]] : i32
23+
unlink_test = unlink(path)
24+
end function unlink_test
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
!RUN: %flang_fc1 -emit-hlfir %s -o - | FileCheck %s
2+
3+
!CHECK-LABEL: func.func @_QPpath_only
4+
!CHECK-SAME: %[[dummyPath:.*]]: !fir.boxchar<1> {fir.bindc_name = "path"}) {
5+
subroutine path_only(path)
6+
CHARACTER(len=*) :: path
7+
!CHECK-DAG: %[[scope:.*]] = fir.dummy_scope : !fir.dscope
8+
!CHECK-DAG: %[[unbox_path:.*]]:2 = fir.unboxchar %[[dummyPath]] : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
9+
!CHECK-DAG: %[[path_decl:.*]]:2 = hlfir.declare %[[unbox_path]]#0 typeparams %[[unbox_path]]#1 dummy_scope %[[scope]] {uniq_name = "_QFpath_onlyEpath"} : (!fir.ref<!fir.char<1,?>>, index, !fir.dscope) -> (!fir.boxchar<1>, !fir.ref<!fir.char<1,?>>)
10+
!CHECK-DAG: %[[src_path_addr:.*]] = fir.address_of(@_{{.*}}) : !fir.ref<!fir.char<1,{{.*}}>>
11+
!CHECK-DAG: %[[line_value:.*]] = arith.constant {{.*}} : i64
12+
!CHECK-DAG: %[[path:.*]] = fir.convert %[[path_decl]]#1 : (!fir.ref<!fir.char<1,?>>) -> !fir.ref<i8>
13+
!CHECK-DAG: %[[path_len:.*]] = fir.convert %[[unbox_path]]#1 : (index) -> i64
14+
!CHECK-DAG: %[[src_path:.*]] = fir.convert %[[src_path_addr]] : (!fir.ref<!fir.char<1,{{.*}}>) -> !fir.ref<i8>
15+
!CHECK-DAG: %[[line:.*]] = fir.convert %[[line_value]] : (i64) -> i32
16+
!CHECK: fir.call @_FortranAUnlink(%[[path]], %[[path_len]], %[[src_path]], %[[line]])
17+
!CHECK-SAME: : (!fir.ref<i8>, i64, !fir.ref<i8>, i32)
18+
!CHECK-SAME: -> i32
19+
call unlink(path)
20+
!CHECK: return
21+
end subroutine path_only
22+
!CHECK: }
23+
24+
!CHECK-LABEL: func.func @_QPall_arguments
25+
!CHECK-SAME: %[[dummyPath:.*]]: !fir.boxchar<1> {fir.bindc_name = "path"}
26+
!CHECK-SAME: %[[dummyStat:.*]]: !fir.ref<i32> {fir.bindc_name = "status"}
27+
!CHECK-SAME: ) {
28+
subroutine all_arguments(path, status)
29+
CHARACTER(len=*) :: path
30+
INTEGER :: status
31+
!CHECK-DAG: %[[scope:.*]] = fir.dummy_scope : !fir.dscope
32+
!CHECK-DAG: %[[unbox_path:.*]]:2 = fir.unboxchar %[[dummyPath]] : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
33+
!CHECK-DAG: %[[path_decl:.*]]:2 = hlfir.declare %[[unbox_path]]#0 typeparams %[[unbox_path]]#1 dummy_scope %[[scope]] {uniq_name = "_QFall_argumentsEpath"} : (!fir.ref<!fir.char<1,?>>, index, !fir.dscope) -> (!fir.boxchar<1>, !fir.ref<!fir.char<1,?>>)
34+
!CHECK-DAG: %[[status_decl:.*]]:2 = hlfir.declare %[[dummyStat]] dummy_scope %[[scope]] {uniq_name = "_QFall_argumentsEstatus"} : (!fir.ref<i32>, !fir.dscope) -> (!fir.ref<i32>, !fir.ref<i32>)
35+
!CHECK-DAG: %[[src_path_addr:.*]] = fir.address_of(@_{{.*}}) : !fir.ref<!fir.char<1,{{.*}}>>
36+
!CHECK-DAG: %[[line_value:.*]] = arith.constant {{.*}} : i64
37+
!CHECK-DAG: %[[path:.*]] = fir.convert %[[path_decl]]#1 : (!fir.ref<!fir.char<1,?>>) -> !fir.ref<i8>
38+
!CHECK-DAG: %[[path_len:.*]] = fir.convert %[[unbox_path]]#1 : (index) -> i64
39+
!CHECK-DAG: %[[src_path:.*]] = fir.convert %[[src_path_addr]] : (!fir.ref<!fir.char<1,{{.*}}>) -> !fir.ref<i8>
40+
!CHECK-DAG: %[[line:.*]] = fir.convert %[[line_value]] : (i64) -> i32
41+
!CHECK: %[[unlink_result:.*]] = fir.call @_FortranAUnlink(%[[path]], %[[path_len]], %[[src_path]], %[[line]])
42+
!CHECK-SAME: : (!fir.ref<i8>, i64, !fir.ref<i8>, i32)
43+
!CHECK-SAME: -> i32
44+
45+
!CHECK-DAG: %[[status_i64:.*]] = fir.convert %[[status_decl]]#0 : (!fir.ref<i32>) -> i64
46+
!CHECK-DAG: %[[c_null:.*]] = arith.constant 0 : i64
47+
!CHECK-DAG: %[[cmp_result:.*]] = arith.cmpi ne, %[[status_i64]], %[[c_null]] : i64
48+
!CHECK: fir.if %[[cmp_result]] {
49+
!CHECK-NEXT: fir.store %[[unlink_result]] to %[[status_decl]]#0 : !fir.ref<i32>
50+
!CHECK-NEXT: }
51+
call unlink(path, status)
52+
!CHECK: return
53+
end subroutine all_arguments
54+
!CHECK: }

0 commit comments

Comments
 (0)