Skip to content

Commit

Permalink
[flang] Lower basic function with scalar integer/logical return value
Browse files Browse the repository at this point in the history
This patch allows the lowring of simple empty function with a
scalar integer or logical return value.
The code in ConvertType.cpp is cleaned up as well. This file was landed
together with the initial flang push and lowering was still a prototype
at that time. Some more cleaning will come with follow up patches.

This patch is part of the upstreaming effort from fir-dev branch.

Reviewed By: PeteSteinfeld

Differential Revision: https://reviews.llvm.org/D119698

Co-authored-by: Jean Perier <jperier@nvidia.com>
  • Loading branch information
clementval and jeanPerier committed Feb 14, 2022
1 parent d238acd commit ad40cc1
Show file tree
Hide file tree
Showing 5 changed files with 368 additions and 195 deletions.
29 changes: 29 additions & 0 deletions flang/include/flang/Lower/CallInterface.h
Expand Up @@ -102,9 +102,31 @@ class CallInterface {
using FortranEntity = typename PassedEntityTypes<T>::FortranEntity;
using FirValue = typename PassedEntityTypes<T>::FirValue;

/// FirPlaceHolder are place holders for the mlir inputs and outputs that are
/// created during the first pass before the mlir::FuncOp is created.
struct FirPlaceHolder {
FirPlaceHolder(mlir::Type t, int passedPosition, Property p)
: type{t}, passedEntityPosition{passedPosition}, property{p} {}
/// Type for this input/output
mlir::Type type;
/// Position of related passedEntity in passedArguments.
/// (passedEntity is the passedResult this value is resultEntityPosition).
int passedEntityPosition;
static constexpr int resultEntityPosition = -1;
/// Indicate property of the entity passedEntityPosition that must be passed
/// through this argument.
Property property;
};

/// Returns the mlir function type
mlir::FunctionType genFunctionType();

/// determineInterface is the entry point of the first pass that defines the
/// interface and is required to get the mlir::FuncOp.
void
determineInterface(bool isImplicit,
const Fortran::evaluate::characteristics::Procedure &);

protected:
CallInterface(Fortran::lower::AbstractConverter &c) : converter{c} {}
/// CRTP handle.
Expand All @@ -113,9 +135,14 @@ class CallInterface {
/// create/find the mlir::FuncOp. Child needs to be initialized first.
void declare();

llvm::SmallVector<FirPlaceHolder> outputs;
mlir::FuncOp func;

Fortran::lower::AbstractConverter &converter;
/// Store characteristic once created, it is required for further information
/// (e.g. getting the length of character result)
std::optional<Fortran::evaluate::characteristics::Procedure> characteristic =
std::nullopt;
};

//===----------------------------------------------------------------------===//
Expand All @@ -132,9 +159,11 @@ class CalleeInterface : public CallInterface<CalleeInterface> {
declare();
}

bool hasAlternateReturns() const;
std::string getMangledName() const;
mlir::Location getCalleeLocation() const;
Fortran::evaluate::characteristics::Procedure characterize() const;
bool isMainProgram() const;

/// On the callee side it does not matter whether the procedure is
/// called through pointers or not.
Expand Down
47 changes: 38 additions & 9 deletions flang/lib/Lower/Bridge.cpp
Expand Up @@ -99,9 +99,8 @@ class FirConverter : public Fortran::lower::AbstractConverter {
TODO_NOLOC("Not implemented genType SomeExpr. Needed for more complex "
"expression lowering");
}
mlir::Type genType(Fortran::lower::SymbolRef) override final {
TODO_NOLOC("Not implemented genType SymbolRef. Needed for more complex "
"expression lowering");
mlir::Type genType(Fortran::lower::SymbolRef sym) override final {
return Fortran::lower::translateSymbolToFIRType(*this, sym);
}
mlir::Type genType(Fortran::common::TypeCategory tc) override final {
TODO_NOLOC("Not implemented genType TypeCategory. Needed for more complex "
Expand Down Expand Up @@ -247,8 +246,11 @@ class FirConverter : public Fortran::lower::AbstractConverter {
for (const Fortran::lower::pft::Variable &var :
funit.getOrderedSymbolTable()) {
const Fortran::semantics::Symbol &sym = var.getSymbol();
if (!sym.IsFuncResult() || !funit.primaryResult)
if (!sym.IsFuncResult() || !funit.primaryResult) {
instantiateVar(var);
} else if (&sym == funit.primaryResult) {
instantiateVar(var);
}
}

// Create most function blocks in advance.
Expand Down Expand Up @@ -335,6 +337,36 @@ class FirConverter : public Fortran::lower::AbstractConverter {
}
void genFIR(const Fortran::parser::EndProgramStmt &) { genExitRoutine(); }

/// END of procedure-like constructs
///
/// Generate the cleanup block before the procedure exits
void genReturnSymbol(const Fortran::semantics::Symbol &functionSymbol) {
const Fortran::semantics::Symbol &resultSym =
functionSymbol.get<Fortran::semantics::SubprogramDetails>().result();
Fortran::lower::SymbolBox resultSymBox = lookupSymbol(resultSym);
mlir::Location loc = toLocation();
if (!resultSymBox) {
mlir::emitError(loc, "failed lowering function return");
return;
}
mlir::Value resultVal = resultSymBox.match(
[&](const fir::CharBoxValue &x) -> mlir::Value {
TODO(loc, "Function return CharBoxValue");
},
[&](const auto &) -> mlir::Value {
mlir::Value resultRef = resultSymBox.getAddr();
mlir::Type resultType = genType(resultSym);
mlir::Type resultRefType = builder->getRefType(resultType);
// A function with multiple entry points returning different types
// tags all result variables with one of the largest types to allow
// them to share the same storage. Convert this to the actual type.
if (resultRef.getType() != resultRefType)
TODO(loc, "Convert to actual type");
return builder->create<fir::LoadOp>(loc, resultRef);
});
builder->create<mlir::ReturnOp>(loc, resultVal);
}

void genFIRProcedureExit(Fortran::lower::pft::FunctionLikeUnit &funit,
const Fortran::semantics::Symbol &symbol) {
if (mlir::Block *finalBlock = funit.finalBlock) {
Expand All @@ -345,7 +377,7 @@ class FirConverter : public Fortran::lower::AbstractConverter {
builder->setInsertionPoint(finalBlock, finalBlock->end());
}
if (Fortran::semantics::IsFunction(symbol)) {
TODO(toLocation(), "Function lowering");
genReturnSymbol(symbol);
} else {
genExitRoutine();
}
Expand Down Expand Up @@ -719,10 +751,6 @@ class FirConverter : public Fortran::lower::AbstractConverter {
TODO(toLocation(), "EndDoStmt lowering");
}

void genFIR(const Fortran::parser::EndFunctionStmt &) {
TODO(toLocation(), "EndFunctionStmt lowering");
}

void genFIR(const Fortran::parser::EndIfStmt &) {
TODO(toLocation(), "EndIfStmt lowering");
}
Expand All @@ -736,6 +764,7 @@ class FirConverter : public Fortran::lower::AbstractConverter {
}

// Nop statements - No code, or code is generated at the construct level.
void genFIR(const Fortran::parser::EndFunctionStmt &) {} // nop
void genFIR(const Fortran::parser::EndSubroutineStmt &) {} // nop

void genFIR(const Fortran::parser::EntryStmt &) {
Expand Down
111 changes: 110 additions & 1 deletion flang/lib/Lower/CallInterface.cpp
Expand Up @@ -12,6 +12,7 @@
#include "flang/Lower/Mangler.h"
#include "flang/Lower/PFTBuilder.h"
#include "flang/Lower/Support/Utils.h"
#include "flang/Lower/Todo.h"
#include "flang/Optimizer/Builder/FIRBuilder.h"
#include "flang/Optimizer/Dialect/FIRDialect.h"
#include "flang/Optimizer/Dialect/FIROpsSupport.h"
Expand All @@ -33,6 +34,11 @@ static std::string getMangledName(const Fortran::semantics::Symbol &symbol) {
// Callee side interface implementation
//===----------------------------------------------------------------------===//

bool Fortran::lower::CalleeInterface::hasAlternateReturns() const {
return !funit.isMainProgram() &&
Fortran::semantics::HasAlternateReturns(funit.getSubprogramSymbol());
}

std::string Fortran::lower::CalleeInterface::getMangledName() const {
if (funit.isMainProgram())
return fir::NameUniquer::doProgramEntry().str();
Expand All @@ -52,6 +58,21 @@ mlir::Location Fortran::lower::CalleeInterface::getCalleeLocation() const {
return converter.genLocation(funit.getStartingSourceLoc());
}

Fortran::evaluate::characteristics::Procedure
Fortran::lower::CalleeInterface::characterize() const {
Fortran::evaluate::FoldingContext &foldingContext =
converter.getFoldingContext();
std::optional<Fortran::evaluate::characteristics::Procedure> characteristic =
Fortran::evaluate::characteristics::Procedure::Characterize(
funit.getSubprogramSymbol(), foldingContext);
assert(characteristic && "Fail to get characteristic from symbol");
return *characteristic;
}

bool Fortran::lower::CalleeInterface::isMainProgram() const {
return funit.isMainProgram();
}

mlir::FuncOp Fortran::lower::CalleeInterface::addEntryBlockAndMapArguments() {
// On the callee side, directly map the mlir::value argument of
// the function block to the Fortran symbols.
Expand Down Expand Up @@ -81,6 +102,13 @@ static void addSymbolAttribute(mlir::FuncOp func,
/// signature and building/finding the mlir::FuncOp.
template <typename T>
void Fortran::lower::CallInterface<T>::declare() {
if (!side().isMainProgram()) {
characteristic.emplace(side().characterize());
bool isImplicit = characteristic->CanBeCalledViaImplicitInterface();
determineInterface(isImplicit, *characteristic);
}
// No input/output for main program

// Create / get funcOp for direct calls. For indirect calls (only meaningful
// on the caller side), no funcOp has to be created here. The mlir::Value
// holding the indirection is used when creating the fir::CallOp.
Expand All @@ -98,9 +126,90 @@ void Fortran::lower::CallInterface<T>::declare() {
}
}

//===----------------------------------------------------------------------===//
// CallInterface implementation: this part is common to both caller and caller
// sides.
//===----------------------------------------------------------------------===//

/// This is the actual part that defines the FIR interface based on the
/// characteristic. It directly mutates the CallInterface members.
template <typename T>
class Fortran::lower::CallInterfaceImpl {
using CallInterface = Fortran::lower::CallInterface<T>;
using FirPlaceHolder = typename CallInterface::FirPlaceHolder;
using Property = typename CallInterface::Property;
using TypeAndShape = Fortran::evaluate::characteristics::TypeAndShape;

public:
CallInterfaceImpl(CallInterface &i)
: interface(i), mlirContext{i.converter.getMLIRContext()} {}

void buildImplicitInterface(
const Fortran::evaluate::characteristics::Procedure &procedure) {
// Handle result
if (const std::optional<Fortran::evaluate::characteristics::FunctionResult>
&result = procedure.functionResult)
handleImplicitResult(*result);
else if (interface.side().hasAlternateReturns())
addFirResult(mlir::IndexType::get(&mlirContext),
FirPlaceHolder::resultEntityPosition, Property::Value);
}

private:
void handleImplicitResult(
const Fortran::evaluate::characteristics::FunctionResult &result) {
if (result.IsProcedurePointer())
TODO(interface.converter.getCurrentLocation(),
"procedure pointer result not yet handled");
const Fortran::evaluate::characteristics::TypeAndShape *typeAndShape =
result.GetTypeAndShape();
assert(typeAndShape && "expect type for non proc pointer result");
Fortran::evaluate::DynamicType dynamicType = typeAndShape->type();
if (dynamicType.category() == Fortran::common::TypeCategory::Character) {
TODO(interface.converter.getCurrentLocation(),
"implicit result character type");
} else if (dynamicType.category() ==
Fortran::common::TypeCategory::Derived) {
TODO(interface.converter.getCurrentLocation(),
"implicit result derived type");
} else {
// All result other than characters/derived are simply returned by value
// in implicit interfaces
mlir::Type mlirType =
getConverter().genType(dynamicType.category(), dynamicType.kind());
addFirResult(mlirType, FirPlaceHolder::resultEntityPosition,
Property::Value);
}
}

void addFirResult(mlir::Type type, int entityPosition, Property p) {
interface.outputs.emplace_back(FirPlaceHolder{type, entityPosition, p});
}

Fortran::lower::AbstractConverter &getConverter() {
return interface.converter;
}
CallInterface &interface;
mlir::MLIRContext &mlirContext;
};

template <typename T>
void Fortran::lower::CallInterface<T>::determineInterface(
bool isImplicit,
const Fortran::evaluate::characteristics::Procedure &procedure) {
CallInterfaceImpl<T> impl(*this);
if (isImplicit)
impl.buildImplicitInterface(procedure);
else
TODO_NOLOC("determineImplicitInterface");
}

template <typename T>
mlir::FunctionType Fortran::lower::CallInterface<T>::genFunctionType() {
return mlir::FunctionType::get(&converter.getMLIRContext(), {}, {});
llvm::SmallVector<mlir::Type> returnTys;
for (const FirPlaceHolder &placeHolder : outputs)
returnTys.emplace_back(placeHolder.type);
return mlir::FunctionType::get(&converter.getMLIRContext(), {}, returnTys);
}

template class Fortran::lower::CallInterface<Fortran::lower::CalleeInterface>;

0 comments on commit ad40cc1

Please sign in to comment.