Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[flang] Set func.func arg attributes for procedure designators #68420

Merged
merged 1 commit into from
Oct 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions flang/include/flang/Lower/CallInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
107 changes: 71 additions & 36 deletions flang/lib/Lower/CallInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,22 @@ 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() &&
"expected intrinsic procedure in designator");
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();
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand All @@ -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 {
Expand All @@ -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; }
Expand All @@ -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
Expand Down
8 changes: 4 additions & 4 deletions flang/lib/Lower/ConvertProcedureDesignator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 8 additions & 11 deletions flang/lib/Lower/IO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
17 changes: 17 additions & 0 deletions flang/test/Lower/HLFIR/procedure-designators-arg-attrs.f90
Original file line number Diff line number Diff line change
@@ -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})