[flang] Create fir.dispatch_table and fir.dt_entry operations

Create the fir.dispatch_table operation based on semantics
information. The fir.dispatch_table will be used for static devirtualization
as well as for fir.select_type conversion.

Depends on D138129

Reviewed By: jeanPerier, PeteSteinfeld

Differential Revision: https://reviews.llvm.org/D138131
This commit is contained in:
Valentin Clement 2022-11-17 10:53:13 +01:00
parent ded52a44d0
commit 6393d2ea24
No known key found for this signature in database
GPG Key ID: 086D54783C928776
11 changed files with 227 additions and 37 deletions

View File

@ -204,6 +204,10 @@ public:
virtual void registerRuntimeTypeInfo(mlir::Location loc,
SymbolRef typeInfoSym) = 0;
virtual void registerDispatchTableInfo(
mlir::Location loc,
const Fortran::semantics::DerivedTypeSpec *typeSpec) = 0;
//===--------------------------------------------------------------------===//
// Locations
//===--------------------------------------------------------------------===//

View File

@ -212,6 +212,11 @@ public:
bodyBuilder, linkage);
}
/// Create a fir::DispatchTable operation.
fir::DispatchTableOp createDispatchTableOp(mlir::Location loc,
llvm::StringRef name,
llvm::StringRef parentName);
/// Convert a StringRef string into a fir::StringLitOp.
fir::StringLitOp createStringLitOp(mlir::Location loc,
llvm::StringRef string);

View File

