[flang-commits] [flang] [flang] Set func.func arg attributes for procedure designators (PR #68420)
via flang-commits
flang-commits at lists.llvm.org
Fri Oct 6 07:50:05 PDT 2023
https://github.com/jeanPerier created https://github.com/llvm/llvm-project/pull/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.
>From 9348a2da9c289878e0057636d40a2f5c37fbcd95 Mon Sep 17 00:00:00 2001
From: Jean Perier <jperier at nvidia.com>
Date: Fri, 6 Oct 2023 06:58:35 -0700
Subject: [PATCH] [flang] Set func.func arg attributes for procedure
designators
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 for in
SignatureBuilder instead of custom code.
---
flang/include/flang/Lower/CallInterface.h | 9 +-
flang/lib/Lower/CallInterface.cpp | 107 ++++++++++++------
.../lib/Lower/ConvertProcedureDesignator.cpp | 8 +-
flang/lib/Lower/IO.cpp | 19 ++--
.../HLFIR/procedure-designators-arg-attrs.f90 | 17 +++
5 files changed, 104 insertions(+), 56 deletions(-)
create mode 100644 flang/test/Lower/HLFIR/procedure-designators-arg-attrs.f90
diff --git a/flang/include/flang/Lower/CallInterface.h b/flang/include/flang/Lower/CallInterface.h
index ec025e792349aba..579bdcfd8988792 100644
--- a/flang/include/flang/Lower/CallInterface.h
+++ b/flang/include/flang/Lower/CallInterface.h
@@ -418,15 +418,14 @@ mlir::FunctionType
translateSignature(const Fortran::evaluate::ProcedureDesignator &,
Fortran::lower::AbstractConverter &);
-/// Declare or find the mlir::func::FuncOp named \p name. If the
-/// mlir::func::FuncOp does not exist yet, declare it with the signature
-/// translated from the ProcedureDesignator argument.
+/// Declare or find the mlir::func::FuncOp for the procedure designator
+/// \p proc. If the mlir::func::FuncOp does not exist yet, declare it with
+/// the signature translated from the ProcedureDesignator argument.
/// Due to Fortran implicit function typing rules, the returned FuncOp is not
/// guaranteed to have the signature from ProcedureDesignator if the FuncOp was
/// already declared.
mlir::func::FuncOp
-getOrDeclareFunction(llvm::StringRef name,
- const Fortran::evaluate::ProcedureDesignator &,
+getOrDeclareFunction(const Fortran::evaluate::ProcedureDesignator &,
Fortran::lower::AbstractConverter &);
/// Return the type of an argument that is a dummy procedure. This may be an
diff --git a/flang/lib/Lower/CallInterface.cpp b/flang/lib/Lower/CallInterface.cpp
index 379d9be0e53a3dc..5299347e561ec77 100644
--- a/flang/lib/Lower/CallInterface.cpp
+++ b/flang/lib/Lower/CallInterface.cpp
@@ -54,10 +54,11 @@ bool Fortran::lower::CallerInterface::hasAlternateReturns() const {
return procRef.hasAlternateReturns();
}
-std::string Fortran::lower::CallerInterface::getMangledName() const {
- const Fortran::evaluate::ProcedureDesignator &proc = procRef.proc();
- // Return the binding label (from BIND(C...)) or the mangled name of the
- // symbol.
+/// Return the binding label (from BIND(C...)) or the mangled name of the
+/// symbol.
+static std::string
+getProcMangledName(const Fortran::evaluate::ProcedureDesignator &proc,
+ Fortran::lower::AbstractConverter &converter) {
if (const Fortran::semantics::Symbol *symbol = proc.GetSymbol())
return converter.mangleName(symbol->GetUltimate());
assert(proc.GetSpecificIntrinsic() &&
@@ -65,6 +66,10 @@ std::string Fortran::lower::CallerInterface::getMangledName() const {
return proc.GetName();
}
+std::string Fortran::lower::CallerInterface::getMangledName() const {
+ return getProcMangledName(procRef.proc(), converter);
+}
+
const Fortran::semantics::Symbol *
Fortran::lower::CallerInterface::getProcedureSymbol() const {
return procRef.proc().GetSymbol();
@@ -127,17 +132,25 @@ Fortran::lower::CallerInterface::getIfIndirectCallSymbol() const {
return nullptr;
}
-mlir::Location Fortran::lower::CallerInterface::getCalleeLocation() const {
- const Fortran::evaluate::ProcedureDesignator &proc = procRef.proc();
- // FIXME: If the callee is defined in the same file but after the current
+static mlir::Location
+getProcedureDesignatorLoc(const Fortran::evaluate::ProcedureDesignator &proc,
+ Fortran::lower::AbstractConverter &converter) {
+ // Note: If the callee is defined in the same file but after the current
// unit we cannot get its location here and the funcOp is created at the
// wrong location (i.e, the caller location).
+ // To prevent this, it is up to the bridge to first declare all functions
+ // defined in the translation unit before lowering any calls or procedure
+ // designator references.
if (const Fortran::semantics::Symbol *symbol = proc.GetSymbol())
return converter.genLocation(symbol->name());
// Use current location for intrinsics.
return converter.getCurrentLocation();
}
+mlir::Location Fortran::lower::CallerInterface::getCalleeLocation() const {
+ return getProcedureDesignatorLoc(procRef.proc(), converter);
+}
+
// Get dummy argument characteristic for a procedure with implicit interface
// from the actual argument characteristic. The actual argument may not be a F77
// entity. The attribute must be dropped and the shape, if any, must be made
@@ -1341,6 +1354,12 @@ class SignatureBuilder
bool isImplicit = forceImplicit || proc.CanBeCalledViaImplicitInterface();
determineInterface(isImplicit, proc);
}
+ SignatureBuilder(const Fortran::evaluate::ProcedureDesignator &procDes,
+ Fortran::lower::AbstractConverter &c)
+ : CallInterface{c}, procDesignator{&procDes},
+ proc{Fortran::evaluate::characteristics::Procedure::Characterize(
+ procDes, converter.getFoldingContext())
+ .value()} {}
/// Does the procedure characteristics being translated have alternate
/// returns ?
bool hasAlternateReturns() const {
@@ -1354,17 +1373,24 @@ class SignatureBuilder
/// This is only here to fulfill CRTP dependencies and should not be called.
std::string getMangledName() const {
- llvm_unreachable("trying to get name from SignatureBuilder");
+ if (procDesignator)
+ return getProcMangledName(*procDesignator, converter);
+ fir::emitFatalError(
+ converter.getCurrentLocation(),
+ "should not query name when only building function type");
}
/// This is only here to fulfill CRTP dependencies and should not be called.
mlir::Location getCalleeLocation() const {
- llvm_unreachable("trying to get callee location from SignatureBuilder");
+ if (procDesignator)
+ return getProcedureDesignatorLoc(*procDesignator, converter);
+ return converter.getCurrentLocation();
}
- /// This is only here to fulfill CRTP dependencies and should not be called.
const Fortran::semantics::Symbol *getProcedureSymbol() const {
- llvm_unreachable("trying to get callee symbol from SignatureBuilder");
+ if (procDesignator)
+ return procDesignator->GetSymbol();
+ return nullptr;
};
Fortran::evaluate::characteristics::Procedure characterize() const {
@@ -1380,11 +1406,37 @@ class SignatureBuilder
return proc;
}
+ /// Set internal procedure attribute on MLIR function. Internal procedure
+ /// are defined in the current file and will not go through SignatureBuilder.
+ void setFuncAttrs(mlir::func::FuncOp) const {}
+
/// This is not the description of an indirect call.
static constexpr bool isIndirectCall() { return false; }
/// Return the translated signature.
- mlir::FunctionType getFunctionType() { return genFunctionType(); }
+ mlir::FunctionType getFunctionType() {
+ if (interfaceDetermined)
+ fir::emitFatalError(converter.getCurrentLocation(),
+ "SignatureBuilder should only be used once");
+ // Most unrestricted intrinsic characteristics have the Elemental attribute
+ // which triggers CanBeCalledViaImplicitInterface to return false. However,
+ // using implicit interface rules is just fine here.
+ bool forceImplicit =
+ procDesignator && procDesignator->GetSpecificIntrinsic();
+ bool isImplicit = forceImplicit || proc.CanBeCalledViaImplicitInterface();
+ determineInterface(isImplicit, proc);
+ interfaceDetermined = true;
+ return genFunctionType();
+ }
+
+ mlir::func::FuncOp getOrCreateFuncOp() {
+ if (interfaceDetermined)
+ fir::emitFatalError(converter.getCurrentLocation(),
+ "SignatureBuilder should only be used once");
+ declare();
+ interfaceDetermined = true;
+ return getFuncOp();
+ }
// Copy of base implementation.
static constexpr bool hasHostAssociated() { return false; }
@@ -1393,47 +1445,30 @@ class SignatureBuilder
}
private:
- const Fortran::evaluate::characteristics::Procedure &proc;
+ const Fortran::evaluate::ProcedureDesignator *procDesignator = nullptr;
+ Fortran::evaluate::characteristics::Procedure proc;
+ bool interfaceDetermined = false;
};
mlir::FunctionType Fortran::lower::translateSignature(
const Fortran::evaluate::ProcedureDesignator &proc,
Fortran::lower::AbstractConverter &converter) {
- std::optional<Fortran::evaluate::characteristics::Procedure> characteristics =
- Fortran::evaluate::characteristics::Procedure::Characterize(
- proc, converter.getFoldingContext());
- // Most unrestricted intrinsic characteristic has the Elemental attribute
- // which triggers CanBeCalledViaImplicitInterface to return false. However,
- // using implicit interface rules is just fine here.
- bool forceImplicit = proc.GetSpecificIntrinsic();
- return SignatureBuilder{characteristics.value(), converter, forceImplicit}
- .getFunctionType();
+ return SignatureBuilder{proc, converter}.getFunctionType();
}
mlir::func::FuncOp Fortran::lower::getOrDeclareFunction(
- llvm::StringRef name, const Fortran::evaluate::ProcedureDesignator &proc,
+ const Fortran::evaluate::ProcedureDesignator &proc,
Fortran::lower::AbstractConverter &converter) {
mlir::ModuleOp module = converter.getModuleOp();
+ std::string name = getProcMangledName(proc, converter);
mlir::func::FuncOp func = fir::FirOpBuilder::getNamedFunction(module, name);
if (func)
return func;
- const Fortran::semantics::Symbol *symbol = proc.GetSymbol();
- assert(symbol && "non user function in getOrDeclareFunction");
// getOrDeclareFunction is only used for functions not defined in the current
// program unit, so use the location of the procedure designator symbol, which
// is the first occurrence of the procedure in the program unit.
- mlir::Location loc = converter.genLocation(symbol->name());
- std::optional<Fortran::evaluate::characteristics::Procedure> characteristics =
- Fortran::evaluate::characteristics::Procedure::Characterize(
- proc, converter.getFoldingContext());
- mlir::FunctionType ty = SignatureBuilder{characteristics.value(), converter,
- /*forceImplicit=*/false}
- .getFunctionType();
- mlir::func::FuncOp newFunc =
- fir::FirOpBuilder::createFunction(loc, module, name, ty);
- addSymbolAttribute(newFunc, *symbol, converter.getMLIRContext());
- return newFunc;
+ return SignatureBuilder{proc, converter}.getOrCreateFuncOp();
}
// Is it required to pass a dummy procedure with \p characteristics as a tuple
diff --git a/flang/lib/Lower/ConvertProcedureDesignator.cpp b/flang/lib/Lower/ConvertProcedureDesignator.cpp
index aa5a7fe0ce5c5a3..20ade1a04049fc4 100644
--- a/flang/lib/Lower/ConvertProcedureDesignator.cpp
+++ b/flang/lib/Lower/ConvertProcedureDesignator.cpp
@@ -62,11 +62,11 @@ fir::ExtendedValue Fortran::lower::convertProcedureDesignator(
std::tie(funcPtr, funcPtrResultLength) =
fir::factory::extractCharacterProcedureTuple(builder, loc, funcPtr);
} else {
- std::string name = converter.mangleName(*symbol);
mlir::func::FuncOp func =
- Fortran::lower::getOrDeclareFunction(name, proc, converter);
- funcPtr = builder.create<fir::AddrOfOp>(loc, func.getFunctionType(),
- builder.getSymbolRefAttr(name));
+ Fortran::lower::getOrDeclareFunction(proc, converter);
+ mlir::SymbolRefAttr nameAttr = builder.getSymbolRefAttr(func.getSymName());
+ funcPtr =
+ builder.create<fir::AddrOfOp>(loc, func.getFunctionType(), nameAttr);
}
if (Fortran::lower::mustPassLengthWithDummyProcedure(proc, converter)) {
// The result length, if available here, must be propagated along the
diff --git a/flang/lib/Lower/IO.cpp b/flang/lib/Lower/IO.cpp
index 48f2baa2e4f4ed2..94135bb570ffbc1 100644
--- a/flang/lib/Lower/IO.cpp
+++ b/flang/lib/Lower/IO.cpp
@@ -362,17 +362,14 @@ getNonTbpDefinedIoTableAddr(Fortran::lower::AbstractConverter &converter,
Fortran::evaluate::ProcedureDesignator{*procSym}},
stmtCtx))));
} else {
- std::string procName = converter.mangleName(*procSym);
- mlir::func::FuncOp procDef = builder.getNamedFunction(procName);
- if (!procDef)
- procDef = Fortran::lower::getOrDeclareFunction(
- procName, Fortran::evaluate::ProcedureDesignator{*procSym},
- converter);
- insert(
- builder.createConvert(loc, refTy,
- builder.create<fir::AddrOfOp>(
- loc, procDef.getFunctionType(),
- builder.getSymbolRefAttr(procName))));
+ mlir::func::FuncOp procDef = Fortran::lower::getOrDeclareFunction(
+ Fortran::evaluate::ProcedureDesignator{*procSym}, converter);
+ mlir::SymbolRefAttr nameAttr =
+ builder.getSymbolRefAttr(procDef.getSymName());
+ insert(builder.createConvert(
+ loc, refTy,
+ builder.create<fir::AddrOfOp>(loc, procDef.getFunctionType(),
+ nameAttr)));
}
} else {
insert(builder.createNullConstant(loc, refTy));
diff --git a/flang/test/Lower/HLFIR/procedure-designators-arg-attrs.f90 b/flang/test/Lower/HLFIR/procedure-designators-arg-attrs.f90
new file mode 100644
index 000000000000000..091d4185b5095bb
--- /dev/null
+++ b/flang/test/Lower/HLFIR/procedure-designators-arg-attrs.f90
@@ -0,0 +1,17 @@
+! Ensure that func.func arguments are given the Fortran attributes
+! even if their first use is in a procedure designator reference
+! and not a call.
+
+! RUN: bbc -emit-hlfir -o - %s | FileCheck %s
+
+subroutine test(x)
+ interface
+ subroutine foo(x)
+ integer, optional, target :: x
+ end subroutine
+ end interface
+ integer, optional, target :: x
+ call takes_proc(foo)
+ call foo(x)
+end subroutine
+! CHECK: func.func private @_QPfoo(!fir.ref<i32> {fir.optional, fir.target})
More information about the flang-commits
mailing list