Skip to content

Commit d9941a5

Browse files
Saldivarcheraahrun
authored andcommitted
[flang] Support FLUSH as an intrinsic subroutine (llvm#165942)
Previously `FLUSH` was only recognized in statement form (e.g. `flush(unit)`); a subroutine invocation `call flush(unit)` was treated as a generic user call with no special semantics. This change teaches lowering/semantics to handle `CALL FLUSH` equivalently. Fixes llvm#119418
1 parent e572ad2 commit d9941a5

File tree

9 files changed

+146
-6
lines changed

9 files changed

+146
-6
lines changed

flang-rt/lib/runtime/extensions.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,17 @@ void FORTRAN_PROCEDURE_NAME(flush)(const int &unit) {
163163
Cookie cookie{IONAME(BeginFlush)(unit, __FILE__, __LINE__)};
164164
IONAME(EndIoStatement)(cookie);
165165
}
166+
167+
void RTNAME(Flush)(int unit) {
168+
// We set the `unit == -1` on the `flush()` case, so flush all units.
169+
if (unit < 0) {
170+
Terminator terminator{__FILE__, __LINE__};
171+
IoErrorHandler handler{terminator};
172+
ExternalFileUnit::FlushAll(handler);
173+
return;
174+
}
175+
FORTRAN_PROCEDURE_NAME(flush)(unit);
176+
}
166177
} // namespace io
167178

168179
// CALL FDATE(DATE)

