Skip to content

Commit 8febe67

Browse files
committed
[flang] Lower statement function references in HLFIR
Enable lowering of statement function references in HLFIR. This follows the same principle as statement function lowering with the current lowering: - Actual arguments are lowered and mapped to the statement function dummy symbols. - "HostAssociated" symbols are mapped to their host values (these are the symbols referred to inside the statement function expressions that are not statement function dummies. e.g: `x` in `stmt_func(i) = x(i)`). - The statement function expression is evaluated. evaluate::SetLength has to be lowered to deal with statement functions returning characters since the front-end is generating one to ensure the statement function expression value is trimmed/padded to match the statement function declared type. Differential Revision: https://reviews.llvm.org/D140220
1 parent 071c62c commit 8febe67

File tree

7 files changed

+137
-8
lines changed

7 files changed

+137
-8
lines changed

flang/include/flang/Lower/ConvertVariable.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ void defineCommonBlocks(
7070
/// instantiateVariable cannot be called.
7171
void mapSymbolAttributes(AbstractConverter &, const pft::Variable &, SymMap &,
7272
StatementContext &, mlir::Value preAlloc = {});
73+
void mapSymbolAttributes(AbstractConverter &, const semantics::SymbolRef &,
74+
SymMap &, StatementContext &,
75+
mlir::Value preAlloc = {});
7376

7477
/// Instantiate the variables that appear in the specification expressions
7578
/// of the result of a function call. The instantiated variables are added

flang/lib/Lower/Bridge.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,11 @@ class FirConverter : public Fortran::lower::AbstractConverter {
449449

450450
void copySymbolBinding(Fortran::lower::SymbolRef src,
451451
Fortran::lower::SymbolRef target) override final {
452-
localSymbols.addSymbol(target, lookupSymbol(src).toExtendedValue());
452+
if (bridge.getLoweringOptions().getLowerToHighLevelFIR())
453+
localSymbols.addVariableDefinition(
454+
target, localSymbols.lookupVariableDefinition(src).value());
455+
else
456+
localSymbols.addSymbol(target, lookupSymbol(src).toExtendedValue());
453457
}
454458

455459
/// Add the symbol binding to the inner-most level of the symbol map and

flang/lib/Lower/ConvertCall.cpp

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,82 @@ fir::ExtendedValue Fortran::lower::genCallOpAndResult(
406406
return callResult;
407407
}
408408

409+
static hlfir::EntityWithAttributes genStmtFunctionRef(
410+
mlir::Location loc, Fortran::lower::AbstractConverter &converter,
411+
Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx,
412+
const Fortran::evaluate::ProcedureRef &procRef) {
413+
const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol();
414+
assert(symbol && "expected symbol in ProcedureRef of statement functions");
415+
const auto &details = symbol->get<Fortran::semantics::SubprogramDetails>();
416+
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
417+
418+
// Statement functions have their own scope, we just need to associate
419+
// the dummy symbols to argument expressions. There are no
420+
// optional/alternate return arguments. Statement functions cannot be
421+
// recursive (directly or indirectly) so it is safe to add dummy symbols to
422+
// the local map here.
423+
symMap.pushScope();
424+
llvm::SmallVector<hlfir::AssociateOp> exprAssociations;
425+
for (auto [arg, bind] : llvm::zip(details.dummyArgs(), procRef.arguments())) {
426+
assert(arg && "alternate return in statement function");
427+
assert(bind && "optional argument in statement function");
428+
const auto *expr = bind->UnwrapExpr();
429+
// TODO: assumed type in statement function, that surprisingly seems
430+
// allowed, probably because nobody thought of restricting this usage.
431+
// gfortran/ifort compiles this.
432+
assert(expr && "assumed type used as statement function argument");
433+
// As per Fortran 2018 C1580, statement function arguments can only be
434+
// scalars.
435+
// The only care is to use the dummy character explicit length if any
436+
// instead of the actual argument length (that can be bigger).
437+
hlfir::EntityWithAttributes loweredArg = Fortran::lower::convertExprToHLFIR(
438+
loc, converter, *expr, symMap, stmtCtx);
439+
fir::FortranVariableOpInterface variableIface = loweredArg.getIfVariable();
440+
if (!variableIface) {
441+
// So far only FortranVariableOpInterface can be mapped to symbols.
442+
// Create an hlfir.associate to create a variable from a potential
443+
// value argument.
444+
mlir::Type argType = converter.genType(*arg);
445+
auto associate = hlfir::genAssociateExpr(
446+
loc, builder, loweredArg, argType, toStringRef(arg->name()));
447+
exprAssociations.push_back(associate);
448+
variableIface = associate;
449+
}
450+
const Fortran::semantics::DeclTypeSpec *type = arg->GetType();
451+
if (type &&
452+
type->category() == Fortran::semantics::DeclTypeSpec::Character) {
453+
// Instantiate character as if it was a normal dummy argument so that the
454+
// statement function dummy character length is applied and dealt with
455+
// correctly.
456+
symMap.addSymbol(*arg, variableIface.getBase());
457+
Fortran::lower::mapSymbolAttributes(converter, *arg, symMap, stmtCtx);
458+
} else {
459+
// No need to create an extra hlfir.declare otherwise for
460+
// numerical and logical scalar dummies.
461+
symMap.addVariableDefinition(*arg, variableIface);
462+
}
463+
}
464+
465+
// Explicitly map statement function host associated symbols to their
466+
// parent scope lowered symbol box.
467+
for (const Fortran::semantics::SymbolRef &sym :
468+
Fortran::evaluate::CollectSymbols(*details.stmtFunction()))
469+
if (const auto *details =
470+
sym->detailsIf<Fortran::semantics::HostAssocDetails>())
471+
converter.copySymbolBinding(details->symbol(), sym);
472+
473+
hlfir::Entity result = Fortran::lower::convertExprToHLFIR(
474+
loc, converter, details.stmtFunction().value(), symMap, stmtCtx);
475+
symMap.popScope();
476+
// The result must not be a variable.
477+
result = hlfir::loadTrivialScalar(loc, builder, result);
478+
if (result.isVariable())
479+
result = hlfir::Entity{builder.create<hlfir::AsExprOp>(loc, result)};
480+
for (auto associate : exprAssociations)
481+
builder.create<hlfir::EndAssociateOp>(loc, associate);
482+
return hlfir::EntityWithAttributes{result};
483+
}
484+
409485
/// Is this a call to an elemental procedure with at least one array argument?
410486
static bool
411487
isElementalProcWithArrayArgs(const Fortran::evaluate::ProcedureRef &procRef) {
@@ -454,7 +530,7 @@ class CallBuilder {
454530
return genIntrinsicRef(procRef, resultType, *specific);
455531
}
456532
if (isStatementFunctionCall(procRef))
457-
TODO(loc, "lowering Statement function call to HLFIR");
533+
return genStmtFunctionRef(loc, converter, symMap, stmtCtx, procRef);
458534

459535
Fortran::lower::CallerInterface caller(procRef, converter);
460536
mlir::FunctionType callSiteType = caller.genFunctionType();

flang/lib/Lower/ConvertExprToHLFIR.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -511,13 +511,15 @@ template <int KIND>
511511
struct BinaryOp<Fortran::evaluate::SetLength<KIND>> {
512512
using Op = Fortran::evaluate::SetLength<KIND>;
513513
static hlfir::EntityWithAttributes gen(mlir::Location loc,
514-
fir::FirOpBuilder &, const Op &,
515-
hlfir::Entity, hlfir::Entity) {
516-
TODO(loc, "SetLength lowering to HLFIR");
514+
fir::FirOpBuilder &builder, const Op &,
515+
hlfir::Entity string,
516+
hlfir::Entity length) {
517+
return hlfir::EntityWithAttributes{
518+
builder.create<hlfir::SetLengthOp>(loc, string, length)};
517519
}
518520
static void
519-
genResultTypeParams(mlir::Location loc, fir::FirOpBuilder &builder,
520-
hlfir::Entity lhs, hlfir::Entity rhs,
521+
genResultTypeParams(mlir::Location, fir::FirOpBuilder &, hlfir::Entity,
522+
hlfir::Entity rhs,
521523
llvm::SmallVectorImpl<mlir::Value> &resultTypeParams) {
522524
resultTypeParams.push_back(rhs);
523525
}

flang/lib/Lower/ConvertVariable.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1890,6 +1890,14 @@ void Fortran::lower::mapCallInterfaceSymbols(
18901890
}
18911891
}
18921892

1893+
void Fortran::lower::mapSymbolAttributes(
1894+
AbstractConverter &converter, const Fortran::semantics::SymbolRef &symbol,
1895+
Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx,
1896+
mlir::Value preAlloc) {
1897+
mapSymbolAttributes(converter, pft::Variable{symbol}, symMap, stmtCtx,
1898+
preAlloc);
1899+
}
1900+
18931901
void Fortran::lower::createRuntimeTypeInfoGlobal(
18941902
Fortran::lower::AbstractConverter &converter, mlir::Location loc,
18951903
const Fortran::semantics::Symbol &typeInfoSym) {

flang/lib/Lower/SymbolMap.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,8 @@ Fortran::lower::SymMap::lookupImpliedDo(Fortran::lower::SymMap::AcDoVar var) {
9292
}
9393

9494
llvm::Optional<fir::FortranVariableOpInterface>
95-
Fortran::lower::SymMap::lookupVariableDefinition(semantics::SymbolRef sym) {
95+
Fortran::lower::SymMap::lookupVariableDefinition(semantics::SymbolRef symRef) {
96+
Fortran::semantics::SymbolRef sym = symRef.get().GetUltimate();
9697
for (auto jmap = symbolMapStack.rbegin(), jend = symbolMapStack.rend();
9798
jmap != jend; ++jmap) {
9899
auto iter = jmap->find(&*sym);
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
! Test lowering of statement functions to HLFIR
2+
! RUN: bbc -emit-fir -hlfir -o - %s 2>&1 | FileCheck %s
3+
4+
subroutine numeric_test(x)
5+
integer :: x(:), i, stmt_func
6+
stmt_func(i) = x(i)
7+
call bar(stmt_func(42))
8+
end subroutine
9+
! CHECK-LABEL: func.func @_QPnumeric_test(
10+
! CHECK: %[[VAL_4:.*]]:2 = hlfir.declare %[[VAL_0:[^)]*]] {{.*}}x"
11+
! CHECK: %[[VAL_6:.*]] = arith.constant 42 : i32
12+
! CHECK: %[[VAL_7:.*]]:3 = hlfir.associate %[[VAL_6]] {uniq_name = "i"} : (i32) -> (!fir.ref<i32>, !fir.ref<i32>, i1)
13+
! CHECK: %[[VAL_8:.*]] = fir.load %[[VAL_7]]#0 : !fir.ref<i32>
14+
! CHECK: %[[VAL_9:.*]] = fir.convert %[[VAL_8]] : (i32) -> i64
15+
! CHECK: %[[VAL_10:.*]] = hlfir.designate %[[VAL_4]]#0 (%[[VAL_9]]) : (!fir.box<!fir.array<?xi32>>, i64) -> !fir.ref<i32>
16+
! CHECK: %[[VAL_11:.*]] = fir.load %[[VAL_10]] : !fir.ref<i32>
17+
18+
subroutine char_test(c, n)
19+
character(*) :: c
20+
character(n) :: char_stmt_func_dummy_arg
21+
character(10) :: stmt_func
22+
stmt_func(char_stmt_func_dummy_arg) = char_stmt_func_dummy_arg
23+
call bar2(stmt_func(c))
24+
end subroutine
25+
! CHECK-LABEL: func.func @_QPchar_test(
26+
! CHECK: %[[VAL_4:.*]]:2 = hlfir.declare %[[VAL_3:.*]]#0 typeparams %[[VAL_3]]#1 {{.*}}c"
27+
! CHECK: %[[VAL_5:.*]]:2 = hlfir.declare %[[VAL_2:[^ ]*]] {{.*}}n"
28+
! CHECK: %[[VAL_13:.*]]:2 = fir.unboxchar %[[VAL_4]]#0 : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
29+
! CHECK: %[[VAL_14:.*]] = fir.load %[[VAL_5]]#0 : !fir.ref<i32>
30+
! CHECK: %[[VAL_15:.*]] = arith.constant 0 : i32
31+
! CHECK: %[[VAL_16:.*]] = arith.cmpi sgt, %[[VAL_14]], %[[VAL_15]] : i32
32+
! CHECK: %[[VAL_17:.*]] = arith.select %[[VAL_16]], %[[VAL_14]], %[[VAL_15]] : i32
33+
! CHECK: %[[VAL_18:.*]]:2 = hlfir.declare %[[VAL_13]]#0 typeparams %[[VAL_17]] {uniq_name = "_QFstmt_funcEchar_stmt_func_dummy_arg"} : (!fir.ref<!fir.char<1,?>>, i32) -> (!fir.boxchar<1>, !fir.ref<!fir.char<1,?>>)
34+
! CHECK: %[[VAL_19:.*]] = arith.constant 10 : i64
35+
! CHECK: %[[VAL_20:.*]] = hlfir.set_length %[[VAL_18]]#0 len %[[VAL_19]] : (!fir.boxchar<1>, i64) -> !hlfir.expr<!fir.char<1,10>>

0 commit comments

Comments
 (0)