Skip to content

Commit f81e46f

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 996cf5d commit f81e46f

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>
@@ -272,5 +274,33 @@ void FORTRAN_PROCEDURE_NAME(qsort)(int *array, int *len, int *isize,
272274
// PERROR(STRING)
273275
void RTNAME(Perror)(const char *str) { perror(str); }
274276

277+
// Extension procedures related to I/O
278+
279+
namespace io {
280+
std::int32_t RTNAME(Fseek)(int unitNumber, std::int64_t zeroBasedPos,
281+
int whence, const char *sourceFileName, int lineNumber) {
282+
if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(unitNumber)}) {
283+
Terminator terminator{sourceFileName, lineNumber};
284+
IoErrorHandler handler{terminator};
285+
if (unit->Fseek(
286+
zeroBasedPos, static_cast<enum FseekWhence>(whence), handler)) {
287+
return IostatOk;
288+
} else {
289+
return IostatCannotReposition;
290+
}
291+
} else {
292+
return IostatBadUnitNumber;
293+
}
294+
}
295+
296+
std::int64_t RTNAME(Ftell)(int unitNumber) {
297+
if (ExternalFileUnit * unit{ExternalFileUnit::LookUp(unitNumber)}) {
298+
return unit->InquirePos() - 1; // zero-based result
299+
} else {
300+
return -1;
301+
}
302+
}
303+
} // namespace io
304+
275305
} // namespace Fortran::runtime
276306
} // 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
@@ -1176,6 +1176,44 @@ program chdir_func
11761176
end program chdir_func
11771177
```
11781178

1179+
### Non-Standard Intrinsics: FSEEK and FTELL
1180+
1181+
#### Description
1182+
`FSEEK(UNIT, OFFSET, WHENCE)` Sets position in file opened as `UNIT`, returns status.
1183+
1184+
`CALL FSEEK(UNIT, OFFSET, WHENCE[, STATUS])` Sets position, returns any error in `STATUS` if present.
1185+
1186+
`FTELL(UNIT)` Returns current absolute byte offset.
1187+
1188+
`CALL FTELL(UNIT, OFFSET)` Set `OFFSET` to current byte offset in file.
1189+
1190+
These intrinsic procedures are available as both functions and subroutines,
1191+
but both forms cannot be used in the same scope.
1192+
1193+
These arguments must all be integers.
1194+
The value returned from the function form of `FTELL` is `INTEGER(8)`.
1195+
1196+
| | |
1197+
|------------|-------------------------------------------------|
1198+
| `UNIT` | An open unit number |
1199+
| `OFFSET` | A byte offset; set to -1 by `FTELL` on error |
1200+
| `WHENCE` | 0: `OFFSET` is an absolute position |
1201+
| | 1: `OFFSET` is relative to the current position |
1202+
| | 2: `OFFSET` is relative to the end of the file |
1203+
| `STATUS` | Set to a nonzero value if an error occurs |
1204+
|------------|-------------------------------------------------|
1205+
1206+
The aliases `FSEEK64`, `FSEEKO64`, `FSEEKI8`, `FTELL64`, `FTELLO64`, and
1207+
`FTELLI8` are also accepted for further compatibility.
1208+
1209+
Avoid using these intrinsics in new code when the standard `ACCESS="STREAM"`
1210+
feature meets your needs.
1211+
1212+
#### Usage and Info
1213+
1214+
- **Standard:** Extensions to GNU, Intel, and SUN (at least)
1215+
- **Class:** Subroutine, function
1216+
11791217
### Non-Standard Intrinsics: IERRNO
11801218

11811219
#### 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},
@@ -1081,11 +1087,16 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
10811087
// LOC, probably others
10821088
// TODO: Optionally warn on operand promotion extension
10831089

1084-
// Aliases for a few generic intrinsic functions for legacy
1085-
// compatibility and builtins.
1090+
// Aliases for a few generic procedures for legacy compatibility and builtins.
10861091
static const std::pair<const char *, const char *> genericAlias[]{
10871092
{"and", "iand"},
10881093
{"getenv", "get_environment_variable"},
1094+
{"fseek64", "fseek"},
1095+
{"fseeko64", "fseek"}, // SUN
1096+
{"fseeki8", "fseek"}, // Intel
1097+
{"ftell64", "ftell"},
1098+
{"ftello64", "ftell"}, // SUN
1099+
{"ftelli8", "ftell"}, // Intel
10891100
{"imag", "aimag"},
10901101
{"lshift", "shiftl"},
10911102
{"or", "ior"},
@@ -1522,6 +1533,17 @@ static const IntrinsicInterface intrinsicSubroutine[]{
15221533
{"exit", {{"status", DefaultInt, Rank::scalar, Optionality::optional}}, {},
15231534
Rank::elemental, IntrinsicClass::impureSubroutine},
15241535
{"free", {{"ptr", Addressable}}, {}},
1536+
{"fseek",
1537+
{{"unit", AnyInt, Rank::scalar}, {"offset", AnyInt, Rank::scalar},
1538+
{"whence", AnyInt, Rank::scalar},
1539+
{"status", AnyInt, Rank::scalar, Optionality::optional,
1540+
common::Intent::InOut}},
1541+
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
1542+
{"ftell",
1543+
{{"unit", AnyInt, Rank::scalar},
1544+
{"offset", AnyInt, Rank::scalar, Optionality::required,
1545+
common::Intent::Out}},
1546+
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
15251547
{"get_command",
15261548
{{"command", DefaultChar, Rank::scalar, Optionality::optional,
15271549
common::Intent::Out},
@@ -2809,9 +2831,9 @@ bool IntrinsicProcTable::Implementation::IsDualIntrinsic(
28092831
const std::string &name) const {
28102832
// Collection for some intrinsics with function and subroutine form,
28112833
// in order to pass the semantic check.
2812-
static const std::string dualIntrinsic[]{{"chdir"s}, {"etime"s}, {"getcwd"s},
2813-
{"hostnm"s}, {"rename"s}, {"second"s}, {"system"s}, {"unlink"s}};
2814-
2834+
static const std::string dualIntrinsic[]{{"chdir"}, {"etime"}, {"fseek"},
2835+
{"ftell"}, {"getcwd"}, {"hostnm"}, {"rename"}, {"second"}, {"system"},
2836+
{"unlink"}};
28152837
return llvm::is_contained(dualIntrinsic, name);
28162838
}
28172839

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},
@@ -4138,6 +4149,69 @@ void IntrinsicLibrary::genFree(llvm::ArrayRef<fir::ExtendedValue> args) {
41384149
fir::runtime::genFree(builder, loc, fir::getBase(args[0]));
41394150
}
41404151

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

0 commit comments

Comments
 (0)