@ -2804,26 +2804,30 @@ def fir_DispatchTableOp : fir_Op<"dispatch_table",
```
}];
let arguments = (ins
SymbolNameAttr:$sym_name,
OptionalAttr<StrAttr>:$parent
);
let hasCustomAssemblyFormat = 1;
let hasVerifier = 1;
let regions = (region SizedRegion<1>:$region);
let regions = (region AnyRegion:$region);
let skipDefaultBuilders = 1;
let builders = [
OpBuilder<(ins "llvm::StringRef":$name, "mlir::Type":$type,
CArg<"llvm::ArrayRef<mlir::NamedAttribute>", "{}">:$attrs),
[{
$_state.addAttribute(mlir::SymbolTable::getSymbolAttrName(),
$_builder.getStringAttr(name));
$_state.addAttributes(attrs);
}]>
"llvm::StringRef":$parent,
CArg<"llvm::ArrayRef<mlir::NamedAttribute>", "{}">:$attrs)>
];
let extraClassDeclaration = [{
/// Append a dispatch table entry to the table.
void appendTableEntry(mlir::Operation *op);
static constexpr llvm::StringRef getParentAttrNameStr() { return "parent"; }
static constexpr llvm::StringRef getExtendsKeyword() { return "extends"; }
mlir::Block &getBlock() {
return getRegion().front();
}

View File

@ -46,11 +46,13 @@
#include "flang/Optimizer/Transforms/Passes.h"
#include "flang/Parser/parse-tree.h"
#include "flang/Runtime/iostat.h"
#include "flang/Semantics/runtime-type-info.h"
#include "flang/Semantics/tools.h"
#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
#include "mlir/IR/PatternMatch.h"
#include "mlir/Parser/Parser.h"
#include "mlir/Transforms/RegionUtils.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
@ -193,6 +195,67 @@ private:
llvm::SmallSetVector<Fortran::semantics::SymbolRef, 64> seen;
};
class DispatchTableConverter {
struct DispatchTableInfo {
const Fortran::semantics::DerivedTypeSpec *typeSpec;
mlir::Location loc;
};
public:
void registerTypeSpec(mlir::Location loc,
const Fortran::semantics::DerivedTypeSpec *typeSpec) {
assert(typeSpec && "type spec is null");
std::string dtName = Fortran::lower::mangle::mangleName(*typeSpec);
if (seen.contains(dtName) || dtName.find("__fortran") != std::string::npos)
return;
seen.insert(dtName);
registeredDispatchTableInfo.emplace_back(DispatchTableInfo{typeSpec, loc});
}
void createDispatchTableOps(Fortran::lower::AbstractConverter &converter) {
for (const DispatchTableInfo &info : registeredDispatchTableInfo) {
std::string dtName = Fortran::lower::mangle::mangleName(*info.typeSpec);
const Fortran::semantics::DerivedTypeSpec *parent =
Fortran::evaluate::GetParentTypeSpec(*info.typeSpec);
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
fir::DispatchTableOp dt = builder.createDispatchTableOp(
info.loc, dtName,
parent ? Fortran::lower::mangle::mangleName(*parent) : "");
auto insertPt = builder.saveInsertionPoint();
std::vector<const Fortran::semantics::Symbol *> bindings =
Fortran::semantics::CollectBindings(*info.typeSpec->scope());
if (!bindings.empty())
builder.createBlock(&dt.getRegion());
for (const Fortran::semantics::Symbol *binding : bindings) {
const auto *details =
binding->detailsIf<Fortran::semantics::ProcBindingDetails>();
std::string bindingName =
Fortran::lower::mangle::mangleName(details->symbol());
builder.create<fir::DTEntryOp>(
info.loc,
mlir::StringAttr::get(builder.getContext(),
binding->name().ToString()),
mlir::SymbolRefAttr::get(builder.getContext(), bindingName));
}
if (!bindings.empty())
builder.create<fir::FirEndOp>(info.loc);
builder.restoreInsertionPoint(insertPt);
}
registeredDispatchTableInfo.clear();
}
private:
/// Store the semantic DerivedTypeSpec that will be required to generate the
/// dispatch table.
llvm::SmallVector<DispatchTableInfo> registeredDispatchTableInfo;
/// Track processed type specs to avoid multiple creation.
llvm::StringSet<> seen;
};
using IncrementLoopNestInfo = llvm::SmallVector<IncrementLoopInfo, 8>;
} // namespace
@ -270,6 +333,10 @@ public:
createGlobalOutsideOfFunctionLowering(
[&]() { runtimeTypeInfoConverter.createTypeInfoGlobals(*this); });
/// Create the dispatch tables for derived types.
createGlobalOutsideOfFunctionLowering(
[&]() { dispatchTableConverter.createDispatchTableOps(*this); });
// Create the list of any environment defaults for the runtime to set. The
// runtime default list is only created if there is a main program to ensure
// it only happens once and to provide consistent results if multiple files
@ -745,6 +812,12 @@ public:
runtimeTypeInfoConverter.registerTypeInfoSymbol(*this, loc, typeInfoSym);
}
void registerDispatchTableInfo(
mlir::Location loc,
const Fortran::semantics::DerivedTypeSpec *typeSpec) override final {
dispatchTableConverter.registerTypeSpec(loc, typeSpec);
}
private:
FirConverter() = delete;
FirConverter(const FirConverter &) = delete;
@ -3591,6 +3664,7 @@ private:
Fortran::lower::SymMap localSymbols;
Fortran::parser::CharBlock currentPosition;
RuntimeTypeInfoConverter runtimeTypeInfoConverter;
DispatchTableConverter dispatchTableConverter;
/// WHERE statement/construct mask expression stack.
Fortran::lower::ImplicitIterSpace implicitIterSpace;

View File

@ -340,6 +340,8 @@ struct TypeBuilder {
}
LLVM_DEBUG(llvm::dbgs() << "derived type: " << rec << '\n');
converter.registerDispatchTableInfo(loc, &tySpec);
// Generate the type descriptor object if any
if (const Fortran::semantics::Scope *derivedScope =
tySpec.scope() ? tySpec.scope() : tySpec.typeSymbol().scope())

View File

@ -271,6 +271,18 @@ fir::GlobalOp fir::FirOpBuilder::createGlobal(
return glob;
}
fir::DispatchTableOp fir::FirOpBuilder::createDispatchTableOp(
mlir::Location loc, llvm::StringRef name, llvm::StringRef parentName) {
auto module = getModule();
auto insertPt = saveInsertionPoint();
if (auto dt = module.lookupSymbol<fir::DispatchTableOp>(name))
return dt;
setInsertionPoint(module.getBody(), module.getBody()->end());
auto dt = create<fir::DispatchTableOp>(loc, name, mlir::Type{}, parentName);
restoreInsertionPoint(insertPt);
return dt;
}
mlir::Value
fir::FirOpBuilder::convertWithSemantics(mlir::Location loc, mlir::Type toTy,
mlir::Value val,

View File

@ -1051,30 +1051,30 @@ struct DispatchOpConversion : public FIROpConversion<fir::DispatchOp> {
}
};
/// Lower `fir.dispatch_table` operation. The dispatch table for a Fortran
/// derived type.
/// `fir.disptach_table` operation has no specific CodeGen. The operation is
/// only used to carry information during FIR to FIR passes.
struct DispatchTableOpConversion
: public FIROpConversion<fir::DispatchTableOp> {
using FIROpConversion::FIROpConversion;
mlir::LogicalResult
matchAndRewrite(fir::DispatchTableOp dispTab, OpAdaptor adaptor,
matchAndRewrite(fir::DispatchTableOp op, OpAdaptor,
mlir::ConversionPatternRewriter &rewriter) const override {
TODO(dispTab.getLoc(), "fir.dispatch_table codegen");
return mlir::failure();
rewriter.eraseOp(op);
return mlir::success();
}
};
/// Lower `fir.dt_entry` operation. An entry in a dispatch table; binds a
/// method-name to a function.
/// `fir.dt_entry` operation has no specific CodeGen. The operation is only used
/// to carry information during FIR to FIR passes.
struct DTEntryOpConversion : public FIROpConversion<fir::DTEntryOp> {
using FIROpConversion::FIROpConversion;
mlir::LogicalResult
matchAndRewrite(fir::DTEntryOp dtEnt, OpAdaptor adaptor,
matchAndRewrite(fir::DTEntryOp op, OpAdaptor,
mlir::ConversionPatternRewriter &rewriter) const override {
TODO(dtEnt.getLoc(), "fir.dt_entry codegen");
return mlir::failure();
rewriter.eraseOp(op);
return mlir::success();
}
};

View File

@ -1092,14 +1092,19 @@ void fir::DispatchTableOp::appendTableEntry(mlir::Operation *op) {
mlir::ParseResult fir::DispatchTableOp::parse(mlir::OpAsmParser &parser,
mlir::OperationState &result) {
// Parse the name as a symbol reference attribute.
mlir::SymbolRefAttr nameAttr;
if (parser.parseAttribute(nameAttr, mlir::SymbolTable::getSymbolAttrName(),
result.attributes))
mlir::StringAttr nameAttr;
if (parser.parseSymbolName(nameAttr, mlir::SymbolTable::getSymbolAttrName(),
result.attributes))
return mlir::failure();
// Convert the parsed name attr into a string attr.
result.attributes.set(mlir::SymbolTable::getSymbolAttrName(),
nameAttr.getRootReference());
if (!failed(parser.parseOptionalKeyword(getExtendsKeyword()))) {
mlir::StringAttr parent;
if (parser.parseLParen() ||
parser.parseAttribute(parent, getParentAttrNameStr(),
result.attributes) ||
parser.parseRParen())
return mlir::failure();
}
// Parse the optional table body.
mlir::Region *body = result.addRegion();
@ -1113,11 +1118,11 @@ mlir::ParseResult fir::DispatchTableOp::parse(mlir::OpAsmParser &parser,
}
void fir::DispatchTableOp::print(mlir::OpAsmPrinter &p) {
auto tableName = getOperation()
->getAttrOfType<mlir::StringAttr>(
mlir::SymbolTable::getSymbolAttrName())
.getValue();
p << " @" << tableName;
p << ' ';
p.printSymbolName(getSymName());
if (getParent())
p << ' ' << getExtendsKeyword() << '('
<< (*this)->getAttr(getParentAttrNameStr()) << ')';
mlir::Region &body = getOperation()->getRegion(0);
if (!body.empty()) {
@ -1128,12 +1133,29 @@ void fir::DispatchTableOp::print(mlir::OpAsmPrinter &p) {
}
mlir::LogicalResult fir::DispatchTableOp::verify() {
if (getRegion().empty())
return mlir::success();
for (auto &op : getBlock())
if (!mlir::isa<fir::DTEntryOp, fir::FirEndOp>(op))
return op.emitOpError("dispatch table must contain dt_entry");
return mlir::success();
}
void fir::DispatchTableOp::build(mlir::OpBuilder &builder,
mlir::OperationState &result,
llvm::StringRef name, mlir::Type type,
llvm::StringRef parent,
llvm::ArrayRef<mlir::NamedAttribute> attrs) {
result.addRegion();
result.addAttribute(mlir::SymbolTable::getSymbolAttrName(),
builder.getStringAttr(name));
if (!parent.empty())
result.addAttribute(getParentAttrNameStr(), builder.getStringAttr(parent));
// result.addAttribute(getSymbolAttrNameStr(),
// mlir::SymbolRefAttr::get(builder.getContext(), name));
result.addAttributes(attrs);
}
//===----------------------------------------------------------------------===//
// EmboxOp
//===----------------------------------------------------------------------===//

View File

@ -1,9 +0,0 @@
// RUN: %not_todo_cmd fir-opt --fir-to-llvm-ir="target=x86_64-unknown-linux-gnu" %s 2>&1 | FileCheck %s
// Test fir.dispatch_table conversion to llvm.
// Not implemented yet.
// CHECK: not yet implemented: fir.dispatch_table codegen
fir.dispatch_table @dispatch_tbl {
fir.dt_entry "method", @method_impl
}

View File

@ -0,0 +1,75 @@
! RUN: bbc -polymorphic-type -emit-fir %s -o - | FileCheck %s
! Tests the generation of fir.dispatch_table operations.
module polymorphic_types
type p1
integer :: a
integer :: b
contains
procedure :: proc1 => proc1_p1
procedure :: aproc
procedure :: zproc
end type
type, extends(p1) :: p2
integer :: c
contains
procedure :: proc1 => proc1_p2
procedure :: aproc2
end type
type, extends(p2) :: p3
integer :: d
contains
procedure :: aproc3
end type
contains
subroutine proc1_p1(p)
class(p1) :: p
end subroutine
subroutine aproc(p)
class(p1) :: p
end subroutine
subroutine zproc(p)
class(p1) :: p
end subroutine
subroutine proc1_p2(p)
class(p2) :: p
end subroutine
subroutine aproc2(p)
class(p2) :: p
end subroutine
subroutine aproc3(p)
class(p3) :: p
end subroutine
end module
! CHECK-LABEL: fir.dispatch_table @_QMpolymorphic_typesTp1 {
! CHECK: fir.dt_entry "aproc", @_QMpolymorphic_typesPaproc
! CHECK: fir.dt_entry "proc1", @_QMpolymorphic_typesPproc1_p1
! CHECK: fir.dt_entry "zproc", @_QMpolymorphic_typesPzproc
! CHECK: }
! CHECK-LABEL: fir.dispatch_table @_QMpolymorphic_typesTp2 extends("_QMpolymorphic_typesTp1") {
! CHECK: fir.dt_entry "aproc", @_QMpolymorphic_typesPaproc
! CHECK: fir.dt_entry "proc1", @_QMpolymorphic_typesPproc1_p2
! CHECK: fir.dt_entry "zproc", @_QMpolymorphic_typesPzproc
! CHECK: fir.dt_entry "aproc2", @_QMpolymorphic_typesPaproc2
! CHECK: }
! CHECK-LABEL: fir.dispatch_table @_QMpolymorphic_typesTp3 extends("_QMpolymorphic_typesTp2") {
! CHECK: fir.dt_entry "aproc", @_QMpolymorphic_typesPaproc
! CHECK: fir.dt_entry "proc1", @_QMpolymorphic_typesPproc1_p2
! CHECK: fir.dt_entry "zproc", @_QMpolymorphic_typesPzproc
! CHECK: fir.dt_entry "aproc2", @_QMpolymorphic_typesPaproc2
! CHECK: fir.dt_entry "aproc3", @_QMpolymorphic_typesPaproc3
! CHECK: }

View File

@ -180,4 +180,5 @@ contains
! CHECK-LABEL: func.func @assumed_type_dummy_array(
! CHECK-SAME: %{{.*}}: !fir.box<!fir.array<?xnone>>
end module