Skip to content

Commit 51862a5

Browse files
committed
[flang] Implement FSEEK and FTELL
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 llvm#132423; CI builds will likely fail until that patch is merged and this PR is rebased.
1 parent 37f8449 commit 51862a5

File tree

10 files changed

+252
-15
lines changed

10 files changed

+252
-15
lines changed

flang-rt/lib/runtime/extensions.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
// These C-coded entry points with Fortran-mangled names implement legacy
1010
// extensions that will eventually be implemented in Fortran.
1111

12+
#include "unit.h"
1213
#include "flang/Runtime/extensions.h"
14+
#include "flang/Runtime/iostat-consts.h"
1315
#include "flang-rt/runtime/descriptor.h"
1416
#include "flang-rt/runtime/terminator.h"
1517
#include "flang-rt/runtime/tools.h"
@@ -268,5 +270,33 @@ void FORTRAN_PROCEDURE_NAME(qsort)(int *array, int *len, int *isize,
268270
qsort(array, *len, *isize, compar);
269271
}
270272

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

flang-rt/lib/runtime/unit.cpp

Lines changed: 38 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,19 @@ void ExternalFileUnit::SetPosition(std::int64_t pos, IoErrorHandler &handler) {
457457
BeginRecord();
458458
}
459459

460+
void ExternalFileUnit::Sought(
461+
std::int64_t zeroBasedPos, IoErrorHandler &handler) {
462+
SetPosition(zeroBasedPos);
463+
if (zeroBasedPos == 0) {
464+
currentRecordNumber = 1;
465+
} else {
466+
// We no longer know which record we're in. Set currentRecordNumber to
467+
// a large value from whence we can both advance and backspace.
468+
currentRecordNumber = std::numeric_limits<std::int64_t>::max() / 2;
469+
endfileRecordNumber.reset();
470+
}
471+
}
472+
460473
bool ExternalFileUnit::SetStreamPos(
461474
std::int64_t oneBasedPos, IoErrorHandler &handler) {
462475
if (access != Access::Stream) {
@@ -474,14 +487,31 @@ bool ExternalFileUnit::SetStreamPos(
474487
frameOffsetInFile_ + recordOffsetInFrame_) {
475488
DoImpliedEndfile(handler);
476489
}
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();
490+
Sought(oneBasedPos - 1, handler);
482491
return true;
483492
}
484493

494+
// GNU FSEEK extension
495+
RT_API_ATTRS bool ExternalFileUnit::Fseek(std::int64_t zeroBasedPos,
496+
enum FseekWhence whence, IoErrorHandler &handler) {
497+
if (whence == FseekEnd) {
498+
Flush(handler); // updates knownSize_
499+
if (auto size{knownSize()}) {
500+
zeroBasedPos += *size;
501+
} else {
502+
return false;
503+
}
504+
} else if (whence == FseekCurrent) {
505+
zeroBasedPos += InquirePos() - 1;
506+
}
507+
if (zeroBasedPos >= 0) {
508+
Sought(zeroBasedPos, handler);
509+
return true;
510+
} else {
511+
return false;
512+
}
513+
}
514+
485515
bool ExternalFileUnit::SetDirectRec(
486516
std::int64_t oneBasedRec, IoErrorHandler &handler) {
487517
if (access != Access::Direct) {
@@ -498,7 +528,7 @@ bool ExternalFileUnit::SetDirectRec(
498528
return false;
499529
}
500530
currentRecordNumber = oneBasedRec;
501-
SetPosition((oneBasedRec - 1) * *openRecl, handler);
531+
SetPosition((oneBasedRec - 1) * *openRecl);
502532
return true;
503533
}
504534

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, IoErrorHandler &);
200208
RT_API_ATTRS void BeginSequentialVariableUnformattedInputRecord(
201209
IoErrorHandler &);
202210
RT_API_ATTRS void BeginVariableFormattedInputRecord(IoErrorHandler &);

flang/docs/Intrinsics.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1096,6 +1096,41 @@ program chdir_func
10961096
end program chdir_func
10971097
```
10981098

1099+
### Non-Standard Intrinsics: FSEEK and FTELL
1100+
1101+
#### Description
1102+
`FSEEK(UNIT, OFFSET, WHENCE)` Sets position in file opened as `UNIT`, returns status.
1103+
1104+
`CALL FSEEK(UNIT, OFFSET, WHENCE[, STATUS])` Sets position, returns any error in `STATUS` if present.
1105+
1106+
`FTELL(UNIT)` Returns current absolute byte offset.
1107+
1108+
`CALL FTELL(UNIT, OFFSET)` Set `OFFSET` to current byte offset in file.
1109+
1110+
These intrinsic procedures are available as both functions and subroutines,
1111+
but both forms cannot be used in the same scope.
1112+
1113+
| | |
1114+
|------------|-------------------------------------------------|
1115+
| `UNIT` | An open unit number |
1116+
| `OFFSET` | A byte offset; set to -1 by `FTELL` on error |
1117+
| `WHENCE` | 0: `OFFSET` is an absolute position |
1118+
| | 1: `OFFSET` is relative to the current position |
1119+
| | 2: `OFFSET` is relative to the end of the file |
1120+
| `STATUS` | Set to a nonzero value if an error occurs |
1121+
|------------|-------------------------------------------------|
1122+
1123+
The aliases `FSEEK64`, `FSEEKO64`, `FSEEKI8`, `FTELL64`, `FTELLO64`, and
1124+
`FTELLI8` are also accepted for further compatibility.
1125+
1126+
Avoid using these intrinsics in new code when the standard `ACCESS="STREAM"`
1127+
feature meets your needs.
1128+
1129+
#### Usage and Info
1130+
1131+
- **Standard:** Extensions to GNU, Intel, and SUN (at least)
1132+
- **Class:** Subroutine, function
1133+
10991134
### Non-Standard Intrinsics: IERRNO
11001135

11011136
#### 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: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,12 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
543543
KINDInt, Rank::vector, IntrinsicClass::transformationalFunction},
544544
{"floor", {{"a", AnyReal}, DefaultingKIND}, KINDInt},
545545
{"fraction", {{"x", SameReal}}, SameReal},
546+
{"fseek",
547+
{{"unit", AnyInt, Rank::scalar}, {"offset", AnyInt, Rank::scalar},
548+
{"whence", AnyInt, Rank::scalar}},
549+
DefaultInt, Rank::scalar},
550+
{"ftell", {{"unit", AnyInt, Rank::scalar}},
551+
TypePattern{IntType, KindCode::exactKind, 8}, Rank::scalar},
546552
{"gamma", {{"x", SameReal}}, SameReal},
547553
{"get_team", {{"level", DefaultInt, Rank::scalar, Optionality::optional}},
548554
TeamType, Rank::scalar, IntrinsicClass::transformationalFunction},
@@ -1073,11 +1079,16 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
10731079
// LOC, probably others
10741080
// TODO: Optionally warn on operand promotion extension
10751081

1076-
// Aliases for a few generic intrinsic functions for legacy
1077-
// compatibility and builtins.
1082+
// Aliases for a few generic procedures for legacy compatibility and builtins.
10781083
static const std::pair<const char *, const char *> genericAlias[]{
10791084
{"and", "iand"},
10801085
{"getenv", "get_environment_variable"},
1086+
{"fseek64", "fseek"},
1087+
{"fseeko64", "fseek"}, // SUN
1088+
{"fseeki8", "fseek"}, // Intel
1089+
{"ftell64", "ftell"},
1090+
{"ftello64", "ftell"}, // SUN
1091+
{"ftelli8", "ftell"}, // Intel
10811092
{"imag", "aimag"},
10821093
{"lshift", "shiftl"},
10831094
{"or", "ior"},
@@ -1506,6 +1517,17 @@ static const IntrinsicInterface intrinsicSubroutine[]{
15061517
{"exit", {{"status", DefaultInt, Rank::scalar, Optionality::optional}}, {},
15071518
Rank::elemental, IntrinsicClass::impureSubroutine},
15081519
{"free", {{"ptr", Addressable}}, {}},
1520+
{"fseek",
1521+
{{"unit", AnyInt, Rank::scalar}, {"offset", AnyInt, Rank::scalar},
1522+
{"whence", AnyInt, Rank::scalar},
1523+
{"status", AnyInt, Rank::scalar, Optionality::optional,
1524+
common::Intent::InOut}},
1525+
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
1526+
{"ftell",
1527+
{{"unit", AnyInt, Rank::scalar},
1528+
{"offset", AnyInt, Rank::scalar, Optionality::required,
1529+
common::Intent::Out}},
1530+
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
15091531
{"get_command",
15101532
{{"command", DefaultChar, Rank::scalar, Optionality::optional,
15111533
common::Intent::Out},
@@ -2764,8 +2786,8 @@ bool IntrinsicProcTable::Implementation::IsDualIntrinsic(
27642786
const std::string &name) const {
27652787
// Collection for some intrinsics with function and subroutine form,
27662788
// in order to pass the semantic check.
2767-
static const std::string dualIntrinsic[]{{"chdir"s}, {"etime"s}, {"getcwd"s},
2768-
{"rename"s}, {"second"s}, {"system"s}};
2789+
static const std::string dualIntrinsic[]{{"chdir"}, {"etime"}, {"fseek"},
2790+
{"ftell"}, {"getcwd"}, {"rename"}, {"second"}, {"system"}};
27692791

27702792
return llvm::is_contained(dualIntrinsic, name);
27712793
}

flang/lib/Optimizer/Builder/IntrinsicCall.cpp

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,17 @@ static constexpr IntrinsicHandler handlers[]{
449449
{"floor", &I::genFloor},
450450
{"fraction", &I::genFraction},
451451
{"free", &I::genFree},
452+
{"fseek",
453+
&I::genFseek,
454+
{{{"unit", asValue},
455+
{"offset", asValue},
456+
{"whence", asValue},
457+
{"status", asAddr, handleDynamicOptional}}},
458+
/*isElemental=*/false},
459+
{"ftell",
460+
&I::genFtell,
461+
{{{"unit", asValue}, {"offset", asAddr}}},
462+
/*isElemental=*/false},
452463
{"get_command",
453464
&I::genGetCommand,
454465
{{{"command", asBox, handleDynamicOptional},
@@ -4113,6 +4124,69 @@ void IntrinsicLibrary::genFree(llvm::ArrayRef<fir::ExtendedValue> args) {
41134124
fir::runtime::genFree(builder, loc, fir::getBase(args[0]));
41144125
}
41154126

4127+
// FSEEK
4128+
fir::ExtendedValue
4129+
IntrinsicLibrary::genFseek(std::optional<mlir::Type> resultType,
4130+
llvm::ArrayRef<fir::ExtendedValue> args) {
4131+
assert((args.size() == 4 && !resultType.has_value()) ||
4132+
(args.size() == 3 && resultType.has_value()));
4133+
mlir::Value unit = fir::getBase(args[0]);
4134+
mlir::Value offset = fir::getBase(args[1]);
4135+
mlir::Value whence = fir::getBase(args[2]);
4136+
if (!unit)
4137+
fir::emitFatalError(loc, "expected UNIT argument");
4138+
if (!offset)
4139+
fir::emitFatalError(loc, "expected OFFSET argument");
4140+
if (!whence)
4141+
fir::emitFatalError(loc, "expected WHENCE argument");
4142+
mlir::Value statusValue =
4143+
fir::runtime::genFseek(builder, loc, unit, offset, whence);
4144+
if (resultType.has_value()) { // function
4145+
return statusValue;
4146+
} else { // subroutine
4147+
const fir::ExtendedValue &statusVar = args[3];
4148+
if (!isStaticallyAbsent(statusVar)) {
4149+
mlir::Value statusAddr = fir::getBase(statusVar);
4150+
mlir::Value statusIsPresentAtRuntime =
4151+
builder.genIsNotNullAddr(loc, statusAddr);
4152+
builder.genIfThen(loc, statusIsPresentAtRuntime)
4153+
.genThen([&]() {
4154+
builder.createStoreWithConvert(loc, statusValue, statusAddr);
4155+
})
4156+
.end();
4157+
}
4158+
return {};
4159+
}
4160+
}
4161+
4162+
// FTELL
4163+
fir::ExtendedValue
4164+
IntrinsicLibrary::genFtell(std::optional<mlir::Type> resultType,
4165+
llvm::ArrayRef<fir::ExtendedValue> args) {
4166+
assert((args.size() == 2 && !resultType.has_value()) ||
4167+
(args.size() == 1 && resultType.has_value()));
4168+
mlir::Value unit = fir::getBase(args[0]);
4169+
if (!unit)
4170+
fir::emitFatalError(loc, "expected UNIT argument");
4171+
mlir::Value offsetValue = fir::runtime::genFtell(builder, loc, unit);
4172+
if (resultType.has_value()) { // function
4173+
return offsetValue;
4174+
} else { // subroutine
4175+
const fir::ExtendedValue &offsetVar = args[1];
4176+
if (!isStaticallyAbsent(offsetVar)) {
4177+
mlir::Value offsetAddr = fir::getBase(offsetVar);
4178+
mlir::Value offsetIsPresentAtRuntime =
4179+
builder.genIsNotNullAddr(loc, offsetAddr);
4180+
builder.genIfThen(loc, offsetIsPresentAtRuntime)
4181+
.genThen([&]() {
4182+
builder.createStoreWithConvert(loc, offsetValue, offsetAddr);
4183+
})
4184+
.end();
4185+
}
4186+
return {};
4187+
}
4188+
}
4189+
41164190
// GETCWD
41174191
fir::ExtendedValue
41184192
IntrinsicLibrary::genGetCwd(std::optional<mlir::Type> resultType,

0 commit comments

Comments
 (0)