Skip to content

Commit c8bde44

Browse files
authored
[flang] Implement FSEEK and FTELL (#133003)
Add function and subroutine forms of FSEEK and FTELL as intrinsic procedures. Accept common aliases from legacy compilers as well. A separate patch to llvm-test-suite will enable tests for these procedures once this patch has merged. Depends on #132423; CI builds will likely fail until that patch is merged and this PR is rebased.
1 parent aca2708 commit c8bde44

File tree

10 files changed

+255
-16
lines changed

10 files changed

+255
-16
lines changed

flang-rt/lib/runtime/extensions.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@
1010
// extensions that will eventually be implemented in Fortran.
1111

1212
#include "flang/Runtime/extensions.h"
13+
#include "unit.h"
1314
#include "flang-rt/runtime/descriptor.h"
1415
#include "flang-rt/runtime/terminator.h"
1516
#include "flang-rt/runtime/tools.h"
1617
#include "flang/Runtime/command.h"
1718
#include "flang/Runtime/entry-names.h"
1819
#include "flang/Runtime/io-api.h"
20+
#include "flang/Runtime/iostat-consts.h"
1921
#include <chrono>
2022
#include <cstdio>
2123
#include <cstring>
@@ -275,5 +277,33 @@ void RTNAME(Perror)(const char *str) { perror(str); }
275277
// GNU extension function TIME()
276278
std::int64_t RTNAME(time)() { return time(nullptr); }
277279

280+
// Extension procedures related to I/O
281+
282+
namespace io {
283+
std::int32_t RTNAME(Fseek)(int unitNumber, std::int64_t zeroBasedPos,
284+
int whence, const char *sourceFileName, int lineNumber) {
285+
if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(unitNumber)}) {
286+
Terminator terminator{sourceFileName, lineNumber};
287+
IoErrorHandler handler{terminator};
288+
if (unit->Fseek(
289+
zeroBasedPos, static_cast<enum FseekWhence>(whence), handler)) {
290+
return IostatOk;
291+
} else {
292+
return IostatCannotReposition;
293+
}
294+
} else {
295+
return IostatBadUnitNumber;
296+
}
297+
}
298+
299+
std::int64_t RTNAME(Ftell)(int unitNumber) {
300+
if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(unitNumber)}) {
301+
return unit->InquirePos() - 1; // zero-based result
302+
} else {
303+
return -1;
304+
}
305+
}
306+
} // namespace io
307+
278308
} // namespace Fortran::runtime
279309
} // extern "C"

flang-rt/lib/runtime/unit.cpp

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -441,14 +441,14 @@ void ExternalFileUnit::Rewind(IoErrorHandler &handler) {
441441
"REWIND(UNIT=%d) on non-sequential file", unitNumber());
442442
} else {
443443
DoImpliedEndfile(handler);
444-
SetPosition(0, handler);
444+
SetPosition(0);
445445
currentRecordNumber = 1;
446446
leftTabLimit.reset();
447447
anyWriteSinceLastPositioning_ = false;
448448
}
449449
}
450450

