Skip to content

Commit 8868431

Browse files
authored
[flang] Set func.func arg attributes for procedure designators (#68420)
Currently, if the first usage of a procedure not defined in the file was inside a procedure designator reference (not a call to it), the lowered func.func lacked the argument attributes if any. Fix this by using `CallInterface<T>::declare` too in SignatureBuilder to create a new func.func instead of using custom code. Note: this problem was made worse by the fact that module variables fir.global are currently lowered before the module procedures func.func are created. I will try to fix that in a later patch (the debug location may still be wrong in certain cases) because there is quite some test fallout when changing the order of globals/funcop in the output.
1 parent 87b2682 commit 8868431

File tree

5 files changed

+104
-56
lines changed

5 files changed

+104
-56
lines changed

flang/include/flang/Lower/CallInterface.h

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -418,15 +418,14 @@ mlir::FunctionType
418418
translateSignature(const Fortran::evaluate::ProcedureDesignator &,
419419
Fortran::lower::AbstractConverter &);
420420

421-
/// Declare or find the mlir::func::FuncOp named \p name. If the
422-
/// mlir::func::FuncOp does not exist yet, declare it with the signature
423-
/// translated from the ProcedureDesignator argument.
421+
/// Declare or find the mlir::func::FuncOp for the procedure designator
422+
/// \p proc. If the mlir::func::FuncOp does not exist yet, declare it with
423+
/// the signature translated from the ProcedureDesignator argument.
424424
/// Due to Fortran implicit function typing rules, the returned FuncOp is not
425425
/// guaranteed to have the signature from ProcedureDesignator if the FuncOp was
426426
/// already declared.
427427
mlir::func::FuncOp
428-
getOrDeclareFunction(llvm::StringRef name,
429-
const Fortran::evaluate::ProcedureDesignator &,
428+
getOrDeclareFunction(const Fortran::evaluate::ProcedureDesignator &,
430429
Fortran::lower::AbstractConverter &);
431430

432431
/// Return the type of an argument that is a dummy procedure. This may be an

flang/lib/Lower/CallInterface.cpp

Lines changed: 71 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -54,17 +54,22 @@ bool Fortran::lower::CallerInterface::hasAlternateReturns() const {
5454
return procRef.hasAlternateReturns();
5555
}
5656

57-
std::string Fortran::lower::CallerInterface::getMangledName() const {
58-
const Fortran::evaluate::ProcedureDesignator &proc = procRef.proc();
59-
// Return the binding label (from BIND(C...)) or the mangled name of the
60-
// symbol.
57+
/// Return the binding label (from BIND(C...)) or the mangled name of the
58+
/// symbol.
59+
static std::string
60+
getProcMangledName(const Fortran::evaluate::ProcedureDesignator &proc,
61+
Fortran::lower::AbstractConverter &converter) {
6162
if (const Fortran::semantics::Symbol *symbol = proc.GetSymbol())
6263
return converter.mangleName(symbol->GetUltimate());
6364
assert(proc.GetSpecificIntrinsic() &&
6465
"expected intrinsic procedure in designator");
6566
return proc.GetName();
6667
}
6768

69+
std::string Fortran::lower::CallerInterface::getMangledName() const {
70+
return getProcMangledName(procRef.proc(), converter);
71+
}
72+
6873
const Fortran::semantics::Symbol *
6974
Fortran::lower::CallerInterface::getProcedureSymbol() const {
7075
return procRef.proc().GetSymbol();
@@ -127,17 +132,25 @@ Fortran::lower::CallerInterface::getIfIndirectCallSymbol() const {
127132
return nullptr;
128133
}
129134

130-
mlir::Location Fortran::lower::CallerInterface::getCalleeLocation() const {
131-
const Fortran::evaluate::ProcedureDesignator &proc = procRef.proc();
132-
// FIXME: If the callee is defined in the same file but after the current
135+
static mlir::Location
136+
getProcedureDesignatorLoc(const Fortran::evaluate::ProcedureDesignator &proc,
137+
Fortran::lower::AbstractConverter &converter) {
138+
// Note: If the callee is defined in the same file but after the current
133139
// unit we cannot get its location here and the funcOp is created at the
134140
// wrong location (i.e, the caller location).
141+
// To prevent this, it is up to the bridge to first declare all functions
142+
// defined in the translation unit before lowering any calls or procedure
143+
// designator references.
135144
if (const Fortran::semantics::Symbol *symbol = proc.GetSymbol())
136145
return converter.genLocation(symbol->name());
137146
// Use current location for intrinsics.
138147
return converter.getCurrentLocation();
139148
}
140149

150+
mlir::Location Fortran::lower::CallerInterface::getCalleeLocation() const {
151+
return getProcedureDesignatorLoc(procRef.proc(), converter);
152+
}
153+
141154
// Get dummy argument characteristic for a procedure with implicit interface
142155
// from the actual argument characteristic. The actual argument may not be a F77
143156
// entity. The attribute must be dropped and the shape, if any, must be made
@@ -1341,6 +1354,12 @@ class SignatureBuilder
13411354
bool isImplicit = forceImplicit || proc.CanBeCalledViaImplicitInterface();
13421355
determineInterface(isImplicit, proc);
13431356
}
1357+
SignatureBuilder(const Fortran::evaluate::ProcedureDesignator &procDes,
1358+
Fortran::lower::AbstractConverter &c)
1359+
: CallInterface{c}, procDesignator{&procDes},
1360+
proc{Fortran::evaluate::characteristics::Procedure::Characterize(
1361+
procDes, converter.getFoldingContext())
1362+
.value()} {}
13441363
/// Does the procedure characteristics being translated have alternate
13451364
/// returns ?
13461365
bool hasAlternateReturns() const {
@@ -1354,17 +1373,24 @@ class SignatureBuilder
13541373

13551374
/// This is only here to fulfill CRTP dependencies and should not be called.
13561375
std::string getMangledName() const {
1357-
llvm_unreachable("trying to get name from SignatureBuilder");
1376+
if (procDesignator)
1377+
return getProcMangledName(*procDesignator, converter);
1378+
fir::emitFatalError(
1379+
converter.getCurrentLocation(),
1380+
"should not query name when only building function type");
13581381
}
13591382

13601383
/// This is only here to fulfill CRTP dependencies and should not be called.
13611384
mlir::Location getCalleeLocation() const {
1362-
llvm_unreachable("trying to get callee location from SignatureBuilder");
1385+
if (procDesignator)
1386+
return getProcedureDesignatorLoc(*procDesignator, converter);
1387+
return converter.getCurrentLocation();
13631388
}
13641389

1365-
/// This is only here to fulfill CRTP dependencies and should not be called.
13661390
const Fortran::semantics::Symbol *getProcedureSymbol() const {
1367-
llvm_unreachable("trying to get callee symbol from SignatureBuilder");
1391+
if (procDesignator)
1392+
return procDesignator->GetSymbol();
1393+
return nullptr;
13681394
};
13691395

13701396
Fortran::evaluate::characteristics::Procedure characterize() const {
@@ -1380,11 +1406,37 @@ class SignatureBuilder
13801406
return proc;
13811407
}
13821408

1409+
/// Set internal procedure attribute on MLIR function. Internal procedure
1410+
/// are defined in the current file and will not go through SignatureBuilder.
1411+
void setFuncAttrs(mlir::func::FuncOp) const {}
1412+
13831413
/// This is not the description of an indirect call.
13841414
static constexpr bool isIndirectCall() { return false; }
13851415

13861416
/// Return the translated signature.
1387-
mlir::FunctionType getFunctionType() { return genFunctionType(); }
1417+
mlir::FunctionType getFunctionType() {
1418+
if (interfaceDetermined)
1419+
fir::emitFatalError(converter.getCurrentLocation(),
1420+
"SignatureBuilder should only be used once");
1421+
// Most unrestricted intrinsic characteristics have the Elemental attribute
1422+
// which triggers CanBeCalledViaImplicitInterface to return false. However,
1423+
// using implicit interface rules is just fine here.
1424+
bool forceImplicit =
1425+
procDesignator && procDesignator->GetSpecificIntrinsic();
1426+
bool isImplicit = forceImplicit || proc.CanBeCalledViaImplicitInterface();
1427+
determineInterface(isImplicit, proc);
1428+
interfaceDetermined = true;
1429+
return genFunctionType();
1430+
}
1431+
1432+
mlir::func::FuncOp getOrCreateFuncOp() {
1433+
if (interfaceDetermined)
1434+
fir::emitFatalError(converter.getCurrentLocation(),
1435+
"SignatureBuilder should only be used once");
1436+
declare();
1437+
interfaceDetermined = true;
1438+
return getFuncOp();
1439+
}
13881440

13891441
// Copy of base implementation.
13901442
static constexpr bool hasHostAssociated() { return false; }
@@ -1393,47 +1445,30 @@ class SignatureBuilder
13931445
}
13941446

13951447
private:
1396-
const Fortran::evaluate::characteristics::Procedure &proc;
1448+
const Fortran::evaluate::ProcedureDesignator *procDesignator = nullptr;
1449+
Fortran::evaluate::characteristics::Procedure proc;
1450+
bool interfaceDetermined = false;
13971451
};
13981452

13991453
mlir::FunctionType Fortran::lower::translateSignature(
14001454
const Fortran::evaluate::ProcedureDesignator &proc,
14011455
Fortran::lower::AbstractConverter &converter) {
1402-
std::optional<Fortran::evaluate::characteristics::Procedure> characteristics =
1403-
Fortran::evaluate::characteristics::Procedure::Characterize(
1404-
proc, converter.getFoldingContext());
1405-
// Most unrestricted intrinsic characteristic has the Elemental attribute
1406-
// which triggers CanBeCalledViaImplicitInterface to return false. However,
1407-
// using implicit interface rules is just fine here.
1408-
bool forceImplicit = proc.GetSpecificIntrinsic();
1409-
return SignatureBuilder{characteristics.value(), converter, forceImplicit}
1410-
.getFunctionType();
1456+
return SignatureBuilder{proc, converter}.getFunctionType();
14111457
}
14121458

14131459
mlir::func::FuncOp Fortran::lower::getOrDeclareFunction(
1414-
llvm::StringRef name, const Fortran::evaluate::ProcedureDesignator &proc,
1460+
const Fortran::evaluate::ProcedureDesignator &proc,
14151461
Fortran::lower::AbstractConverter &converter) {
14161462
mlir::ModuleOp module = converter.getModuleOp();
1463+
std::string name = getProcMangledName(proc, converter);
14171464
mlir::func::FuncOp func = fir::FirOpBuilder::getNamedFunction(module, name);
14181465
if (func)
14191466
return func;
14201467

1421-
const Fortran::semantics::Symbol *symbol = proc.GetSymbol();
1422-
assert(symbol && "non user function in getOrDeclareFunction");
14231468
// getOrDeclareFunction is only used for functions not defined in the current
14241469
// program unit, so use the location of the procedure designator symbol, which
14251470
// is the first occurrence of the procedure in the program unit.
1426-
mlir::Location loc = converter.genLocation(symbol->name());
1427-
std::optional<Fortran::evaluate::characteristics::Procedure> characteristics =
1428-
Fortran::evaluate::characteristics::Procedure::Characterize(
1429-
proc, converter.getFoldingContext());
1430-
mlir::FunctionType ty = SignatureBuilder{characteristics.value(), converter,
1431-
/*forceImplicit=*/false}
1432-
.getFunctionType();
1433-
mlir::func::FuncOp newFunc =
1434-
fir::FirOpBuilder::createFunction(loc, module, name, ty);
1435-
addSymbolAttribute(newFunc, *symbol, converter.getMLIRContext());
1436-
return newFunc;
1471+
return SignatureBuilder{proc, converter}.getOrCreateFuncOp();
14371472
}
14381473

14391474
// Is it required to pass a dummy procedure with \p characteristics as a tuple

flang/lib/Lower/ConvertProcedureDesignator.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,11 @@ fir::ExtendedValue Fortran::lower::convertProcedureDesignator(
6262
std::tie(funcPtr, funcPtrResultLength) =
6363
fir::factory::extractCharacterProcedureTuple(builder, loc, funcPtr);
6464
} else {
65-
std::string name = converter.mangleName(*symbol);
6665
mlir::func::FuncOp func =
67-
Fortran::lower::getOrDeclareFunction(name, proc, converter);
68-
funcPtr = builder.create<fir::AddrOfOp>(loc, func.getFunctionType(),
69-
builder.getSymbolRefAttr(name));
66+
Fortran::lower::getOrDeclareFunction(proc, converter);
67+
mlir::SymbolRefAttr nameAttr = builder.getSymbolRefAttr(func.getSymName());
68+
funcPtr =
69+
builder.create<fir::AddrOfOp>(loc, func.getFunctionType(), nameAttr);
7070
}
7171
if (Fortran::lower::mustPassLengthWithDummyProcedure(proc, converter)) {
7272
// The result length, if available here, must be propagated along the

flang/lib/Lower/IO.cpp

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -362,17 +362,14 @@ getNonTbpDefinedIoTableAddr(Fortran::lower::AbstractConverter &converter,
362362
Fortran::evaluate::ProcedureDesignator{*procSym}},
363363
stmtCtx))));
364364
} else {
365-
std::string procName = converter.mangleName(*procSym);
366-
mlir::func::FuncOp procDef = builder.getNamedFunction(procName);
367-
if (!procDef)
368-
procDef = Fortran::lower::getOrDeclareFunction(
369-
procName, Fortran::evaluate::ProcedureDesignator{*procSym},
370-
converter);
371-
insert(
372-
builder.createConvert(loc, refTy,
373-
builder.create<fir::AddrOfOp>(
374-
loc, procDef.getFunctionType(),
375-
builder.getSymbolRefAttr(procName))));
365+
mlir::func::FuncOp procDef = Fortran::lower::getOrDeclareFunction(
366+
Fortran::evaluate::ProcedureDesignator{*procSym}, converter);
367+
mlir::SymbolRefAttr nameAttr =
368+
builder.getSymbolRefAttr(procDef.getSymName());
369+
insert(builder.createConvert(
370+
loc, refTy,
371+
builder.create<fir::AddrOfOp>(loc, procDef.getFunctionType(),
372+
nameAttr)));
376373
}
377374
} else {
378375
insert(builder.createNullConstant(loc, refTy));
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
! Ensure that func.func arguments are given the Fortran attributes
2+
! even if their first use is in a procedure designator reference
3+
! and not a call.
4+
5+
! RUN: bbc -emit-hlfir -o - %s | FileCheck %s
6+
7+
subroutine test(x)
8+
interface
9+
subroutine foo(x)
10+
integer, optional, target :: x
11+
end subroutine
12+
end interface
13+
integer, optional, target :: x
14+
call takes_proc(foo)
15+
call foo(x)
16+
end subroutine
17+
! CHECK: func.func private @_QPfoo(!fir.ref<i32> {fir.optional, fir.target})

0 commit comments

Comments
 (0)