[flang] add option to generate runtime type info as external (#145901)
So far flang generates runtime derived type info global definitions (as opposed to declarations) for all the types used in the current compilation unit even when the derived types are defined in other compilation units. It is using linkonce_odr to achieve derived type descriptor address "uniqueness" aspect needed to match two derived type inside the runtime. This comes at a big compile time cost because of all the extra globals and their definitions in apps with many and complex derived types. This patch adds and experimental option to only generate the rtti definition for the types defined in the current compilation unit and to only generate external declaration for the derived type descriptor object of types defined elsewhere. Note that objects compiled with this option are not compatible with object files compiled without because files compiled without it may drop the rtti for type they defined if it is not used in the compilation unit because of the linkonce_odr aspect. I am adding the option so that we can better measure the extra cost of the current approach on apps and allow speeding up some compilation where devirtualization does not matter (and the build config links to all module file object anyway).
This commit is contained in:
@@ -1585,6 +1585,7 @@ bool IsExtensibleType(const DerivedTypeSpec *);
|
|||||||
bool IsSequenceOrBindCType(const DerivedTypeSpec *);
|
bool IsSequenceOrBindCType(const DerivedTypeSpec *);
|
||||||
bool IsBuiltinDerivedType(const DerivedTypeSpec *derived, const char *name);
|
bool IsBuiltinDerivedType(const DerivedTypeSpec *derived, const char *name);
|
||||||
bool IsBuiltinCPtr(const Symbol &);
|
bool IsBuiltinCPtr(const Symbol &);
|
||||||
|
bool IsFromBuiltinModule(const Symbol &);
|
||||||
bool IsEventType(const DerivedTypeSpec *);
|
bool IsEventType(const DerivedTypeSpec *);
|
||||||
bool IsLockType(const DerivedTypeSpec *);
|
bool IsLockType(const DerivedTypeSpec *);
|
||||||
bool IsNotifyType(const DerivedTypeSpec *);
|
bool IsNotifyType(const DerivedTypeSpec *);
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ struct FIRToLLVMPassOptions {
|
|||||||
// that such programs would crash at runtime if the derived type descriptors
|
// that such programs would crash at runtime if the derived type descriptors
|
||||||
// are required by the runtime, so this is only an option to help debugging.
|
// are required by the runtime, so this is only an option to help debugging.
|
||||||
bool ignoreMissingTypeDescriptors = false;
|
bool ignoreMissingTypeDescriptors = false;
|
||||||
|
// Similar to ignoreMissingTypeDescriptors, but generate external declaration
|
||||||
|
// for the missing type descriptor globals instead.
|
||||||
|
bool skipExternalRttiDefinition = false;
|
||||||
|
|
||||||
// Generate TBAA information for FIR types and memory accessing operations.
|
// Generate TBAA information for FIR types and memory accessing operations.
|
||||||
bool applyTBAA = false;
|
bool applyTBAA = false;
|
||||||
|
|||||||
@@ -32,6 +32,19 @@ extern llvm::cl::opt<std::size_t> arrayStackAllocationThreshold;
|
|||||||
/// generated by the frontend.
|
/// generated by the frontend.
|
||||||
extern llvm::cl::opt<bool> ignoreMissingTypeDescriptors;
|
extern llvm::cl::opt<bool> ignoreMissingTypeDescriptors;
|
||||||
|
|
||||||
|
/// Shared option in tools to only generate rtti static object definitions for
|
||||||
|
/// derived types defined in the current compilation unit. Derived type
|
||||||
|
/// descriptor object for types defined in other objects will only be declared
|
||||||
|
/// as external. This also changes the linkage of rtti objects defined in the
|
||||||
|
/// current compilation unit from linkonce_odr to external so that unused rtti
|
||||||
|
/// objects are retained and can be accessed from other compilation units. This
|
||||||
|
/// is an experimental option to explore compilation speed improvements and is
|
||||||
|
/// an ABI breaking change because of the linkage change.
|
||||||
|
/// It will also require linking against module file objects of modules defining
|
||||||
|
/// only types (even for trivial types without type bound procedures, which
|
||||||
|
/// differs from most compilers).
|
||||||
|
extern llvm::cl::opt<bool> skipExternalRttiDefinition;
|
||||||
|
|
||||||
/// Default optimization level used to create Flang pass pipeline is O0.
|
/// Default optimization level used to create Flang pass pipeline is O0.
|
||||||
extern llvm::OptimizationLevel defaultOptLevel;
|
extern llvm::OptimizationLevel defaultOptLevel;
|
||||||
|
|
||||||
|
|||||||
@@ -35,32 +35,6 @@ inline std::int64_t toInt(mlir::arith::ConstantOp cop) {
|
|||||||
.getSExtValue();
|
.getSExtValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reconstruct binding tables for dynamic dispatch.
|
|
||||||
using BindingTable = llvm::DenseMap<llvm::StringRef, unsigned>;
|
|
||||||
using BindingTables = llvm::DenseMap<llvm::StringRef, BindingTable>;
|
|
||||||
|
|
||||||
inline void buildBindingTables(BindingTables &bindingTables,
|
|
||||||
mlir::ModuleOp mod) {
|
|
||||||
|
|
||||||
// The binding tables are defined in FIR after lowering inside fir.type_info
|
|
||||||
// operations. Go through each binding tables and store the procedure name and
|
|
||||||
// binding index for later use by the fir.dispatch conversion pattern.
|
|
||||||
for (auto typeInfo : mod.getOps<fir::TypeInfoOp>()) {
|
|
||||||
unsigned bindingIdx = 0;
|
|
||||||
BindingTable bindings;
|
|
||||||
if (typeInfo.getDispatchTable().empty()) {
|
|
||||||
bindingTables[typeInfo.getSymName()] = bindings;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (auto dtEntry :
|
|
||||||
typeInfo.getDispatchTable().front().getOps<fir::DTEntryOp>()) {
|
|
||||||
bindings[dtEntry.getMethod()] = bindingIdx;
|
|
||||||
++bindingIdx;
|
|
||||||
}
|
|
||||||
bindingTables[typeInfo.getSymName()] = bindings;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Translate front-end KINDs for use in the IR and code gen.
|
// Translate front-end KINDs for use in the IR and code gen.
|
||||||
inline std::vector<fir::KindTy>
|
inline std::vector<fir::KindTy>
|
||||||
fromDefaultKinds(const Fortran::common::IntrinsicTypeDefaultKinds &defKinds) {
|
fromDefaultKinds(const Fortran::common::IntrinsicTypeDefaultKinds &defKinds) {
|
||||||
|
|||||||
@@ -38,6 +38,10 @@ RuntimeDerivedTypeTables BuildRuntimeDerivedTypeTables(SemanticsContext &);
|
|||||||
/// to describe other derived types at runtime in flang descriptor.
|
/// to describe other derived types at runtime in flang descriptor.
|
||||||
constexpr char typeInfoBuiltinModule[]{"__fortran_type_info"};
|
constexpr char typeInfoBuiltinModule[]{"__fortran_type_info"};
|
||||||
|
|
||||||
|
/// Name of the builtin derived type in __fortran_type_inf that is used for
|
||||||
|
/// derived type descriptors.
|
||||||
|
constexpr char typeDescriptorTypeName[]{"derivedtype"};
|
||||||
|
|
||||||
/// Name of the bindings descriptor component in the DerivedType type of the
|
/// Name of the bindings descriptor component in the DerivedType type of the
|
||||||
/// __Fortran_type_info module
|
/// __Fortran_type_info module
|
||||||
constexpr char bindingDescCompName[]{"binding"};
|
constexpr char bindingDescCompName[]{"binding"};
|
||||||
|
|||||||
@@ -2334,6 +2334,11 @@ bool IsBuiltinCPtr(const Symbol &symbol) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsFromBuiltinModule(const Symbol &symbol) {
|
||||||
|
const Scope &scope{symbol.GetUltimate().owner()};
|
||||||
|
return IsSameModule(&scope, scope.context().GetBuiltinsScope());
|
||||||
|
}
|
||||||
|
|
||||||
bool IsIsoCType(const DerivedTypeSpec *derived) {
|
bool IsIsoCType(const DerivedTypeSpec *derived) {
|
||||||
return IsBuiltinDerivedType(derived, "c_ptr") ||
|
return IsBuiltinDerivedType(derived, "c_ptr") ||
|
||||||
IsBuiltinDerivedType(derived, "c_funptr");
|
IsBuiltinDerivedType(derived, "c_funptr");
|
||||||
|
|||||||
@@ -52,6 +52,7 @@
|
|||||||
#include "flang/Optimizer/Dialect/FIROps.h"
|
#include "flang/Optimizer/Dialect/FIROps.h"
|
||||||
#include "flang/Optimizer/Dialect/Support/FIRContext.h"
|
#include "flang/Optimizer/Dialect/Support/FIRContext.h"
|
||||||
#include "flang/Optimizer/HLFIR/HLFIROps.h"
|
#include "flang/Optimizer/HLFIR/HLFIROps.h"
|
||||||
|
#include "flang/Optimizer/Passes/CommandLineOpts.h"
|
||||||
#include "flang/Optimizer/Support/DataLayout.h"
|
#include "flang/Optimizer/Support/DataLayout.h"
|
||||||
#include "flang/Optimizer/Support/FatalError.h"
|
#include "flang/Optimizer/Support/FatalError.h"
|
||||||
#include "flang/Optimizer/Support/InternalNames.h"
|
#include "flang/Optimizer/Support/InternalNames.h"
|
||||||
@@ -262,6 +263,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void createTypeInfo(Fortran::lower::AbstractConverter &converter) {
|
void createTypeInfo(Fortran::lower::AbstractConverter &converter) {
|
||||||
|
createTypeInfoForTypeDescriptorBuiltinType(converter);
|
||||||
while (!registeredTypeInfoA.empty()) {
|
while (!registeredTypeInfoA.empty()) {
|
||||||
currentTypeInfoStack = ®isteredTypeInfoB;
|
currentTypeInfoStack = ®isteredTypeInfoB;
|
||||||
for (const TypeInfo &info : registeredTypeInfoA)
|
for (const TypeInfo &info : registeredTypeInfoA)
|
||||||
@@ -277,10 +279,22 @@ public:
|
|||||||
private:
|
private:
|
||||||
void createTypeInfoOpAndGlobal(Fortran::lower::AbstractConverter &converter,
|
void createTypeInfoOpAndGlobal(Fortran::lower::AbstractConverter &converter,
|
||||||
const TypeInfo &info) {
|
const TypeInfo &info) {
|
||||||
Fortran::lower::createRuntimeTypeInfoGlobal(converter, info.symbol.get());
|
if (!::skipExternalRttiDefinition)
|
||||||
|
Fortran::lower::createRuntimeTypeInfoGlobal(converter, info.symbol.get());
|
||||||
createTypeInfoOp(converter, info);
|
createTypeInfoOp(converter, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void createTypeInfoForTypeDescriptorBuiltinType(
|
||||||
|
Fortran::lower::AbstractConverter &converter) {
|
||||||
|
if (registeredTypeInfoA.empty())
|
||||||
|
return;
|
||||||
|
auto builtinTypeInfoType = llvm::cast<fir::RecordType>(
|
||||||
|
converter.genType(registeredTypeInfoA[0].symbol.get()));
|
||||||
|
converter.getFirOpBuilder().createTypeInfoOp(
|
||||||
|
registeredTypeInfoA[0].loc, builtinTypeInfoType,
|
||||||
|
/*parentType=*/fir::RecordType{});
|
||||||
|
}
|
||||||
|
|
||||||
void createTypeInfoOp(Fortran::lower::AbstractConverter &converter,
|
void createTypeInfoOp(Fortran::lower::AbstractConverter &converter,
|
||||||
const TypeInfo &info) {
|
const TypeInfo &info) {
|
||||||
fir::RecordType parentType{};
|
fir::RecordType parentType{};
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
#include "flang/Optimizer/Dialect/FIROps.h"
|
#include "flang/Optimizer/Dialect/FIROps.h"
|
||||||
#include "flang/Optimizer/Dialect/Support/FIRContext.h"
|
#include "flang/Optimizer/Dialect/Support/FIRContext.h"
|
||||||
#include "flang/Optimizer/HLFIR/HLFIROps.h"
|
#include "flang/Optimizer/HLFIR/HLFIROps.h"
|
||||||
|
#include "flang/Optimizer/Passes/CommandLineOpts.h"
|
||||||
#include "flang/Optimizer/Support/FatalError.h"
|
#include "flang/Optimizer/Support/FatalError.h"
|
||||||
#include "flang/Optimizer/Support/InternalNames.h"
|
#include "flang/Optimizer/Support/InternalNames.h"
|
||||||
#include "flang/Optimizer/Support/Utils.h"
|
#include "flang/Optimizer/Support/Utils.h"
|
||||||
@@ -652,8 +653,13 @@ getLinkageAttribute(fir::FirOpBuilder &builder,
|
|||||||
// Runtime type info for a same derived type is identical in each compilation
|
// Runtime type info for a same derived type is identical in each compilation
|
||||||
// unit. It desired to avoid having to link against module that only define a
|
// unit. It desired to avoid having to link against module that only define a
|
||||||
// type. Therefore the runtime type info is generated everywhere it is needed
|
// type. Therefore the runtime type info is generated everywhere it is needed
|
||||||
// with `linkonce_odr` LLVM linkage.
|
// with `linkonce_odr` LLVM linkage (unless the skipExternalRttiDefinition
|
||||||
if (var.isRuntimeTypeInfoData())
|
// option is set, in which case one will need to link against objects of
|
||||||
|
// modules defining types). Builtin objects rtti is always generated because
|
||||||
|
// the builtin module is currently not compiled or part of the runtime.
|
||||||
|
if (var.isRuntimeTypeInfoData() &&
|
||||||
|
(!::skipExternalRttiDefinition ||
|
||||||
|
Fortran::semantics::IsFromBuiltinModule(var.getSymbol())))
|
||||||
return builder.createLinkOnceODRLinkage();
|
return builder.createLinkOnceODRLinkage();
|
||||||
if (var.isModuleOrSubmoduleVariable())
|
if (var.isModuleOrSubmoduleVariable())
|
||||||
return {}; // external linkage
|
return {}; // external linkage
|
||||||
|
|||||||
@@ -1294,6 +1294,51 @@ genCUFAllocDescriptor(mlir::Location loc,
|
|||||||
.getResult();
|
.getResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the address of the type descriptor global variable that was created by
|
||||||
|
/// lowering for derived type \p recType.
|
||||||
|
template <typename ModOpTy>
|
||||||
|
static mlir::Value
|
||||||
|
getTypeDescriptor(ModOpTy mod, mlir::ConversionPatternRewriter &rewriter,
|
||||||
|
mlir::Location loc, fir::RecordType recType,
|
||||||
|
const fir::FIRToLLVMPassOptions &options) {
|
||||||
|
std::string name =
|
||||||
|
options.typeDescriptorsRenamedForAssembly
|
||||||
|
? fir::NameUniquer::getTypeDescriptorAssemblyName(recType.getName())
|
||||||
|
: fir::NameUniquer::getTypeDescriptorName(recType.getName());
|
||||||
|
mlir::Type llvmPtrTy = ::getLlvmPtrType(mod.getContext());
|
||||||
|
if (auto global = mod.template lookupSymbol<fir::GlobalOp>(name))
|
||||||
|
return rewriter.create<mlir::LLVM::AddressOfOp>(loc, llvmPtrTy,
|
||||||
|
global.getSymName());
|
||||||
|
// The global may have already been translated to LLVM.
|
||||||
|
if (auto global = mod.template lookupSymbol<mlir::LLVM::GlobalOp>(name))
|
||||||
|
return rewriter.create<mlir::LLVM::AddressOfOp>(loc, llvmPtrTy,
|
||||||
|
global.getSymName());
|
||||||
|
// Type info derived types do not have type descriptors since they are the
|
||||||
|
// types defining type descriptors.
|
||||||
|
if (options.ignoreMissingTypeDescriptors ||
|
||||||
|
fir::NameUniquer::belongsToModule(
|
||||||
|
name, Fortran::semantics::typeInfoBuiltinModule))
|
||||||
|
return rewriter.create<mlir::LLVM::ZeroOp>(loc, llvmPtrTy);
|
||||||
|
|
||||||
|
if (!options.skipExternalRttiDefinition)
|
||||||
|
fir::emitFatalError(loc,
|
||||||
|
"runtime derived type info descriptor was not "
|
||||||
|
"generated and skipExternalRttiDefinition and "
|
||||||
|
"ignoreMissingTypeDescriptors options are not set");
|
||||||
|
|
||||||
|
// Rtti for a derived type defined in another compilation unit and for which
|
||||||
|
// rtti was not defined in lowering because of the skipExternalRttiDefinition
|
||||||
|
// option. Generate the object declaration now.
|
||||||
|
auto insertPt = rewriter.saveInsertionPoint();
|
||||||
|
rewriter.setInsertionPoint(mod.getBody(), mod.getBody()->end());
|
||||||
|
mlir::LLVM::GlobalOp global = rewriter.create<mlir::LLVM::GlobalOp>(
|
||||||
|
loc, llvmPtrTy, /*constant=*/true, mlir::LLVM::Linkage::External, name,
|
||||||
|
mlir::Attribute());
|
||||||
|
rewriter.restoreInsertionPoint(insertPt);
|
||||||
|
return rewriter.create<mlir::LLVM::AddressOfOp>(loc, llvmPtrTy,
|
||||||
|
global.getSymName());
|
||||||
|
}
|
||||||
|
|
||||||
/// Common base class for embox to descriptor conversion.
|
/// Common base class for embox to descriptor conversion.
|
||||||
template <typename OP>
|
template <typename OP>
|
||||||
struct EmboxCommonConversion : public fir::FIROpConversion<OP> {
|
struct EmboxCommonConversion : public fir::FIROpConversion<OP> {
|
||||||
@@ -1406,36 +1451,6 @@ struct EmboxCommonConversion : public fir::FIROpConversion<OP> {
|
|||||||
stride);
|
stride);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the address of the type descriptor global variable that was created by
|
|
||||||
/// lowering for derived type \p recType.
|
|
||||||
template <typename ModOpTy>
|
|
||||||
mlir::Value
|
|
||||||
getTypeDescriptor(ModOpTy mod, mlir::ConversionPatternRewriter &rewriter,
|
|
||||||
mlir::Location loc, fir::RecordType recType) const {
|
|
||||||
std::string name =
|
|
||||||
this->options.typeDescriptorsRenamedForAssembly
|
|
||||||
? fir::NameUniquer::getTypeDescriptorAssemblyName(recType.getName())
|
|
||||||
: fir::NameUniquer::getTypeDescriptorName(recType.getName());
|
|
||||||
mlir::Type llvmPtrTy = ::getLlvmPtrType(mod.getContext());
|
|
||||||
if (auto global = mod.template lookupSymbol<fir::GlobalOp>(name)) {
|
|
||||||
return rewriter.create<mlir::LLVM::AddressOfOp>(loc, llvmPtrTy,
|
|
||||||
global.getSymName());
|
|
||||||
}
|
|
||||||
if (auto global = mod.template lookupSymbol<mlir::LLVM::GlobalOp>(name)) {
|
|
||||||
// The global may have already been translated to LLVM.
|
|
||||||
return rewriter.create<mlir::LLVM::AddressOfOp>(loc, llvmPtrTy,
|
|
||||||
global.getSymName());
|
|
||||||
}
|
|
||||||
// Type info derived types do not have type descriptors since they are the
|
|
||||||
// types defining type descriptors.
|
|
||||||
if (!this->options.ignoreMissingTypeDescriptors &&
|
|
||||||
!fir::NameUniquer::belongsToModule(
|
|
||||||
name, Fortran::semantics::typeInfoBuiltinModule))
|
|
||||||
fir::emitFatalError(
|
|
||||||
loc, "runtime derived type info descriptor was not generated");
|
|
||||||
return rewriter.create<mlir::LLVM::ZeroOp>(loc, llvmPtrTy);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename ModOpTy>
|
template <typename ModOpTy>
|
||||||
mlir::Value populateDescriptor(mlir::Location loc, ModOpTy mod,
|
mlir::Value populateDescriptor(mlir::Location loc, ModOpTy mod,
|
||||||
fir::BaseBoxType boxTy, mlir::Type inputType,
|
fir::BaseBoxType boxTy, mlir::Type inputType,
|
||||||
@@ -1500,7 +1515,8 @@ struct EmboxCommonConversion : public fir::FIROpConversion<OP> {
|
|||||||
mlir::Type innerType = fir::unwrapInnerType(inputType);
|
mlir::Type innerType = fir::unwrapInnerType(inputType);
|
||||||
if (innerType && mlir::isa<fir::RecordType>(innerType)) {
|
if (innerType && mlir::isa<fir::RecordType>(innerType)) {
|
||||||
auto recTy = mlir::dyn_cast<fir::RecordType>(innerType);
|
auto recTy = mlir::dyn_cast<fir::RecordType>(innerType);
|
||||||
typeDesc = getTypeDescriptor(mod, rewriter, loc, recTy);
|
typeDesc =
|
||||||
|
getTypeDescriptor(mod, rewriter, loc, recTy, this->options);
|
||||||
} else {
|
} else {
|
||||||
// Unlimited polymorphic type descriptor with no record type. Set
|
// Unlimited polymorphic type descriptor with no record type. Set
|
||||||
// type descriptor address to a clean state.
|
// type descriptor address to a clean state.
|
||||||
@@ -1508,8 +1524,8 @@ struct EmboxCommonConversion : public fir::FIROpConversion<OP> {
|
|||||||
loc, ::getLlvmPtrType(mod.getContext()));
|
loc, ::getLlvmPtrType(mod.getContext()));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
typeDesc = getTypeDescriptor(mod, rewriter, loc,
|
typeDesc = getTypeDescriptor(
|
||||||
fir::unwrapIfDerived(boxTy));
|
mod, rewriter, loc, fir::unwrapIfDerived(boxTy), this->options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (typeDesc)
|
if (typeDesc)
|
||||||
@@ -3021,22 +3037,10 @@ struct TypeDescOpConversion : public fir::FIROpConversion<fir::TypeDescOp> {
|
|||||||
assert(mlir::isa<fir::RecordType>(inTy) && "expecting fir.type");
|
assert(mlir::isa<fir::RecordType>(inTy) && "expecting fir.type");
|
||||||
auto recordType = mlir::dyn_cast<fir::RecordType>(inTy);
|
auto recordType = mlir::dyn_cast<fir::RecordType>(inTy);
|
||||||
auto module = typeDescOp.getOperation()->getParentOfType<mlir::ModuleOp>();
|
auto module = typeDescOp.getOperation()->getParentOfType<mlir::ModuleOp>();
|
||||||
std::string typeDescName =
|
mlir::Value typeDesc = getTypeDescriptor(
|
||||||
this->options.typeDescriptorsRenamedForAssembly
|
module, rewriter, typeDescOp.getLoc(), recordType, this->options);
|
||||||
? fir::NameUniquer::getTypeDescriptorAssemblyName(
|
rewriter.replaceOp(typeDescOp, typeDesc);
|
||||||
recordType.getName())
|
return mlir::success();
|
||||||
: fir::NameUniquer::getTypeDescriptorName(recordType.getName());
|
|
||||||
auto llvmPtrTy = ::getLlvmPtrType(typeDescOp.getContext());
|
|
||||||
if (auto global = module.lookupSymbol<mlir::LLVM::GlobalOp>(typeDescName)) {
|
|
||||||
rewriter.replaceOpWithNewOp<mlir::LLVM::AddressOfOp>(
|
|
||||||
typeDescOp, llvmPtrTy, global.getSymName());
|
|
||||||
return mlir::success();
|
|
||||||
} else if (auto global = module.lookupSymbol<fir::GlobalOp>(typeDescName)) {
|
|
||||||
rewriter.replaceOpWithNewOp<mlir::LLVM::AddressOfOp>(
|
|
||||||
typeDescOp, llvmPtrTy, global.getSymName());
|
|
||||||
return mlir::success();
|
|
||||||
}
|
|
||||||
return mlir::failure();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,12 @@ cl::opt<bool> ignoreMissingTypeDescriptors(
|
|||||||
"translating FIR to LLVM"),
|
"translating FIR to LLVM"),
|
||||||
cl::init(false), cl::Hidden);
|
cl::init(false), cl::Hidden);
|
||||||
|
|
||||||
|
cl::opt<bool> skipExternalRttiDefinition(
|
||||||
|
"skip-external-rtti-definition", llvm::cl::init(false),
|
||||||
|
llvm::cl::desc("do not define rtti static objects for types belonging to "
|
||||||
|
"other compilation units"),
|
||||||
|
cl::Hidden);
|
||||||
|
|
||||||
OptimizationLevel defaultOptLevel{OptimizationLevel::O0};
|
OptimizationLevel defaultOptLevel{OptimizationLevel::O0};
|
||||||
|
|
||||||
codegenoptions::DebugInfoKind noDebugInfo{codegenoptions::NoDebugInfo};
|
codegenoptions::DebugInfoKind noDebugInfo{codegenoptions::NoDebugInfo};
|
||||||
|
|||||||
@@ -108,6 +108,7 @@ void addFIRToLLVMPass(mlir::PassManager &pm,
|
|||||||
const MLIRToLLVMPassPipelineConfig &config) {
|
const MLIRToLLVMPassPipelineConfig &config) {
|
||||||
fir::FIRToLLVMPassOptions options;
|
fir::FIRToLLVMPassOptions options;
|
||||||
options.ignoreMissingTypeDescriptors = ignoreMissingTypeDescriptors;
|
options.ignoreMissingTypeDescriptors = ignoreMissingTypeDescriptors;
|
||||||
|
options.skipExternalRttiDefinition = skipExternalRttiDefinition;
|
||||||
options.applyTBAA = config.AliasAnalysis;
|
options.applyTBAA = config.AliasAnalysis;
|
||||||
options.forceUnifiedTBAATree = useOldAliasTags;
|
options.forceUnifiedTBAATree = useOldAliasTags;
|
||||||
options.typeDescriptorsRenamedForAssembly =
|
options.typeDescriptorsRenamedForAssembly =
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
#include "flang/Optimizer/Dialect/Support/KindMapping.h"
|
#include "flang/Optimizer/Dialect/Support/KindMapping.h"
|
||||||
#include "flang/Optimizer/Support/InternalNames.h"
|
#include "flang/Optimizer/Support/InternalNames.h"
|
||||||
#include "flang/Optimizer/Support/TypeCode.h"
|
#include "flang/Optimizer/Support/TypeCode.h"
|
||||||
#include "flang/Optimizer/Support/Utils.h"
|
|
||||||
#include "flang/Optimizer/Transforms/Passes.h"
|
#include "flang/Optimizer/Transforms/Passes.h"
|
||||||
#include "flang/Runtime/derived-api.h"
|
#include "flang/Runtime/derived-api.h"
|
||||||
#include "flang/Semantics/runtime-type-info.h"
|
#include "flang/Semantics/runtime-type-info.h"
|
||||||
@@ -38,6 +37,45 @@ namespace fir {
|
|||||||
using namespace fir;
|
using namespace fir;
|
||||||
using namespace mlir;
|
using namespace mlir;
|
||||||
|
|
||||||
|
// Reconstruct binding tables for dynamic dispatch.
|
||||||
|
using BindingTable = llvm::DenseMap<llvm::StringRef, unsigned>;
|
||||||
|
using BindingTables = llvm::DenseMap<llvm::StringRef, BindingTable>;
|
||||||
|
|
||||||
|
static std::string getTypeDescriptorTypeName() {
|
||||||
|
llvm::SmallVector<llvm::StringRef, 1> modules = {
|
||||||
|
Fortran::semantics::typeInfoBuiltinModule};
|
||||||
|
return fir::NameUniquer::doType(modules, /*proc=*/{}, /*blockId=*/0,
|
||||||
|
Fortran::semantics::typeDescriptorTypeName,
|
||||||
|
/*kinds=*/{});
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::optional<mlir::Type>
|
||||||
|
buildBindingTables(BindingTables &bindingTables, mlir::ModuleOp mod) {
|
||||||
|
|
||||||
|
std::optional<mlir::Type> typeDescriptorType;
|
||||||
|
std::string typeDescriptorTypeName = getTypeDescriptorTypeName();
|
||||||
|
// The binding tables are defined in FIR after lowering inside fir.type_info
|
||||||
|
// operations. Go through each binding tables and store the procedure name and
|
||||||
|
// binding index for later use by the fir.dispatch conversion pattern.
|
||||||
|
for (auto typeInfo : mod.getOps<fir::TypeInfoOp>()) {
|
||||||
|
if (!typeDescriptorType && typeInfo.getSymName() == typeDescriptorTypeName)
|
||||||
|
typeDescriptorType = typeInfo.getType();
|
||||||
|
unsigned bindingIdx = 0;
|
||||||
|
BindingTable bindings;
|
||||||
|
if (typeInfo.getDispatchTable().empty()) {
|
||||||
|
bindingTables[typeInfo.getSymName()] = bindings;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (auto dtEntry :
|
||||||
|
typeInfo.getDispatchTable().front().getOps<fir::DTEntryOp>()) {
|
||||||
|
bindings[dtEntry.getMethod()] = bindingIdx;
|
||||||
|
++bindingIdx;
|
||||||
|
}
|
||||||
|
bindingTables[typeInfo.getSymName()] = bindings;
|
||||||
|
}
|
||||||
|
return typeDescriptorType;
|
||||||
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
/// SelectTypeOp converted to an if-then-else chain
|
/// SelectTypeOp converted to an if-then-else chain
|
||||||
@@ -77,9 +115,10 @@ private:
|
|||||||
struct DispatchOpConv : public OpConversionPattern<fir::DispatchOp> {
|
struct DispatchOpConv : public OpConversionPattern<fir::DispatchOp> {
|
||||||
using OpConversionPattern<fir::DispatchOp>::OpConversionPattern;
|
using OpConversionPattern<fir::DispatchOp>::OpConversionPattern;
|
||||||
|
|
||||||
DispatchOpConv(mlir::MLIRContext *ctx, const BindingTables &bindingTables)
|
DispatchOpConv(mlir::MLIRContext *ctx, const BindingTables &bindingTables,
|
||||||
|
std::optional<mlir::Type> typeDescriptorType)
|
||||||
: mlir::OpConversionPattern<fir::DispatchOp>(ctx),
|
: mlir::OpConversionPattern<fir::DispatchOp>(ctx),
|
||||||
bindingTables(bindingTables) {}
|
bindingTables(bindingTables), typeDescriptorType{typeDescriptorType} {}
|
||||||
|
|
||||||
llvm::LogicalResult
|
llvm::LogicalResult
|
||||||
matchAndRewrite(fir::DispatchOp dispatch, OpAdaptor adaptor,
|
matchAndRewrite(fir::DispatchOp dispatch, OpAdaptor adaptor,
|
||||||
@@ -111,13 +150,11 @@ struct DispatchOpConv : public OpConversionPattern<fir::DispatchOp> {
|
|||||||
|
|
||||||
mlir::Value passedObject = dispatch.getObject();
|
mlir::Value passedObject = dispatch.getObject();
|
||||||
|
|
||||||
auto module = dispatch.getOperation()->getParentOfType<mlir::ModuleOp>();
|
if (!typeDescriptorType)
|
||||||
Type typeDescTy;
|
return emitError(loc) << "cannot find " << getTypeDescriptorTypeName()
|
||||||
std::string typeDescName =
|
<< " fir.type_info that is required to get the "
|
||||||
NameUniquer::getTypeDescriptorName(recordType.getName());
|
"related builtin type and lower fir.dispatch";
|
||||||
if (auto global = module.lookupSymbol<fir::GlobalOp>(typeDescName)) {
|
mlir::Type typeDescTy = *typeDescriptorType;
|
||||||
typeDescTy = global.getType();
|
|
||||||
}
|
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
// Before:
|
// Before:
|
||||||
@@ -213,6 +250,7 @@ struct DispatchOpConv : public OpConversionPattern<fir::DispatchOp> {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
BindingTables bindingTables;
|
BindingTables bindingTables;
|
||||||
|
std::optional<mlir::Type> typeDescriptorType;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Convert FIR structured control flow ops to CFG ops.
|
/// Convert FIR structured control flow ops to CFG ops.
|
||||||
@@ -229,10 +267,11 @@ public:
|
|||||||
mlir::RewritePatternSet patterns(context);
|
mlir::RewritePatternSet patterns(context);
|
||||||
|
|
||||||
BindingTables bindingTables;
|
BindingTables bindingTables;
|
||||||
buildBindingTables(bindingTables, mod);
|
std::optional<mlir::Type> typeDescriptorType =
|
||||||
|
buildBindingTables(bindingTables, mod);
|
||||||
|
|
||||||
patterns.insert<SelectTypeConv>(context);
|
patterns.insert<SelectTypeConv>(context);
|
||||||
patterns.insert<DispatchOpConv>(context, bindingTables);
|
patterns.insert<DispatchOpConv>(context, bindingTables, typeDescriptorType);
|
||||||
mlir::ConversionTarget target(*context);
|
mlir::ConversionTarget target(*context);
|
||||||
target.addLegalDialect<mlir::affine::AffineDialect,
|
target.addLegalDialect<mlir::affine::AffineDialect,
|
||||||
mlir::cf::ControlFlowDialect, FIROpsDialect,
|
mlir::cf::ControlFlowDialect, FIROpsDialect,
|
||||||
@@ -379,16 +418,11 @@ llvm::LogicalResult SelectTypeConv::genTypeLadderStep(
|
|||||||
} else if (auto a = mlir::dyn_cast<fir::SubclassAttr>(attr)) {
|
} else if (auto a = mlir::dyn_cast<fir::SubclassAttr>(attr)) {
|
||||||
// Retrieve the type descriptor from the type guard statement record type.
|
// Retrieve the type descriptor from the type guard statement record type.
|
||||||
assert(mlir::isa<fir::RecordType>(a.getType()) && "expect fir.record type");
|
assert(mlir::isa<fir::RecordType>(a.getType()) && "expect fir.record type");
|
||||||
fir::RecordType recTy = mlir::dyn_cast<fir::RecordType>(a.getType());
|
mlir::Value typeDescAddr =
|
||||||
std::string typeDescName =
|
rewriter.create<fir::TypeDescOp>(loc, mlir::TypeAttr::get(a.getType()));
|
||||||
fir::NameUniquer::getTypeDescriptorName(recTy.getName());
|
mlir::Type refNoneType = ReferenceType::get(rewriter.getNoneType());
|
||||||
auto typeDescGlobal = mod.lookupSymbol<fir::GlobalOp>(typeDescName);
|
|
||||||
auto typeDescAddr = rewriter.create<fir::AddrOfOp>(
|
|
||||||
loc, fir::ReferenceType::get(typeDescGlobal.getType()),
|
|
||||||
typeDescGlobal.getSymbol());
|
|
||||||
mlir::Type typeDescTy = ReferenceType::get(rewriter.getNoneType());
|
|
||||||
mlir::Value typeDesc =
|
mlir::Value typeDesc =
|
||||||
rewriter.create<ConvertOp>(loc, typeDescTy, typeDescAddr);
|
rewriter.create<ConvertOp>(loc, refNoneType, typeDescAddr);
|
||||||
|
|
||||||
// Prepare the selector descriptor for the runtime call.
|
// Prepare the selector descriptor for the runtime call.
|
||||||
mlir::Type descNoneTy = fir::BoxType::get(rewriter.getNoneType());
|
mlir::Type descNoneTy = fir::BoxType::get(rewriter.getNoneType());
|
||||||
@@ -406,7 +440,7 @@ llvm::LogicalResult SelectTypeConv::genTypeLadderStep(
|
|||||||
mlir::UnitAttr::get(rewriter.getContext()));
|
mlir::UnitAttr::get(rewriter.getContext()));
|
||||||
callee =
|
callee =
|
||||||
fir::createFuncOp(rewriter.getUnknownLoc(), mod, fctName,
|
fir::createFuncOp(rewriter.getUnknownLoc(), mod, fctName,
|
||||||
rewriter.getFunctionType({descNoneTy, typeDescTy},
|
rewriter.getFunctionType({descNoneTy, refNoneType},
|
||||||
rewriter.getI1Type()),
|
rewriter.getI1Type()),
|
||||||
{runtimeAttr});
|
{runtimeAttr});
|
||||||
}
|
}
|
||||||
@@ -435,20 +469,11 @@ SelectTypeConv::genTypeDescCompare(mlir::Location loc, mlir::Value selector,
|
|||||||
mlir::Type ty, mlir::ModuleOp mod,
|
mlir::Type ty, mlir::ModuleOp mod,
|
||||||
mlir::PatternRewriter &rewriter) const {
|
mlir::PatternRewriter &rewriter) const {
|
||||||
assert(mlir::isa<fir::RecordType>(ty) && "expect fir.record type");
|
assert(mlir::isa<fir::RecordType>(ty) && "expect fir.record type");
|
||||||
fir::RecordType recTy = mlir::dyn_cast<fir::RecordType>(ty);
|
mlir::Value typeDescAddr =
|
||||||
std::string typeDescName =
|
rewriter.create<fir::TypeDescOp>(loc, mlir::TypeAttr::get(ty));
|
||||||
fir::NameUniquer::getTypeDescriptorName(recTy.getName());
|
mlir::Value selectorTdescAddr = rewriter.create<fir::BoxTypeDescOp>(
|
||||||
auto typeDescGlobal = mod.lookupSymbol<fir::GlobalOp>(typeDescName);
|
loc, typeDescAddr.getType(), selector);
|
||||||
if (!typeDescGlobal)
|
|
||||||
return {};
|
|
||||||
auto typeDescAddr = rewriter.create<fir::AddrOfOp>(
|
|
||||||
loc, fir::ReferenceType::get(typeDescGlobal.getType()),
|
|
||||||
typeDescGlobal.getSymbol());
|
|
||||||
auto intPtrTy = rewriter.getIndexType();
|
auto intPtrTy = rewriter.getIndexType();
|
||||||
mlir::Type tdescType =
|
|
||||||
fir::TypeDescType::get(mlir::NoneType::get(rewriter.getContext()));
|
|
||||||
mlir::Value selectorTdescAddr =
|
|
||||||
rewriter.create<fir::BoxTypeDescOp>(loc, tdescType, selector);
|
|
||||||
auto typeDescInt =
|
auto typeDescInt =
|
||||||
rewriter.create<fir::ConvertOp>(loc, intPtrTy, typeDescAddr);
|
rewriter.create<fir::ConvertOp>(loc, intPtrTy, typeDescAddr);
|
||||||
auto selectorTdescInt =
|
auto selectorTdescInt =
|
||||||
|
|||||||
47
flang/test/Integration/skip-external-rtti-definition.F90
Normal file
47
flang/test/Integration/skip-external-rtti-definition.F90
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
! Test -skip-external-rtti-definition option
|
||||||
|
|
||||||
|
!RUN: rm -rf %t && mkdir -p %t
|
||||||
|
!RUN: %flang_fc1 -fsyntax-only -DSTEP=1 -J%t %s
|
||||||
|
!RUN: %flang_fc1 -emit-llvm -J%t %s -o - | FileCheck %s -check-prefix=LINKONCE
|
||||||
|
!RUN: %flang_fc1 -emit-llvm -J%t -mllvm -skip-external-rtti-definition %s -o - | FileCheck %s -check-prefix=EXTERNAL
|
||||||
|
|
||||||
|
#if STEP == 1
|
||||||
|
module module_external_type_definition
|
||||||
|
type t1
|
||||||
|
end type
|
||||||
|
end module
|
||||||
|
#else
|
||||||
|
|
||||||
|
module module_same_unit_type_definition
|
||||||
|
type t2
|
||||||
|
end type
|
||||||
|
end module
|
||||||
|
|
||||||
|
subroutine test
|
||||||
|
use module_external_type_definition
|
||||||
|
use module_same_unit_type_definition
|
||||||
|
interface
|
||||||
|
subroutine needs_descriptor(x)
|
||||||
|
class(*) :: x
|
||||||
|
end subroutine
|
||||||
|
end interface
|
||||||
|
type(t1) :: x1
|
||||||
|
type(t2) :: x2
|
||||||
|
call needs_descriptor(x1)
|
||||||
|
call needs_descriptor(x2)
|
||||||
|
end subroutine
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
! LINKONCE-DAG: @_QMmodule_external_type_definitionEXnXt1 = linkonce_odr constant [2 x i8] c"t1", comdat
|
||||||
|
! LINKONCE-DAG: @_QMmodule_external_type_definitionEXdtXt1 = linkonce_odr constant {{.*}} {
|
||||||
|
! LINKONCE-DAG: @_QMmodule_same_unit_type_definitionEXnXt2 = linkonce_odr constant [2 x i8] c"t2", comdat
|
||||||
|
! LINKONCE-DAG: @_QMmodule_same_unit_type_definitionEXdtXt2 = linkonce_odr constant {{.*}} {
|
||||||
|
|
||||||
|
! EXTERNAL-NOT: @_QMmodule_external_type_definitionEXnXt1
|
||||||
|
! EXTERNAL: @_QMmodule_same_unit_type_definitionEXnXt2 = constant [2 x i8] c"t2"
|
||||||
|
! EXTERNAL-NOT: @_QMmodule_external_type_definitionEXnXt1
|
||||||
|
! EXTERNAL: @_QMmodule_same_unit_type_definitionEXdtXt2 = constant {{.*}} {
|
||||||
|
! EXTERNAL-NOT: @_QMmodule_external_type_definitionEXnXt1
|
||||||
|
! EXTERNAL: @_QMmodule_external_type_definitionEXdtXt1 = external constant ptr
|
||||||
|
! EXTERNAL-NOT: @_QMmodule_external_type_definitionEXnXt1
|
||||||
@@ -30,8 +30,8 @@ contains
|
|||||||
|
|
||||||
! CHECK-LABEL: func.func @_QMselect_type_2Pselect_type1(
|
! CHECK-LABEL: func.func @_QMselect_type_2Pselect_type1(
|
||||||
! CHECK-SAME: %[[ARG0:.*]]: !fir.class<!fir.type<_QMselect_type_2Tp1{a:i32,b:i32}>> {fir.bindc_name = "a"}) {
|
! CHECK-SAME: %[[ARG0:.*]]: !fir.class<!fir.type<_QMselect_type_2Tp1{a:i32,b:i32}>> {fir.bindc_name = "a"}) {
|
||||||
! CHECK: %[[TDESC_P3_ADDR:.*]] = fir.address_of(@_QMselect_type_2E.dt.p3) : !fir.ref<!fir.type<{{.*}}>>
|
! CHECK: %[[TDESC_P3_ADDR:.*]] = fir.type_desc !fir.type<_QMselect_type_2Tp3
|
||||||
! CHECK: %[[TDESC_P3_CONV:.*]] = fir.convert %[[TDESC_P3_ADDR]] : (!fir.ref<!fir.type<{{.*}}>>) -> !fir.ref<none>
|
! CHECK: %[[TDESC_P3_CONV:.*]] = fir.convert %[[TDESC_P3_ADDR]] : (!fir.tdesc{{.*}}>) -> !fir.ref<none>
|
||||||
! CHECK: %[[BOX_NONE:.*]] = fir.convert %[[ARG0]] : (!fir.class<!fir.type<_QMselect_type_2Tp1{a:i32,b:i32}>>) -> !fir.box<none>
|
! CHECK: %[[BOX_NONE:.*]] = fir.convert %[[ARG0]] : (!fir.class<!fir.type<_QMselect_type_2Tp1{a:i32,b:i32}>>) -> !fir.box<none>
|
||||||
! CHECK: %[[CLASS_IS_CMP:.*]] = fir.call @_FortranAClassIs(%[[BOX_NONE]], %[[TDESC_P3_CONV]]) : (!fir.box<none>, !fir.ref<none>) -> i1
|
! CHECK: %[[CLASS_IS_CMP:.*]] = fir.call @_FortranAClassIs(%[[BOX_NONE]], %[[TDESC_P3_CONV]]) : (!fir.box<none>, !fir.ref<none>) -> i1
|
||||||
! CHECK: cf.cond_br %[[CLASS_IS_CMP]], ^[[CLASS_IS_P3_BLK:.*]], ^[[NOT_CLASS_IS_P3_BLK:.*]]
|
! CHECK: cf.cond_br %[[CLASS_IS_CMP]], ^[[CLASS_IS_P3_BLK:.*]], ^[[NOT_CLASS_IS_P3_BLK:.*]]
|
||||||
@@ -40,8 +40,8 @@ contains
|
|||||||
! CHECK: ^bb[[CLASS_IS_P1:[0-9]]]:
|
! CHECK: ^bb[[CLASS_IS_P1:[0-9]]]:
|
||||||
! CHECK: cf.br ^bb[[END_SELECT_BLK:[0-9]]]
|
! CHECK: cf.br ^bb[[END_SELECT_BLK:[0-9]]]
|
||||||
! CHECK: ^[[NOT_CLASS_IS_P3_BLK]]:
|
! CHECK: ^[[NOT_CLASS_IS_P3_BLK]]:
|
||||||
! CHECK: %[[TDESC_P1_ADDR:.*]] = fir.address_of(@_QMselect_type_2E.dt.p1) : !fir.ref<!fir.type<{{.*}}>>
|
! CHECK: %[[TDESC_P1_ADDR:.*]] = fir.type_desc !fir.type<_QMselect_type_2Tp1
|
||||||
! CHECK: %[[TDESC_P1_CONV:.*]] = fir.convert %[[TDESC_P1_ADDR]] : (!fir.ref<!fir.type<{{.*}}>>) -> !fir.ref<none>
|
! CHECK: %[[TDESC_P1_CONV:.*]] = fir.convert %[[TDESC_P1_ADDR]] : (!fir.tdesc{{.*}}>) -> !fir.ref<none>
|
||||||
! CHECK: %[[BOX_NONE:.*]] = fir.convert %[[ARG0]] : (!fir.class<!fir.type<_QMselect_type_2Tp1{a:i32,b:i32}>>) -> !fir.box<none>
|
! CHECK: %[[BOX_NONE:.*]] = fir.convert %[[ARG0]] : (!fir.class<!fir.type<_QMselect_type_2Tp1{a:i32,b:i32}>>) -> !fir.box<none>
|
||||||
! CHECK: %[[CLASS_IS_CMP:.*]] = fir.call @_FortranAClassIs(%[[BOX_NONE]], %[[TDESC_P1_CONV]]) : (!fir.box<none>, !fir.ref<none>) -> i1
|
! CHECK: %[[CLASS_IS_CMP:.*]] = fir.call @_FortranAClassIs(%[[BOX_NONE]], %[[TDESC_P1_CONV]]) : (!fir.box<none>, !fir.ref<none>) -> i1
|
||||||
! CHECK: cf.cond_br %[[CLASS_IS_CMP]], ^bb[[CLASS_IS_P1]], ^bb[[NOT_CLASS_IS_P1]]
|
! CHECK: cf.cond_br %[[CLASS_IS_CMP]], ^bb[[CLASS_IS_P1]], ^bb[[NOT_CLASS_IS_P1]]
|
||||||
|
|||||||
@@ -68,15 +68,15 @@ contains
|
|||||||
|
|
||||||
! CFG-LABEL: func.func @_QMselect_type_lower_testPselect_type1(
|
! CFG-LABEL: func.func @_QMselect_type_lower_testPselect_type1(
|
||||||
! CFG-SAME: %[[ARG0:.*]]: !fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>> {fir.bindc_name = "a"}) {
|
! CFG-SAME: %[[ARG0:.*]]: !fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>> {fir.bindc_name = "a"}) {
|
||||||
! CFG: %[[TDESC_P1_ADDR:.*]] = fir.address_of(@_QMselect_type_lower_testE.dt.p1) : !fir.ref<!fir.type<{{.*}}>>
|
! CFG: %[[TDESC_P1_ADDR:.*]] = fir.type_desc !fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>
|
||||||
! CFG: %[[BOX_TDESC:.*]] = fir.box_tdesc %[[ARG0]] : (!fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>) -> !fir.tdesc<none>
|
! CFG: %[[BOX_TDESC:.*]] = fir.box_tdesc %[[ARG0]] : (!fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>) -> !fir.tdesc<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>
|
||||||
! CFG: %[[TDESC_P1_CONV:.*]] = fir.convert %[[TDESC_P1_ADDR]] : (!fir.ref<!fir.type<{{.*}}>>) -> index
|
! CFG: %[[TDESC_P1_CONV:.*]] = fir.convert %[[TDESC_P1_ADDR]] : (!fir.tdesc<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>) -> index
|
||||||
! CFG: %[[BOX_TDESC_CONV:.*]] = fir.convert %[[BOX_TDESC]] : (!fir.tdesc<none>) -> index
|
! CFG: %[[BOX_TDESC_CONV:.*]] = fir.convert %[[BOX_TDESC]] : (!fir.tdesc<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>) -> index
|
||||||
! CFG: %[[TDESC_CMP:.*]] = arith.cmpi eq, %[[TDESC_P1_CONV]], %[[BOX_TDESC_CONV]] : index
|
! CFG: %[[TDESC_CMP:.*]] = arith.cmpi eq, %[[TDESC_P1_CONV]], %[[BOX_TDESC_CONV]] : index
|
||||||
! CFG: cf.cond_br %[[TDESC_CMP]], ^[[TYPE_IS_P1_BLK:.*]], ^[[NOT_TYPE_IS_P1_BLK:.*]]
|
! CFG: cf.cond_br %[[TDESC_CMP]], ^[[TYPE_IS_P1_BLK:.*]], ^[[NOT_TYPE_IS_P1_BLK:.*]]
|
||||||
! CFG: ^[[NOT_TYPE_IS_P1_BLK]]:
|
! CFG: ^[[NOT_TYPE_IS_P1_BLK]]:
|
||||||
! CFG: %[[TDESC_P2_ADDR:.*]] = fir.address_of(@_QMselect_type_lower_testE.dt.p2) : !fir.ref<!fir.type<{{.*}}>>
|
! CFG: %[[TDESC_P2_ADDR:.*]] = fir.type_desc !fir.type<_QMselect_type_lower_testTp2{{.*}}
|
||||||
! CFG: %[[TDESC_P2_CONV:.*]] = fir.convert %[[TDESC_P2_ADDR]] : (!fir.ref<!fir.type<{{.*}}>>) -> !fir.ref<none>
|
! CFG: %[[TDESC_P2_CONV:.*]] = fir.convert %[[TDESC_P2_ADDR]] : (!fir.tdesc<{{.*}}>) -> !fir.ref<none>
|
||||||
! CFG: %[[BOX_NONE:.*]] = fir.convert %[[ARG0]] : (!fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>) -> !fir.box<none>
|
! CFG: %[[BOX_NONE:.*]] = fir.convert %[[ARG0]] : (!fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>) -> !fir.box<none>
|
||||||
! CFG: %[[CLASS_IS:.*]] = fir.call @_FortranAClassIs(%[[BOX_NONE]], %[[TDESC_P2_CONV]]) : (!fir.box<none>, !fir.ref<none>) -> i1
|
! CFG: %[[CLASS_IS:.*]] = fir.call @_FortranAClassIs(%[[BOX_NONE]], %[[TDESC_P2_CONV]]) : (!fir.box<none>, !fir.ref<none>) -> i1
|
||||||
! CFG: cf.cond_br %[[CLASS_IS]], ^bb[[CLASS_IS_P2_BLK:.*]], ^[[NOT_CLASS_IS_P2_BLK:.*]]
|
! CFG: cf.cond_br %[[CLASS_IS]], ^bb[[CLASS_IS_P2_BLK:.*]], ^[[NOT_CLASS_IS_P2_BLK:.*]]
|
||||||
@@ -87,8 +87,8 @@ contains
|
|||||||
! CFG: ^bb[[CLASS_IS_P1_BLK:[0-9]]]:
|
! CFG: ^bb[[CLASS_IS_P1_BLK:[0-9]]]:
|
||||||
! CFG: cf.br ^[[END_SELECT_BLK:.*]]
|
! CFG: cf.br ^[[END_SELECT_BLK:.*]]
|
||||||
! CFG: ^[[NOT_CLASS_IS_P2_BLK]]:
|
! CFG: ^[[NOT_CLASS_IS_P2_BLK]]:
|
||||||
! CFG: %[[TDESC_P1_ADDR:.*]] = fir.address_of(@_QMselect_type_lower_testE.dt.p1) : !fir.ref<!fir.type<{{.*}}>>
|
! CFG: %[[TDESC_P1_ADDR:.*]] = fir.type_desc !fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>
|
||||||
! CFG: %[[TDESC_P1_CONV:.*]] = fir.convert %[[TDESC_P1_ADDR]] : (!fir.ref<!fir.type<{{.*}}>>) -> !fir.ref<none>
|
! CFG: %[[TDESC_P1_CONV:.*]] = fir.convert %[[TDESC_P1_ADDR]] : (!fir.tdesc<{{.*}}>) -> !fir.ref<none>
|
||||||
! CFG: %[[BOX_NONE:.*]] = fir.convert %[[ARG0]] : (!fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>) -> !fir.box<none>
|
! CFG: %[[BOX_NONE:.*]] = fir.convert %[[ARG0]] : (!fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>) -> !fir.box<none>
|
||||||
! CFG: %[[CLASS_IS:.*]] = fir.call @_FortranAClassIs(%[[BOX_NONE]], %[[TDESC_P1_CONV]]) : (!fir.box<none>, !fir.ref<none>) -> i1
|
! CFG: %[[CLASS_IS:.*]] = fir.call @_FortranAClassIs(%[[BOX_NONE]], %[[TDESC_P1_CONV]]) : (!fir.box<none>, !fir.ref<none>) -> i1
|
||||||
! CFG: cf.cond_br %[[CLASS_IS]], ^bb[[CLASS_IS_P1_BLK]], ^bb[[NOT_CLASS_IS_P1_BLK]]
|
! CFG: cf.cond_br %[[CLASS_IS]], ^bb[[CLASS_IS_P1_BLK]], ^bb[[NOT_CLASS_IS_P1_BLK]]
|
||||||
@@ -126,15 +126,15 @@ contains
|
|||||||
! CFG: %[[GET_CLASS:.*]] = fir.call @_QMselect_type_lower_testPget_class() {{.*}} : () -> !fir.class<!fir.ptr<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>>
|
! CFG: %[[GET_CLASS:.*]] = fir.call @_QMselect_type_lower_testPget_class() {{.*}} : () -> !fir.class<!fir.ptr<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>>
|
||||||
! CFG: fir.save_result %[[GET_CLASS]] to %[[CLASS_ALLOCA]] : !fir.class<!fir.ptr<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>>, !fir.ref<!fir.class<!fir.ptr<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>>>
|
! CFG: fir.save_result %[[GET_CLASS]] to %[[CLASS_ALLOCA]] : !fir.class<!fir.ptr<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>>, !fir.ref<!fir.class<!fir.ptr<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>>>
|
||||||
! CFG: %[[LOAD_CLASS:.*]] = fir.load %[[CLASS_ALLOCA]] : !fir.ref<!fir.class<!fir.ptr<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>>>
|
! CFG: %[[LOAD_CLASS:.*]] = fir.load %[[CLASS_ALLOCA]] : !fir.ref<!fir.class<!fir.ptr<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>>>
|
||||||
! CFG: %[[TDESC_P1_ADDR:.*]] = fir.address_of(@_QMselect_type_lower_testE.dt.p1) : !fir.ref<!fir.type<{{.*}}>>
|
! CFG: %[[TDESC_P1_ADDR:.*]] = fir.type_desc !fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>
|
||||||
! CFG: %[[CLASS_TDESC:.*]] = fir.box_tdesc %[[LOAD_CLASS]] : (!fir.class<!fir.ptr<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>>) -> !fir.tdesc<none>
|
! CFG: %[[CLASS_TDESC:.*]] = fir.box_tdesc %[[LOAD_CLASS]] : (!fir.class<!fir.ptr<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>>) -> !fir.tdesc<{{.*}}>
|
||||||
! CFG: %[[TDESC_P1_CONV:.*]] = fir.convert %[[TDESC_P1_ADDR]] : (!fir.ref<!fir.type<{{.*}}>>) -> index
|
! CFG: %[[TDESC_P1_CONV:.*]] = fir.convert %[[TDESC_P1_ADDR]] : (!fir.tdesc{{.*}}>) -> index
|
||||||
! CFG: %[[BOX_TDESC_CONV:.*]] = fir.convert %[[CLASS_TDESC]] : (!fir.tdesc<none>) -> index
|
! CFG: %[[BOX_TDESC_CONV:.*]] = fir.convert %[[CLASS_TDESC]] : (!fir.tdesc<{{.*}}>) -> index
|
||||||
! CFG: %[[TDESC_CMP:.*]] = arith.cmpi eq, %[[TDESC_P1_CONV]], %[[BOX_TDESC_CONV]] : index
|
! CFG: %[[TDESC_CMP:.*]] = arith.cmpi eq, %[[TDESC_P1_CONV]], %[[BOX_TDESC_CONV]] : index
|
||||||
! CFG: cf.cond_br %[[TDESC_CMP]], ^[[TYPE_IS_P1_BLK:.*]], ^[[NOT_TYPE_IS_P1_BLK:.*]]
|
! CFG: cf.cond_br %[[TDESC_CMP]], ^[[TYPE_IS_P1_BLK:.*]], ^[[NOT_TYPE_IS_P1_BLK:.*]]
|
||||||
! CFG: ^[[NOT_TYPE_IS_P1_BLK]]:
|
! CFG: ^[[NOT_TYPE_IS_P1_BLK]]:
|
||||||
! CFG: %[[TDESC_P1_ADDR:.*]] = fir.address_of(@_QMselect_type_lower_testE.dt.p1) : !fir.ref<!fir.type<{{.*}}>>
|
! CFG: %[[TDESC_P1_ADDR:.*]] = fir.type_desc !fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>
|
||||||
! CFG: %[[TDESC_P1_CONV:.*]] = fir.convert %[[TDESC_P1_ADDR]] : (!fir.ref<!fir.type<{{.*}}>>) -> !fir.ref<none>
|
! CFG: %[[TDESC_P1_CONV:.*]] = fir.convert %[[TDESC_P1_ADDR]] : (!fir.tdesc{{.*}}>) -> !fir.ref<none>
|
||||||
! CFG: %[[BOX_NONE:.*]] = fir.convert %[[LOAD_CLASS]] : (!fir.class<!fir.ptr<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>>) -> !fir.box<none>
|
! CFG: %[[BOX_NONE:.*]] = fir.convert %[[LOAD_CLASS]] : (!fir.class<!fir.ptr<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>>) -> !fir.box<none>
|
||||||
! CFG: %[[CLASS_IS:.*]] = fir.call @_FortranAClassIs(%[[BOX_NONE]], %[[TDESC_P1_CONV]]) : (!fir.box<none>, !fir.ref<none>) -> i1
|
! CFG: %[[CLASS_IS:.*]] = fir.call @_FortranAClassIs(%[[BOX_NONE]], %[[TDESC_P1_CONV]]) : (!fir.box<none>, !fir.ref<none>) -> i1
|
||||||
! CFG: cf.cond_br %[[CLASS_IS]], ^[[CLASS_IS_BLK:.*]], ^[[NOT_CLASS_IS_BLK:.*]]
|
! CFG: cf.cond_br %[[CLASS_IS]], ^[[CLASS_IS_BLK:.*]], ^[[NOT_CLASS_IS_BLK:.*]]
|
||||||
@@ -176,15 +176,15 @@ contains
|
|||||||
! CFG-LABEL: func.func @_QMselect_type_lower_testPselect_type3(
|
! CFG-LABEL: func.func @_QMselect_type_lower_testPselect_type3(
|
||||||
! CFG-SAME: %[[ARG0:.*]]: !fir.ref<!fir.class<!fir.ptr<!fir.array<?x!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>>>> {fir.bindc_name = "a"}) {
|
! CFG-SAME: %[[ARG0:.*]]: !fir.ref<!fir.class<!fir.ptr<!fir.array<?x!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>>>> {fir.bindc_name = "a"}) {
|
||||||
! CFG: %[[SELECTOR:.*]] = fir.embox %{{.*}} source_box %{{.*}} : (!fir.ref<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>, !fir.class<{{.*}}>) -> !fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>
|
! CFG: %[[SELECTOR:.*]] = fir.embox %{{.*}} source_box %{{.*}} : (!fir.ref<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>, !fir.class<{{.*}}>) -> !fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>
|
||||||
! CFG: %[[TDESC_P1_ADDR:.*]] = fir.address_of(@_QMselect_type_lower_testE.dt.p1) : !fir.ref<!fir.type<{{.*}}>>
|
! CFG: %[[TDESC_P1_ADDR:.*]] = fir.type_desc !fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>
|
||||||
! CFG: %[[SELECTOR_TDESC:.*]] = fir.box_tdesc %[[SELECTOR]] : (!fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>) -> !fir.tdesc<none>
|
! CFG: %[[SELECTOR_TDESC:.*]] = fir.box_tdesc %[[SELECTOR]] : (!fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>) -> !fir.tdesc<{{.*}}>
|
||||||
! CFG: %[[TDESC_P1_CONV:.*]] = fir.convert %[[TDESC_P1_ADDR]] : (!fir.ref<!fir.type<{{.*}}>>) -> index
|
! CFG: %[[TDESC_P1_CONV:.*]] = fir.convert %[[TDESC_P1_ADDR]] : (!fir.tdesc{{.*}}>) -> index
|
||||||
! CFG: %[[TDESC_CONV:.*]] = fir.convert %[[SELECTOR_TDESC]] : (!fir.tdesc<none>) -> index
|
! CFG: %[[TDESC_CONV:.*]] = fir.convert %[[SELECTOR_TDESC]] : (!fir.tdesc<{{.*}}>) -> index
|
||||||
! CFG: %[[TDESC_CMP:.*]] = arith.cmpi eq, %[[TDESC_P1_CONV]], %[[TDESC_CONV]] : index
|
! CFG: %[[TDESC_CMP:.*]] = arith.cmpi eq, %[[TDESC_P1_CONV]], %[[TDESC_CONV]] : index
|
||||||
! CFG: cf.cond_br %[[TDESC_CMP]], ^[[TYPE_IS_P1_BLK:.*]], ^[[NOT_TYPE_IS_P1_BLK:.*]]
|
! CFG: cf.cond_br %[[TDESC_CMP]], ^[[TYPE_IS_P1_BLK:.*]], ^[[NOT_TYPE_IS_P1_BLK:.*]]
|
||||||
! CFG: ^[[NOT_TYPE_IS_P1_BLK]]:
|
! CFG: ^[[NOT_TYPE_IS_P1_BLK]]:
|
||||||
! CFG: %[[TDESC_P1_ADDR:.*]] = fir.address_of(@_QMselect_type_lower_testE.dt.p1) : !fir.ref<!fir.type<{{.*}}>>
|
! CFG: %[[TDESC_P1_ADDR:.*]] = fir.type_desc !fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>
|
||||||
! CFG: %[[TDESC_P1_CONV:.*]] = fir.convert %[[TDESC_P1_ADDR]] : (!fir.ref<!fir.type<{{.*}}>>) -> !fir.ref<none>
|
! CFG: %[[TDESC_P1_CONV:.*]] = fir.convert %[[TDESC_P1_ADDR]] : (!fir.tdesc{{.*}}>) -> !fir.ref<none>
|
||||||
! CFG: %[[BOX_NONE:.*]] = fir.convert %[[SELECTOR]] : (!fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>) -> !fir.box<none>
|
! CFG: %[[BOX_NONE:.*]] = fir.convert %[[SELECTOR]] : (!fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>) -> !fir.box<none>
|
||||||
! CFG: %[[CLASS_IS:.*]] = fir.call @_FortranAClassIs(%[[BOX_NONE]], %[[TDESC_P1_CONV]]) : (!fir.box<none>, !fir.ref<none>) -> i1
|
! CFG: %[[CLASS_IS:.*]] = fir.call @_FortranAClassIs(%[[BOX_NONE]], %[[TDESC_P1_CONV]]) : (!fir.box<none>, !fir.ref<none>) -> i1
|
||||||
! CFG: cf.cond_br %[[CLASS_IS]], ^[[CLASS_IS_BLK:.*]], ^[[NOT_CLASS_IS:.*]]
|
! CFG: cf.cond_br %[[CLASS_IS]], ^[[CLASS_IS_BLK:.*]], ^[[NOT_CLASS_IS:.*]]
|
||||||
@@ -222,25 +222,25 @@ contains
|
|||||||
|
|
||||||
! CFG-LABEL: func.func @_QMselect_type_lower_testPselect_type4(
|
! CFG-LABEL: func.func @_QMselect_type_lower_testPselect_type4(
|
||||||
! CFG-SAME: %[[ARG0:.*]]: !fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>> {fir.bindc_name = "a"}) {
|
! CFG-SAME: %[[ARG0:.*]]: !fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>> {fir.bindc_name = "a"}) {
|
||||||
! CFG: %[[TDESC_P3_8_ADDR:.*]] = fir.address_of(@_QMselect_type_lower_testE.dt.p3.8) : !fir.ref<!fir.type<{{.*}}>>
|
! CFG: %[[TDESC_P3_8_ADDR:.*]] = fir.type_desc !fir.type<_QMselect_type_lower_testTp3K8
|
||||||
! CFG: %[[BOX_TDESC:.*]] = fir.box_tdesc %[[ARG0]] : (!fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>) -> !fir.tdesc<none>
|
! CFG: %[[BOX_TDESC:.*]] = fir.box_tdesc %[[ARG0]] : (!fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>) -> !fir.tdesc<{{.*}}>
|
||||||
! CFG: %[[TDESC_P3_8_CONV:.*]] = fir.convert %[[TDESC_P3_8_ADDR]] : (!fir.ref<!fir.type<{{.*}}>>) -> index
|
! CFG: %[[TDESC_P3_8_CONV:.*]] = fir.convert %[[TDESC_P3_8_ADDR]] : (!fir.tdesc{{.*}}>) -> index
|
||||||
! CFG: %[[BOX_TDESC_CONV:.*]] = fir.convert %[[BOX_TDESC]] : (!fir.tdesc<none>) -> index
|
! CFG: %[[BOX_TDESC_CONV:.*]] = fir.convert %[[BOX_TDESC]] : (!fir.tdesc<{{.*}}>) -> index
|
||||||
! CFG: %[[TDESC_CMP:.*]] = arith.cmpi eq, %[[TDESC_P3_8_CONV]], %[[BOX_TDESC_CONV]] : index
|
! CFG: %[[TDESC_CMP:.*]] = arith.cmpi eq, %[[TDESC_P3_8_CONV]], %[[BOX_TDESC_CONV]] : index
|
||||||
! CFG: cf.cond_br %[[TDESC_CMP]], ^[[P3_8_BLK:.*]], ^[[NOT_P3_8_BLK:.*]]
|
! CFG: cf.cond_br %[[TDESC_CMP]], ^[[P3_8_BLK:.*]], ^[[NOT_P3_8_BLK:.*]]
|
||||||
! CFG: ^[[NOT_P3_8_BLK]]:
|
! CFG: ^[[NOT_P3_8_BLK]]:
|
||||||
! CFG: %[[TDESC_P3_4_ADDR:.*]] = fir.address_of(@_QMselect_type_lower_testE.dt.p3.4) : !fir.ref<!fir.type<{{.*}}>>
|
! CFG: %[[TDESC_P3_4_ADDR:.*]] = fir.type_desc !fir.type<_QMselect_type_lower_testTp3K4
|
||||||
! CFG: %[[BOX_TDESC:.*]] = fir.box_tdesc %[[ARG0]] : (!fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>) -> !fir.tdesc<none>
|
! CFG: %[[BOX_TDESC:.*]] = fir.box_tdesc %[[ARG0]] : (!fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>) -> !fir.tdesc<{{.*}}>
|
||||||
! CFG: %[[TDESC_P3_4_CONV:.*]] = fir.convert %[[TDESC_P3_4_ADDR]] : (!fir.ref<!fir.type<{{.*}}>>) -> index
|
! CFG: %[[TDESC_P3_4_CONV:.*]] = fir.convert %[[TDESC_P3_4_ADDR]] : (!fir.tdesc{{.*}}>) -> index
|
||||||
! CFG: %[[BOX_TDESC_CONV:.*]] = fir.convert %[[BOX_TDESC]] : (!fir.tdesc<none>) -> index
|
! CFG: %[[BOX_TDESC_CONV:.*]] = fir.convert %[[BOX_TDESC]] : (!fir.tdesc<{{.*}}>) -> index
|
||||||
! CFG: %[[TDESC_CMP:.*]] = arith.cmpi eq, %[[TDESC_P3_4_CONV]], %[[BOX_TDESC_CONV]] : index
|
! CFG: %[[TDESC_CMP:.*]] = arith.cmpi eq, %[[TDESC_P3_4_CONV]], %[[BOX_TDESC_CONV]] : index
|
||||||
! CFG: cf.cond_br %[[TDESC_CMP]], ^[[P3_4_BLK:.*]], ^[[NOT_P3_4_BLK:.*]]
|
! CFG: cf.cond_br %[[TDESC_CMP]], ^[[P3_4_BLK:.*]], ^[[NOT_P3_4_BLK:.*]]
|
||||||
! CFG: ^[[P3_8_BLK]]:
|
! CFG: ^[[P3_8_BLK]]:
|
||||||
! CFG: _FortranAioOutputAscii
|
! CFG: _FortranAioOutputAscii
|
||||||
! CFG: cf.br ^bb[[EXIT_SELECT_BLK:[0-9]]]
|
! CFG: cf.br ^bb[[EXIT_SELECT_BLK:[0-9]]]
|
||||||
! CFG: ^[[NOT_P3_4_BLK]]:
|
! CFG: ^[[NOT_P3_4_BLK]]:
|
||||||
! CFG: %[[TDESC_P1_ADDR:.*]] = fir.address_of(@_QMselect_type_lower_testE.dt.p1) : !fir.ref<!fir.type<{{.*}}>>
|
! CFG: %[[TDESC_P1_ADDR:.*]] = fir.type_desc !fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>
|
||||||
! CFG: %[[TDESC_P1_CONV:.*]] = fir.convert %[[TDESC_P1_ADDR]] : (!fir.ref<!fir.type<{{.*}}>>) -> !fir.ref<none>
|
! CFG: %[[TDESC_P1_CONV:.*]] = fir.convert %[[TDESC_P1_ADDR]] : (!fir.tdesc{{.*}}>) -> !fir.ref<none>
|
||||||
! CFG: %[[BOX_NONE:.*]] = fir.convert %[[ARG0]] : (!fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>) -> !fir.box<none>
|
! CFG: %[[BOX_NONE:.*]] = fir.convert %[[ARG0]] : (!fir.class<!fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>>) -> !fir.box<none>
|
||||||
! CFG: %[[CLASS_IS:.*]] = fir.call @_FortranAClassIs(%[[BOX_NONE]], %[[TDESC_P1_CONV]]) : (!fir.box<none>, !fir.ref<none>) -> i1
|
! CFG: %[[CLASS_IS:.*]] = fir.call @_FortranAClassIs(%[[BOX_NONE]], %[[TDESC_P1_CONV]]) : (!fir.box<none>, !fir.ref<none>) -> i1
|
||||||
! CFG: cf.cond_br %[[CLASS_IS]], ^[[P1_BLK:.*]], ^[[NOT_P1_BLK:.*]]
|
! CFG: cf.cond_br %[[CLASS_IS]], ^[[P1_BLK:.*]], ^[[NOT_P1_BLK:.*]]
|
||||||
@@ -409,8 +409,8 @@ contains
|
|||||||
|
|
||||||
! CFG-LABEL: func.func @_QMselect_type_lower_testPselect_type7(
|
! CFG-LABEL: func.func @_QMselect_type_lower_testPselect_type7(
|
||||||
! CFG-SAME: %[[ARG0:.*]]: !fir.class<none> {fir.bindc_name = "a"}) {
|
! CFG-SAME: %[[ARG0:.*]]: !fir.class<none> {fir.bindc_name = "a"}) {
|
||||||
! CFG: %[[TDESC_P4_ADDR:.*]] = fir.address_of(@_QMselect_type_lower_testE.dt.p4) : !fir.ref<!fir.type<{{.*}}>>
|
! CFG: %[[TDESC_P4_ADDR:.*]] = fir.type_desc !fir.type<_QMselect_type_lower_testTp4
|
||||||
! CFG: %[[TDESC_P4_CONV:.*]] = fir.convert %[[TDESC_P4_ADDR]] : (!fir.ref<!fir.type<{{.*}}>>) -> !fir.ref<none>
|
! CFG: %[[TDESC_P4_CONV:.*]] = fir.convert %[[TDESC_P4_ADDR]] : (!fir.tdesc{{.*}}>) -> !fir.ref<none>
|
||||||
! CFG: %[[BOX_NONE:.*]] = fir.convert %[[ARG0]] : (!fir.class<none>) -> !fir.box<none>
|
! CFG: %[[BOX_NONE:.*]] = fir.convert %[[ARG0]] : (!fir.class<none>) -> !fir.box<none>
|
||||||
! CFG: %[[CLASS_IS_P4:.*]] = fir.call @_FortranAClassIs(%[[BOX_NONE]], %[[TDESC_P4_CONV]]) : (!fir.box<none>, !fir.ref<none>) -> i1
|
! CFG: %[[CLASS_IS_P4:.*]] = fir.call @_FortranAClassIs(%[[BOX_NONE]], %[[TDESC_P4_CONV]]) : (!fir.box<none>, !fir.ref<none>) -> i1
|
||||||
! CFG: cf.cond_br %[[CLASS_IS_P4]], ^[[CLASS_IS_P4_BLK:.*]], ^[[CLASS_NOT_P4_BLK:.*]]
|
! CFG: cf.cond_br %[[CLASS_IS_P4]], ^[[CLASS_IS_P4_BLK:.*]], ^[[CLASS_NOT_P4_BLK:.*]]
|
||||||
@@ -419,16 +419,16 @@ contains
|
|||||||
! CFG: ^bb[[CLASS_IS_P1_BLK:[0-9]]]:
|
! CFG: ^bb[[CLASS_IS_P1_BLK:[0-9]]]:
|
||||||
! CFG: cf.br ^[[EXIT_SELECT_BLK:.*]]
|
! CFG: cf.br ^[[EXIT_SELECT_BLK:.*]]
|
||||||
! CFG: ^bb[[CLASS_NOT_P2_BLK:[0-9]]]:
|
! CFG: ^bb[[CLASS_NOT_P2_BLK:[0-9]]]:
|
||||||
! CFG: %[[TDESC_P1_ADDR:.*]] = fir.address_of(@_QMselect_type_lower_testE.dt.p1) : !fir.ref<!fir.type<{{.*}}>>
|
! CFG: %[[TDESC_P1_ADDR:.*]] = fir.type_desc !fir.type<_QMselect_type_lower_testTp1{a:i32,b:i32}>
|
||||||
! CFG: %[[TDESC_P1_CONV:.*]] = fir.convert %[[TDESC_P1_ADDR]] : (!fir.ref<!fir.type<{{.*}}>>) -> !fir.ref<none>
|
! CFG: %[[TDESC_P1_CONV:.*]] = fir.convert %[[TDESC_P1_ADDR]] : (!fir.tdesc{{.*}}>) -> !fir.ref<none>
|
||||||
! CFG: %[[BOX_NONE:.*]] = fir.convert %[[ARG0]] : (!fir.class<none>) -> !fir.box<none>
|
! CFG: %[[BOX_NONE:.*]] = fir.convert %[[ARG0]] : (!fir.class<none>) -> !fir.box<none>
|
||||||
! CFG: %[[CLASS_IS_P1:.*]] = fir.call @_FortranAClassIs(%[[BOX_NONE]], %[[TDESC_P1_CONV]]) : (!fir.box<none>, !fir.ref<none>) -> i1
|
! CFG: %[[CLASS_IS_P1:.*]] = fir.call @_FortranAClassIs(%[[BOX_NONE]], %[[TDESC_P1_CONV]]) : (!fir.box<none>, !fir.ref<none>) -> i1
|
||||||
! CFG: cf.cond_br %[[CLASS_IS_P1]], ^bb[[CLASS_IS_P1_BLK]], ^bb[[CLASS_NOT_P1_BLK]]
|
! CFG: cf.cond_br %[[CLASS_IS_P1]], ^bb[[CLASS_IS_P1_BLK]], ^bb[[CLASS_NOT_P1_BLK]]
|
||||||
! CFG: ^bb[[CLASS_IS_P2_BLK:[0-9]]]:
|
! CFG: ^bb[[CLASS_IS_P2_BLK:[0-9]]]:
|
||||||
! CFG: cf.br ^[[EXIT_SELECT_BLK]]
|
! CFG: cf.br ^[[EXIT_SELECT_BLK]]
|
||||||
! CFG: ^[[CLASS_NOT_P4_BLK]]:
|
! CFG: ^[[CLASS_NOT_P4_BLK]]:
|
||||||
! CFG: %[[TDESC_P2_ADDR:.*]] = fir.address_of(@_QMselect_type_lower_testE.dt.p2) : !fir.ref<!fir.type<{{.*}}>>
|
! CFG: %[[TDESC_P2_ADDR:.*]] = fir.type_desc !fir.type<_QMselect_type_lower_testTp2
|
||||||
! CFG: %[[TDESC_P2_CONV:.*]] = fir.convert %[[TDESC_P2_ADDR]] : (!fir.ref<!fir.type<{{.*}}>>) -> !fir.ref<none>
|
! CFG: %[[TDESC_P2_CONV:.*]] = fir.convert %[[TDESC_P2_ADDR]] : (!fir.tdesc{{.*}}>) -> !fir.ref<none>
|
||||||
! CFG: %[[BOX_NONE:.*]] = fir.convert %[[ARG0]] : (!fir.class<none>) -> !fir.box<none>
|
! CFG: %[[BOX_NONE:.*]] = fir.convert %[[ARG0]] : (!fir.class<none>) -> !fir.box<none>
|
||||||
! CFG: %[[CLASS_IS_P2:.*]] = fir.call @_FortranAClassIs(%[[BOX_NONE]], %[[TDESC_P2_CONV]]) : (!fir.box<none>, !fir.ref<none>) -> i1
|
! CFG: %[[CLASS_IS_P2:.*]] = fir.call @_FortranAClassIs(%[[BOX_NONE]], %[[TDESC_P2_CONV]]) : (!fir.box<none>, !fir.ref<none>) -> i1
|
||||||
! CFG: cf.cond_br %[[CLASS_IS_P2]], ^bb[[CLASS_IS_P2_BLK]], ^bb[[CLASS_NOT_P2_BLK]]
|
! CFG: cf.cond_br %[[CLASS_IS_P2]], ^bb[[CLASS_IS_P2_BLK]], ^bb[[CLASS_NOT_P2_BLK]]
|
||||||
|
|||||||
Reference in New Issue
Block a user