451-
void ExternalFileUnit::SetPosition(std::int64_t pos, IoErrorHandler &handler) {
451+
void ExternalFileUnit::SetPosition(std::int64_t pos) {
452452
frameOffsetInFile_ = pos;
453453
recordOffsetInFrame_ = 0;
454454
if (access == Access::Direct) {
@@ -457,6 +457,18 @@ void ExternalFileUnit::SetPosition(std::int64_t pos, IoErrorHandler &handler) {
457457
BeginRecord();
458458
}
459459

460+
void ExternalFileUnit::Sought(std::int64_t zeroBasedPos) {
461+
SetPosition(zeroBasedPos);
462+
if (zeroBasedPos == 0) {
463+
currentRecordNumber = 1;
464+
} else {
465+
// We no longer know which record we're in. Set currentRecordNumber to
466+
// a large value from whence we can both advance and backspace.
467+
currentRecordNumber = std::numeric_limits<std::int64_t>::max() / 2;
468+
endfileRecordNumber.reset();
469+
}
470+
}
471+
460472
bool ExternalFileUnit::SetStreamPos(
461473
std::int64_t oneBasedPos, IoErrorHandler &handler) {
462474
if (access != Access::Stream) {
@@ -474,14 +486,31 @@ bool ExternalFileUnit::SetStreamPos(
474486
frameOffsetInFile_ + recordOffsetInFrame_) {
475487
DoImpliedEndfile(handler);
476488
}
477-
SetPosition(oneBasedPos - 1, handler);
478-
// We no longer know which record we're in. Set currentRecordNumber to
479-
// a large value from whence we can both advance and backspace.
480-
currentRecordNumber = std::numeric_limits<std::int64_t>::max() / 2;
481-
endfileRecordNumber.reset();
489+
Sought(oneBasedPos - 1);
482490
return true;
483491
}
484492

493+
// GNU FSEEK extension
494+
RT_API_ATTRS bool ExternalFileUnit::Fseek(std::int64_t zeroBasedPos,
495+
enum FseekWhence whence, IoErrorHandler &handler) {
496+
if (whence == FseekEnd) {
497+
Flush(handler); // updates knownSize_
498+
if (auto size{knownSize()}) {
499+
zeroBasedPos += *size;
500+
} else {
501+
return false;
502+
}
503+
} else if (whence == FseekCurrent) {
504+
zeroBasedPos += InquirePos() - 1;
505+
}
506+
if (zeroBasedPos >= 0) {
507+
Sought(zeroBasedPos);
508+
return true;
509+
} else {
510+
return false;
511+
}
512+
}
513+
485514
bool ExternalFileUnit::SetDirectRec(
486515
std::int64_t oneBasedRec, IoErrorHandler &handler) {
487516
if (access != Access::Direct) {
@@ -498,7 +527,7 @@ bool ExternalFileUnit::SetDirectRec(
498527
return false;
499528
}
500529
currentRecordNumber = oneBasedRec;
501-
SetPosition((oneBasedRec - 1) * *openRecl, handler);
530+
SetPosition((oneBasedRec - 1) * *openRecl);
502531
return true;
503532
}
504533

flang-rt/lib/runtime/unit.h

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ class UnitMap;
3333
class ChildIo;
3434
class ExternalFileUnit;
3535

36+
enum FseekWhence {
37+
FseekSet = 0,
38+
FseekCurrent = 1,
39+
FseekEnd = 2,
40+
};
41+
3642
RT_OFFLOAD_VAR_GROUP_BEGIN
3743
// Predefined file units.
3844
extern RT_VAR_ATTRS ExternalFileUnit *defaultInput; // unit 5
@@ -176,8 +182,9 @@ class ExternalFileUnit : public ConnectionState,
176182
RT_API_ATTRS void Endfile(IoErrorHandler &);
177183
RT_API_ATTRS void Rewind(IoErrorHandler &);
178184
RT_API_ATTRS void EndIoStatement();
179-
RT_API_ATTRS bool SetStreamPos(
180-
std::int64_t, IoErrorHandler &); // one-based, for POS=
185+
RT_API_ATTRS bool SetStreamPos(std::int64_t oneBasedPos, IoErrorHandler &);
186+
RT_API_ATTRS bool Fseek(
187+
std::int64_t zeroBasedPos, enum FseekWhence, IoErrorHandler &);
181188
RT_API_ATTRS bool SetDirectRec(
182189
std::int64_t, IoErrorHandler &); // one-based, for REC=
183190
RT_API_ATTRS std::int64_t InquirePos() const {
@@ -196,7 +203,8 @@ class ExternalFileUnit : public ConnectionState,
196203
static RT_API_ATTRS UnitMap &CreateUnitMap();
197204
static RT_API_ATTRS UnitMap &GetUnitMap();
198205
RT_API_ATTRS const char *FrameNextInput(IoErrorHandler &, std::size_t);
199-
RT_API_ATTRS void SetPosition(std::int64_t, IoErrorHandler &); // zero-based
206+
RT_API_ATTRS void SetPosition(std::int64_t zeroBasedPos);
207+
RT_API_ATTRS void Sought(std::int64_t zeroBasedPos);
200208
RT_API_ATTRS void BeginSequentialVariableUnformattedInputRecord(
201209
IoErrorHandler &);
202210
RT_API_ATTRS void BeginVariableFormattedInputRecord(IoErrorHandler &);

flang/docs/Intrinsics.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1197,6 +1197,44 @@ program chdir_func
11971197
end program chdir_func
11981198
```
11991199

1200+
### Non-Standard Intrinsics: FSEEK and FTELL
1201+
1202+
#### Description
1203+
`FSEEK(UNIT, OFFSET, WHENCE)` Sets position in file opened as `UNIT`, returns status.
1204+
1205+
`CALL FSEEK(UNIT, OFFSET, WHENCE[, STATUS])` Sets position, returns any error in `STATUS` if present.
1206+
1207+
`FTELL(UNIT)` Returns current absolute byte offset.
1208+
1209+
`CALL FTELL(UNIT, OFFSET)` Set `OFFSET` to current byte offset in file.
1210+
1211+
These intrinsic procedures are available as both functions and subroutines,
1212+
but both forms cannot be used in the same scope.
1213+
1214+
These arguments must all be integers.
1215+
The value returned from the function form of `FTELL` is `INTEGER(8)`.
1216+
1217+
| | |
1218+
|------------|-------------------------------------------------|
1219+
| `UNIT` | An open unit number |
1220+
| `OFFSET` | A byte offset; set to -1 by `FTELL` on error |
1221+
| `WHENCE` | 0: `OFFSET` is an absolute position |
1222+
| | 1: `OFFSET` is relative to the current position |
1223+
| | 2: `OFFSET` is relative to the end of the file |
1224+
| `STATUS` | Set to a nonzero value if an error occurs |
1225+
|------------|-------------------------------------------------|
1226+
1227+
The aliases `FSEEK64`, `FSEEKO64`, `FSEEKI8`, `FTELL64`, `FTELLO64`, and
1228+
`FTELLI8` are also accepted for further compatibility.
1229+
1230+
Avoid using these intrinsics in new code when the standard `ACCESS="STREAM"`
1231+
feature meets your needs.
1232+
1233+
#### Usage and Info
1234+
1235+
- **Standard:** Extensions to GNU, Intel, and SUN (at least)
1236+
- **Class:** Subroutine, function
1237+
12001238
### Non-Standard Intrinsics: IERRNO
12011239

12021240
#### Description

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,10 @@ struct IntrinsicLibrary {
266266
mlir::Value genFraction(mlir::Type resultType,
267267
mlir::ArrayRef<mlir::Value> args);
268268
void genFree(mlir::ArrayRef<fir::ExtendedValue> args);
269+
fir::ExtendedValue genFseek(std::optional<mlir::Type>,
270+
mlir::ArrayRef<fir::ExtendedValue> args);
271+
fir::ExtendedValue genFtell(std::optional<mlir::Type>,
272+
mlir::ArrayRef<fir::ExtendedValue> args);
269273
fir::ExtendedValue genGetCwd(std::optional<mlir::Type> resultType,
270274
llvm::ArrayRef<fir::ExtendedValue> args);
271275
void genGetCommand(mlir::ArrayRef<fir::ExtendedValue> args);

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ void genEtime(fir::FirOpBuilder &builder, mlir::Location loc,
4949

5050
void genFree(fir::FirOpBuilder &builder, mlir::Location loc, mlir::Value ptr);
5151

52+
mlir::Value genFseek(fir::FirOpBuilder &builder, mlir::Location loc,
53+
mlir::Value unit, mlir::Value offset, mlir::Value whence);
54+
mlir::Value genFtell(fir::FirOpBuilder &builder, mlir::Location loc,
55+
mlir::Value unit);
56+
5257
mlir::Value genGetUID(fir::FirOpBuilder &, mlir::Location);
5358
mlir::Value genGetGID(fir::FirOpBuilder &, mlir::Location);
5459

flang/include/flang/Runtime/extensions.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ void FORTRAN_PROCEDURE_NAME(fdate)(char *string, std::int64_t length);
3838

3939
void RTNAME(Free)(std::intptr_t ptr);
4040

41+
// Common extensions FSEEK & FTELL, variously named
42+
std::int32_t RTNAME(Fseek)(int unit, std::int64_t zeroBasedPos, int whence,
43+
const char *sourceFileName, int lineNumber);
44+
std::int64_t RTNAME(Ftell)(int unit);
45+
4146
// GNU Fortran 77 compatibility function IARGC.
4247
std::int32_t FORTRAN_PROCEDURE_NAME(iargc)();
4348

flang/lib/Evaluate/intrinsics.cpp

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,12 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
545545
KINDInt, Rank::vector, IntrinsicClass::transformationalFunction},
546546
{"floor", {{"a", AnyReal}, DefaultingKIND}, KINDInt},
547547
{"fraction", {{"x", SameReal}}, SameReal},
548+
{"fseek",
549+
{{"unit", AnyInt, Rank::scalar}, {"offset", AnyInt, Rank::scalar},
550+
{"whence", AnyInt, Rank::scalar}},
551+
DefaultInt, Rank::scalar},
552+
{"ftell", {{"unit", AnyInt, Rank::scalar}},
553+
TypePattern{IntType, KindCode::exactKind, 8}, Rank::scalar},
548554
{"gamma", {{"x", SameReal}}, SameReal},
549555
{"get_team", {{"level", DefaultInt, Rank::scalar, Optionality::optional}},
550556
TeamType, Rank::scalar, IntrinsicClass::transformationalFunction},
@@ -1083,11 +1089,16 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
10831089
// LOC, probably others
10841090
// TODO: Optionally warn on operand promotion extension
10851091

1086-
// Aliases for a few generic intrinsic functions for legacy
1087-
// compatibility and builtins.
1092+
// Aliases for a few generic procedures for legacy compatibility and builtins.
10881093
static const std::pair<const char *, const char *> genericAlias[]{
10891094
{"and", "iand"},
10901095
{"getenv", "get_environment_variable"},
1096+
{"fseek64", "fseek"},
1097+
{"fseeko64", "fseek"}, // SUN
1098+
{"fseeki8", "fseek"}, // Intel
1099+
{"ftell64", "ftell"},
1100+
{"ftello64", "ftell"}, // SUN
1101+
{"ftelli8", "ftell"}, // Intel
10911102
{"imag", "aimag"},
10921103
{"lshift", "shiftl"},
10931104
{"or", "ior"},
@@ -1524,6 +1535,17 @@ static const IntrinsicInterface intrinsicSubroutine[]{
15241535
{"exit", {{"status", DefaultInt, Rank::scalar, Optionality::optional}}, {},
15251536
Rank::elemental, IntrinsicClass::impureSubroutine},
15261537
{"free", {{"ptr", Addressable}}, {}},
1538+
{"fseek",
1539+
{{"unit", AnyInt, Rank::scalar}, {"offset", AnyInt, Rank::scalar},
1540+
{"whence", AnyInt, Rank::scalar},
1541+
{"status", AnyInt, Rank::scalar, Optionality::optional,
1542+
common::Intent::InOut}},
1543+
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
1544+
{"ftell",
1545+
{{"unit", AnyInt, Rank::scalar},
1546+
{"offset", AnyInt, Rank::scalar, Optionality::required,
1547+
common::Intent::Out}},
1548+
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
15271549
{"get_command",
15281550
{{"command", DefaultChar, Rank::scalar, Optionality::optional,
15291551
common::Intent::Out},
@@ -2811,9 +2833,9 @@ bool IntrinsicProcTable::Implementation::IsDualIntrinsic(
28112833
const std::string &name) const {
28122834
// Collection for some intrinsics with function and subroutine form,
28132835
// in order to pass the semantic check.
2814-
static const std::string dualIntrinsic[]{{"chdir"s}, {"etime"s}, {"getcwd"s},
2815-
{"hostnm"s}, {"rename"s}, {"second"s}, {"system"s}, {"unlink"s}};
2816-
2836+
static const std::string dualIntrinsic[]{{"chdir"}, {"etime"}, {"fseek"},
2837+
{"ftell"}, {"getcwd"}, {"hostnm"}, {"rename"}, {"second"}, {"system"},
2838+
{"unlink"}};
28172839
return llvm::is_contained(dualIntrinsic, name);
28182840
}
28192841

flang/lib/Optimizer/Builder/IntrinsicCall.cpp

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,17 @@ static constexpr IntrinsicHandler handlers[]{
462462
{"floor", &I::genFloor},
463463
{"fraction", &I::genFraction},
464464
{"free", &I::genFree},
465+
{"fseek",
466+
&I::genFseek,
467+
{{{"unit", asValue},
468+
{"offset", asValue},
469+
{"whence", asValue},
470+
{"status", asAddr, handleDynamicOptional}}},
471+
/*isElemental=*/false},
472+
{"ftell",
473+
&I::genFtell,
474+
{{{"unit", asValue}, {"offset", asAddr}}},
475+
/*isElemental=*/false},
465476
{"get_command",
466477
&I::genGetCommand,
467478
{{{"command", asBox, handleDynamicOptional},
@@ -4139,6 +4150,69 @@ void IntrinsicLibrary::genFree(llvm::ArrayRef<fir::ExtendedValue> args) {
41394150
fir::runtime::genFree(builder, loc, fir::getBase(args[0]));
41404151
}
41414152

4153+
// FSEEK
4154+
fir::ExtendedValue
4155+
IntrinsicLibrary::genFseek(std::optional<mlir::Type> resultType,
4156+
llvm::ArrayRef<fir::ExtendedValue> args) {
4157+
assert((args.size() == 4 && !resultType.has_value()) ||
4158+
(args.size() == 3 && resultType.has_value()));
4159+
mlir::Value unit = fir::getBase(args[0]);
4160+
mlir::Value offset = fir::getBase(args[1]);
4161+
mlir::Value whence = fir::getBase(args[2]);
4162+
if (!unit)
4163+
fir::emitFatalError(loc, "expected UNIT argument");
4164+
if (!offset)
4165+
fir::emitFatalError(loc, "expected OFFSET argument");
4166+
if (!whence)
4167+
fir::emitFatalError(loc, "expected WHENCE argument");
4168+
mlir::Value statusValue =
4169+
fir::runtime::genFseek(builder, loc, unit, offset, whence);
4170+
if (resultType.has_value()) { // function
4171+
return builder.createConvert(loc, *resultType, statusValue);
4172+
} else { // subroutine
4173+
const fir::ExtendedValue &statusVar = args[3];
4174+
if (!isStaticallyAbsent(statusVar)) {
4175+
mlir::Value statusAddr = fir::getBase(statusVar);
4176+
mlir::Value statusIsPresentAtRuntime =
4177+
builder.genIsNotNullAddr(loc, statusAddr);
4178+
builder.genIfThen(loc, statusIsPresentAtRuntime)
4179+
.genThen([&]() {
4180+
builder.createStoreWithConvert(loc, statusValue, statusAddr);
4181+
})
4182+
.end();
4183+
}
4184+
return {};
4185+
}
4186+
}
4187+
4188+
// FTELL
4189+
fir::ExtendedValue
4190+
IntrinsicLibrary::genFtell(std::optional<mlir::Type> resultType,
4191+
llvm::ArrayRef<fir::ExtendedValue> args) {
4192+
assert((args.size() == 2 && !resultType.has_value()) ||
4193+
(args.size() == 1 && resultType.has_value()));
4194+
mlir::Value unit = fir::getBase(args[0]);
4195+
if (!unit)
4196+
fir::emitFatalError(loc, "expected UNIT argument");
4197+
mlir::Value offsetValue = fir::runtime::genFtell(builder, loc, unit);
4198+
if (resultType.has_value()) { // function
4199+
return offsetValue;
4200+
} else { // subroutine
4201+
const fir::ExtendedValue &offsetVar = args[1];
4202+
if (!isStaticallyAbsent(offsetVar)) {
4203+
mlir::Value offsetAddr = fir::getBase(offsetVar);
4204+
mlir::Value offsetIsPresentAtRuntime =
4205+
builder.genIsNotNullAddr(loc, offsetAddr);
4206+
builder.genIfThen(loc, offsetIsPresentAtRuntime)
4207+
.genThen([&]() {
4208+
builder.createStoreWithConvert(loc, offsetValue, offsetAddr);
4209+
})
4210+
.end();
4211+
}
4212+
return {};
4213+
}
4214+
}
4215+
41424216
// GETCWD
41434217
fir::ExtendedValue
41444218
IntrinsicLibrary::genGetCwd(std::optional<mlir::Type> resultType,

0 commit comments

Comments
 (0)