flang/docs/Intrinsics.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1288,6 +1288,40 @@ program chdir_func
12881288
end program chdir_func
12891289
```
12901290

1291+
### Non-Standard Intrinsics: FLUSH
1292+
1293+
#### Description
1294+
`FLUSH(UNIT)` causes all pending I/O operations for the file connected to the
1295+
specified unit to be completed. If `UNIT` is omitted, all units are flushed.
1296+
1297+
#### Arguments
1298+
1299+
| | |
1300+
|------------|---------------------------------------------------------------------------------------------------|
1301+
| `UNIT` | (Optional) The unit number of an open file. If omitted, all open units are flushed. The type shall be `INTEGER`. |
1302+
1303+
#### Usage and Info
1304+
1305+
- **Standard:** GNU extension
1306+
- **Class:** Subroutine
1307+
- **Syntax:** `CALL FLUSH([UNIT])`
1308+
1309+
#### Example
1310+
```Fortran
1311+
program demo_flush
1312+
integer :: unit
1313+
1314+
! Flush all units
1315+
call flush()
1316+
1317+
! Flush specific unit
1318+
open(unit=10, file='output.dat')
1319+
write(10, *) 'Data'
1320+
call flush(10)
1321+
close(10)
1322+
end program demo_flush
1323+
```
1324+
12911325
### Non-Standard Intrinsics: FSEEK and FTELL
12921326

12931327
#### Description

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ struct IntrinsicLibrary {
254254
template <Extremum, ExtremumBehavior>
255255
mlir::Value genExtremum(mlir::Type, llvm::ArrayRef<mlir::Value>);
256256
mlir::Value genFloor(mlir::Type, llvm::ArrayRef<mlir::Value>);
257+
void genFlush(llvm::ArrayRef<fir::ExtendedValue>);
257258
mlir::Value genFraction(mlir::Type resultType,
258259
mlir::ArrayRef<mlir::Value> args);
259260
void genFree(mlir::ArrayRef<fir::ExtendedValue> args);

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ mlir::Value genDsecnds(fir::FirOpBuilder &builder, mlir::Location loc,
5151
void genEtime(fir::FirOpBuilder &builder, mlir::Location loc,
5252
mlir::Value values, mlir::Value time);
5353

54+
void genFlush(fir::FirOpBuilder &builder, mlir::Location loc, mlir::Value unit);
55+
5456
void genFree(fir::FirOpBuilder &builder, mlir::Location loc, mlir::Value ptr);
5557

5658
mlir::Value genFseek(fir::FirOpBuilder &builder, mlir::Location loc,

flang/include/flang/Runtime/extensions.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ double RTNAME(Dsecnds)(double *refTime, const char *sourceFile, int line);
3434

3535
// CALL FLUSH(n) antedates the Fortran 2003 FLUSH statement.
3636
void FORTRAN_PROCEDURE_NAME(flush)(const int &unit);
37+
void RTNAME(Flush)(int unit);
3738

3839
// GNU extension subroutine FDATE
3940
void FORTRAN_PROCEDURE_NAME(fdate)(char *string, std::int64_t length);

flang/lib/Evaluate/intrinsics.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1597,6 +1597,10 @@ static const IntrinsicInterface intrinsicSubroutine[]{
15971597
{"exit", {{"status", DefaultInt, Rank::scalar, Optionality::optional}}, {},
15981598
Rank::elemental, IntrinsicClass::impureSubroutine},
15991599
{"free", {{"ptr", Addressable}}, {}},
1600+
{"flush",
1601+
{{"unit", AnyInt, Rank::scalar, Optionality::optional,
1602+
common::Intent::In}},
1603+
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
16001604
{"fseek",
16011605
{{"unit", AnyInt, Rank::scalar}, {"offset", AnyInt, Rank::scalar},
16021606
{"whence", AnyInt, Rank::scalar},

flang/lib/Optimizer/Builder/IntrinsicCall.cpp

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,11 @@ static bool isStaticallyAbsent(llvm::ArrayRef<mlir::Value> args,
9191
size_t argIndex) {
9292
return args.size() <= argIndex || !args[argIndex];
9393
}
94+
static bool isOptional(mlir::Value value) {
95+
auto varIface = mlir::dyn_cast_or_null<fir::FortranVariableOpInterface>(
96+
value.getDefiningOp());
97+
return varIface && varIface.isOptional();
98+
}
9499

95100
/// Test if an ExtendedValue is present. This is used to test if an intrinsic
96101
/// argument is present at compile time. This does not imply that the related
@@ -303,6 +308,10 @@ static constexpr IntrinsicHandler handlers[]{
303308
{"back", asValue, handleDynamicOptional}}},
304309
/*isElemental=*/false},
305310
{"floor", &I::genFloor},
311+
{"flush",
312+
&I::genFlush,
313+
{{{"unit", asAddr}}},
314+
/*isElemental=*/false},
306315
{"fraction", &I::genFraction},
307316
{"free", &I::genFree},
308317
{"fseek",
@@ -3942,6 +3951,40 @@ mlir::Value IntrinsicLibrary::genFloor(mlir::Type resultType,
39423951
return builder.createConvert(loc, resultType, floor);
39433952
}
39443953

3954+
// FLUSH
3955+
void IntrinsicLibrary::genFlush(llvm::ArrayRef<fir::ExtendedValue> args) {
3956+
assert(args.size() == 1);
3957+
3958+
mlir::Value unit;
3959+
if (isStaticallyAbsent(args[0]))
3960+
// Give a sentinal value of `-1` on the `()` case.
3961+
unit = builder.createIntegerConstant(loc, builder.getI32Type(), -1);
3962+
else {
3963+
unit = fir::getBase(args[0]);
3964+
if (isOptional(unit)) {
3965+
mlir::Value isPresent =
3966+
fir::IsPresentOp::create(builder, loc, builder.getI1Type(), unit);
3967+
unit = builder
3968+
.genIfOp(loc, builder.getI32Type(), isPresent,
3969+
/*withElseRegion=*/true)
3970+
.genThen([&]() {
3971+
mlir::Value loaded = fir::LoadOp::create(builder, loc, unit);
3972+
fir::ResultOp::create(builder, loc, loaded);
3973+
})
3974+
.genElse([&]() {
3975+
mlir::Value negOne = builder.createIntegerConstant(
3976+
loc, builder.getI32Type(), -1);
3977+
fir::ResultOp::create(builder, loc, negOne);
3978+
})
3979+
.getResults()[0];
3980+
} else {
3981+
unit = fir::LoadOp::create(builder, loc, unit);
3982+
}
3983+
}
3984+
3985+
fir::runtime::genFlush(builder, loc, unit);
3986+
}
3987+
39453988
// FRACTION
39463989
mlir::Value IntrinsicLibrary::genFraction(mlir::Type resultType,
39473990
llvm::ArrayRef<mlir::Value> args) {
@@ -6298,12 +6341,6 @@ IntrinsicLibrary::genCharacterCompare(mlir::Type resultType,
62986341
fir::getBase(args[1]), fir::getLen(args[1]));
62996342
}
63006343

6301-
static bool isOptional(mlir::Value value) {
6302-
auto varIface = mlir::dyn_cast_or_null<fir::FortranVariableOpInterface>(
6303-
value.getDefiningOp());
6304-
return varIface && varIface.isOptional();
6305-
}
6306-
63076344
// LOC
63086345
fir::ExtendedValue
63096346
IntrinsicLibrary::genLoc(mlir::Type resultType,

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,15 @@ void fir::runtime::genEtime(fir::FirOpBuilder &builder, mlir::Location loc,
137137
fir::CallOp::create(builder, loc, runtimeFunc, args);
138138
}
139139

140+
void fir::runtime::genFlush(fir::FirOpBuilder &builder, mlir::Location loc,
141+
mlir::Value unit) {
142+
auto runtimeFunc = fir::runtime::getRuntimeFunc<mkRTKey(Flush)>(loc, builder);
143+
llvm::SmallVector<mlir::Value> args = fir::runtime::createArguments(
144+
builder, loc, runtimeFunc.getFunctionType(), unit);
145+
146+
fir::CallOp::create(builder, loc, runtimeFunc, args);
147+
}
148+
140149
void fir::runtime::genFree(fir::FirOpBuilder &builder, mlir::Location loc,
141150
mlir::Value ptr) {
142151
auto runtimeFunc = fir::runtime::getRuntimeFunc<mkRTKey(Free)>(loc, builder);
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
! RUN: bbc -emit-hlfir %s -o - | FileCheck %s
2+
! RUN: %flang_fc1 -emit-hlfir %s -o - | FileCheck %s
3+
!
4+
! Test lowering of intrinsic subroutine FLUSH with and without optional UNIT argument.
5+
!
6+
! CHECK-LABEL: func.func @_QPflush_all()
7+
! CHECK: %[[UNIT:.*]] = arith.constant -1 : i32
8+
! CHECK: fir.call @_FortranAFlush(%[[UNIT]]) fastmath<contract> : (i32) -> ()
9+
! CHECK: return
10+
subroutine flush_all()
11+
call flush() ! flush all units
12+
end subroutine
13+
14+
! CHECK-LABEL: func.func @_QPflush_unit()
15+
! CHECK: %[[ALLOCA:.*]] = fir.alloca i32
16+
! CHECK: %[[UNITC:.*]] = arith.constant 10 : i32
17+
! CHECK: fir.store %[[UNITC]] to %[[ALLOCA]] : !fir.ref<i32>
18+
! CHECK: %[[LOADED:.*]] = fir.load %[[ALLOCA]] : !fir.ref<i32>
19+
! CHECK: fir.call @_FortranAFlush(%[[LOADED]]) fastmath<contract> : (i32) -> ()
20+
! CHECK: return
21+
subroutine flush_unit()
22+
call flush(10) ! flush specific unit
23+
end subroutine
24+
25+
! CHECK-LABEL: func.func @_QPflush_optional(
26+
! CHECK-SAME: %[[ARG0:.*]]: !fir.ref<i32> {fir.bindc_name = "unit", fir.optional}) {
27+
! CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[ARG0]] dummy_scope %{{.*}} {fortran_attrs = #fir.var_attrs<optional>, uniq_name = "_QFflush_optionalEunit"} : (!fir.ref<i32>, !fir.dscope) -> (!fir.ref<i32>, !fir.ref<i32>)
28+
! CHECK: %[[IS_PRESENT:.*]] = fir.is_present %[[DECL]]#0 : (!fir.ref<i32>) -> i1
29+
! CHECK: %[[UNIT:.*]] = fir.if %[[IS_PRESENT]] -> (i32) {
30+
! CHECK: %[[LOADED:.*]] = fir.load %[[DECL]]#0 : !fir.ref<i32>
31+
! CHECK: fir.result %[[LOADED]] : i32
32+
! CHECK: } else {
33+
! CHECK: %[[DEFAULT:.*]] = arith.constant -1 : i32
34+
! CHECK: fir.result %[[DEFAULT]] : i32
35+
! CHECK: }
36+
! CHECK: fir.call @_FortranAFlush(%[[UNIT]]) fastmath<contract> : (i32) -> ()
37+
! CHECK: return
38+
subroutine flush_optional(unit)
39+
integer, optional :: unit
40+
call flush(unit) ! flush with dynamically optional argument
41+
end subroutine

0 commit comments

Comments
 (0)