Index: flang/include/flang/Lower/ConvertVariable.h =================================================================== --- flang/include/flang/Lower/ConvertVariable.h +++ flang/include/flang/Lower/ConvertVariable.h @@ -88,7 +88,8 @@ /// This handles the local instantiation of the target variable. mlir::Value genInitialDataTarget(Fortran::lower::AbstractConverter &, mlir::Location, mlir::Type boxType, - const SomeExpr &initialTarget); + const SomeExpr &initialTarget, + bool couldBeInEquivalence = false); /// Call \p genInit to generate code inside \p global initializer region. void createGlobalInitialization( Index: flang/include/flang/Lower/Mangler.h =================================================================== --- flang/include/flang/Lower/Mangler.h +++ flang/include/flang/Lower/Mangler.h @@ -91,6 +91,9 @@ Fortran::common::TypeCategory::Derived); } +/// Return the compiler-generated name of a static namelist variable descriptor. +std::string globalNamelistDescriptorName(const Fortran::semantics::Symbol &sym); + } // namespace lower::mangle } // namespace Fortran Index: flang/include/flang/Lower/PFTBuilder.h =================================================================== --- flang/include/flang/Lower/PFTBuilder.h +++ flang/include/flang/Lower/PFTBuilder.h @@ -37,7 +37,6 @@ struct FunctionLikeUnit; using EvaluationList = std::list; -using LabelEvalMap = llvm::DenseMap; /// Provide a variant like container that can hold references. It can hold /// constant or mutable references. It is used in the other classes to provide @@ -445,7 +444,7 @@ const semantics::Symbol *namingSymbol; /// Compiler generated symbol with the aggregate initial value if any. const semantics::Symbol *initialValueSymbol = nullptr; - /// Is this a global aggregate ? + /// Is this a global aggregate? bool isGlobalAggregate; }; @@ -485,10 +484,10 @@ return std::visit([](const auto &x) { return x.isGlobal(); }, var); } - /// Is this a module variable ? - bool isModuleVariable() const { + /// Is this a module or submodule variable? + bool isModuleOrSubmoduleVariable() const { const semantics::Scope *scope = getOwningScope(); - return scope && scope->IsModule(); + return scope && scope->kind() == Fortran::semantics::Scope::Kind::Module; } const Fortran::semantics::Scope *getOwningScope() const { @@ -522,7 +521,7 @@ return s->aliaser; return false; } - std::size_t getAlias() const { + std::size_t getAliasOffset() const { if (auto *s = std::get_if(&var)) return s->aliasOffset; return 0; @@ -568,6 +567,25 @@ std::variant var; }; +using VariableList = std::vector; +using ScopeVariableListMap = + std::map; + +/// Find or create an ordered list of the equivalence sets and variables that +/// appear in \p scope. The result is cached in \p map. +const VariableList &getScopeVariableList(const Fortran::semantics::Scope &scope, + ScopeVariableListMap &map); + +/// Create an ordered list of the equivalence sets and variables that appear in +/// \p scope. The result is not cached. +VariableList getScopeVariableList(const Fortran::semantics::Scope &scope); + +/// Create an ordered list of the equivalence sets and variables that \p symbol +/// depends on. \p symbol itself will be the last variable in the list. +VariableList getDependentVariableList(const Fortran::semantics::Symbol &); + +void dump(VariableList &, std::string s = {}); // `s` is an optional dump label + /// Function-like units may contain evaluations (executable statements) and /// nested function-like units (internal procedures and function statements). struct FunctionLikeUnit : public ProgramUnit { @@ -597,8 +615,6 @@ FunctionLikeUnit(FunctionLikeUnit &&) = default; FunctionLikeUnit(const FunctionLikeUnit &) = delete; - std::vector getOrderedSymbolTable() { return varList[0]; } - bool isMainProgram() const { return endStmt.isA>(); } @@ -652,9 +668,13 @@ LLVM_DUMP_METHOD void dump() const; - /// Anonymous programs do not have a begin statement + /// Get the function scope. + const Fortran::semantics::Scope &getScope() const { return *scope; } + + /// Anonymous programs do not have a begin statement. std::optional beginStmt; FunctionStatement endStmt; + const semantics::Scope *scope; EvaluationList evaluationList; LabelEvalMap labelEvaluationMap; SymbolLabelMap assignSymbolLabelMap; @@ -673,7 +693,6 @@ const semantics::Symbol *primaryResult{nullptr}; /// Terminal basic block (if any) mlir::Block *finalBlock{}; - std::vector> varList; HostAssociations hostAssociations; }; @@ -694,8 +713,6 @@ LLVM_DUMP_METHOD void dump() const; - std::vector getOrderedSymbolTable() { return varList[0]; } - /// Get the starting source location for this module like unit. parser::CharBlock getStartingSourceLoc() const; @@ -706,7 +723,6 @@ ModuleStatement endStmt; std::list nestedFunctions; EvaluationList evaluationList; - std::vector> varList; }; /// Block data units contain the variables and data initializers for common @@ -746,6 +762,9 @@ const semantics::CommonBlockList &getCommonBlocks() const { return commonBlocks; } + ScopeVariableListMap &getScopeVariableListMap() { + return scopeVariableListMap; + } /// LLVM dump method on a Program. LLVM_DUMP_METHOD void dump() const; @@ -753,13 +772,9 @@ private: std::list units; semantics::CommonBlockList commonBlocks; + ScopeVariableListMap scopeVariableListMap; // module and submodule scopes }; -/// Return the list of variables that appears in the specification expressions -/// of a function result. -std::vector -buildFuncResultDependencyList(const Fortran::semantics::Symbol &); - /// Helper to get location from FunctionLikeUnit/ModuleLikeUnit begin/end /// statements. template @@ -777,6 +792,14 @@ [](auto &p) -> ParentType * { return getAncestor(p); }}); } +/// Get the "global" scopeVariableListMap, stored in the pft root node. +template +ScopeVariableListMap &getScopeVariableListMap(A &node) { + Program *pftRoot = getAncestor(node); + assert(pftRoot && "pft must have a root"); + return pftRoot->getScopeVariableListMap(); +} + /// Call the provided \p callBack on all symbols that are referenced inside \p /// funit. void visitAllSymbols(const FunctionLikeUnit &funit, Index: flang/lib/Lower/Bridge.cpp =================================================================== --- flang/lib/Lower/Bridge.cpp +++ flang/lib/Lower/Bridge.cpp @@ -3079,10 +3079,6 @@ void genFIR(const Fortran::parser::SelectTypeStmt &) {} // nop void genFIR(const Fortran::parser::TypeGuardStmt &) {} // nop - void genFIR(const Fortran::parser::NamelistStmt &) { - TODO(toLocation(), "NamelistStmt lowering"); - } - /// Generate FIR for the Evaluation `eval`. void genFIR(Fortran::lower::pft::Evaluation &eval, bool unstructuredContext = true) { @@ -3204,9 +3200,13 @@ Fortran::lower::genThreadprivateOp(*this, var); } - /// Prepare to translate a new function + /// Start translation of a function. void startNewFunction(Fortran::lower::pft::FunctionLikeUnit &funit) { assert(!builder && "expected nullptr"); + const Fortran::semantics::Scope &scope = funit.getScope(); + LLVM_DEBUG(llvm::dbgs() << "\n[bridge - startNewFunction]"; + if (auto *sym = scope.symbol()) llvm::dbgs() << " " << *sym; + llvm::dbgs() << "\n"); Fortran::lower::CalleeInterface callee(funit, *this); mlir::func::FuncOp func = callee.addEntryBlockAndMapArguments(); builder = new fir::FirOpBuilder(func, bridge.getKindMap()); @@ -3217,33 +3217,36 @@ mapDummiesAndResults(funit, callee); - // Note: not storing Variable references because getOrderedSymbolTable - // below returns a temporary. + // Non-primary results of a function with multiple entry points. + // These result values share storage with the primary result. llvm::SmallVector deferredFuncResultList; - // Backup actual argument for entry character results - // with different lengths. It needs to be added to the non - // primary results symbol before mapSymbolAttributes is called. + // Backup actual argument for entry character results with different + // lengths. It needs to be added to the non-primary results symbol before + // mapSymbolAttributes is called. Fortran::lower::SymbolBox resultArg; if (std::optional passedResult = callee.getPassedResult()) resultArg = lookupSymbol(passedResult->entity->get()); Fortran::lower::AggregateStoreMap storeMap; - // The front-end is currently not adding module variables referenced - // in a module procedure as host associated. As a result we need to - // instantiate all module variables here if this is a module procedure. - // It is likely that the front-end behavior should change here. - // This also applies to internal procedures inside module procedures. - if (auto *module = Fortran::lower::pft::getAncestor< - Fortran::lower::pft::ModuleLikeUnit>(funit)) - for (const Fortran::lower::pft::Variable &var : - module->getOrderedSymbolTable()) - instantiateVar(var, storeMap); + // Map all containing submodule and module equivalences and variables, in + // case they are referenced. It might be better to limit this to variables + // that are actually referenced, although that is more complicated when + // there are equivalenced variables. + auto &scopeVariableListMap = + Fortran::lower::pft::getScopeVariableListMap(funit); + for (auto *scp = &scope.parent(); !scp->IsGlobal(); scp = &scp->parent()) + if (scp->kind() == Fortran::semantics::Scope::Kind::Module) + for (const auto &var : Fortran::lower::pft::getScopeVariableList( + *scp, scopeVariableListMap)) + instantiateVar(var, storeMap); + + // Map function equivalences and variables. mlir::Value primaryFuncResultStorage; for (const Fortran::lower::pft::Variable &var : - funit.getOrderedSymbolTable()) { + Fortran::lower::pft::getScopeVariableList(scope)) { // Always instantiate aggregate storage blocks. if (var.isAggregateStore()) { instantiateVar(var, storeMap); @@ -3251,9 +3254,9 @@ } const Fortran::semantics::Symbol &sym = var.getSymbol(); if (funit.parentHasHostAssoc()) { - // Never instantitate host associated variables, as they are already - // instantiated from an argument tuple. Instead, just bind the symbol to - // the reference to the host variable, which must be in the map. + // Never instantiate host associated variables, as they are already + // instantiated from an argument tuple. Instead, just bind the symbol + // to the host variable, which must be in the map. const Fortran::semantics::Symbol &ultimate = sym.GetUltimate(); if (funit.parentHostAssoc().isAssociated(ultimate)) { Fortran::lower::SymbolBox hostBox = lookupSymbol(ultimate); @@ -3379,7 +3382,7 @@ startBlock(newBlock); } - /// Emit return and cleanup after the function has been translated. + /// Finish translation of a function. void endNewFunction(Fortran::lower::pft::FunctionLikeUnit &funit) { setCurrentPosition(Fortran::lower::pft::stmtSourceLoc(funit.endStmt)); if (funit.isMainProgram()) @@ -3387,12 +3390,15 @@ else genFIRProcedureExit(funit, funit.getSubprogramSymbol()); funit.finalBlock = nullptr; - LLVM_DEBUG(llvm::dbgs() << "*** Lowering result:\n\n" + LLVM_DEBUG(llvm::dbgs() << "\n[bridge - endNewFunction"; + if (auto *sym = funit.scope->symbol()) llvm::dbgs() + << " " << sym->name(); + llvm::dbgs() << "] generated IR:\n\n" << *builder->getFunction() << '\n'); - // FIXME: Simplification should happen in a normal pass, not here. + // Eliminate dead code as a prerequisite to calling other IR passes. + // FIXME: This simplification should happen in a normal pass, not here. mlir::IRRewriter rewriter(*builder); - (void)mlir::simplifyRegions(rewriter, - {builder->getRegion()}); // remove dead code + (void)mlir::simplifyRegions(rewriter, {builder->getRegion()}); delete builder; builder = nullptr; hostAssocTuple = mlir::Value{}; @@ -3446,14 +3452,6 @@ /// Lower a procedure (nest). void lowerFunc(Fortran::lower::pft::FunctionLikeUnit &funit) { - if (!funit.isMainProgram()) { - const Fortran::semantics::Symbol &procSymbol = - funit.getSubprogramSymbol(); - if (procSymbol.owner().IsSubmodule()) - TODO(toLocation(), "support for submodules"); - if (Fortran::semantics::IsSeparateModuleProcedureInterface(&procSymbol)) - TODO(toLocation(), "separate module procedure"); - } setCurrentPosition(funit.getStartingSourceLoc()); for (int entryIndex = 0, last = funit.entryPointList.size(); entryIndex < last; ++entryIndex) { @@ -3473,8 +3471,10 @@ void lowerModuleDeclScope(Fortran::lower::pft::ModuleLikeUnit &mod) { setCurrentPosition(mod.getStartingSourceLoc()); createGlobalOutsideOfFunctionLowering([&]() { - for (const Fortran::lower::pft::Variable &var : - mod.getOrderedSymbolTable()) { + auto &scopeVariableListMap = + Fortran::lower::pft::getScopeVariableListMap(mod); + for (const auto &var : Fortran::lower::pft::getScopeVariableList( + mod.getScope(), scopeVariableListMap)) { // Only define the variables owned by this module. const Fortran::semantics::Scope *owningScope = var.getOwningScope(); if (!owningScope || mod.getScope() == *owningScope) Index: flang/lib/Lower/ConvertVariable.cpp =================================================================== --- flang/lib/Lower/ConvertVariable.cpp +++ flang/lib/Lower/ConvertVariable.cpp @@ -191,7 +191,8 @@ /// create initial-data-target fir.box in a global initializer region. mlir::Value Fortran::lower::genInitialDataTarget( Fortran::lower::AbstractConverter &converter, mlir::Location loc, - mlir::Type boxType, const Fortran::lower::SomeExpr &initialTarget) { + mlir::Type boxType, const Fortran::lower::SomeExpr &initialTarget, + bool couldBeInEquivalence) { Fortran::lower::SymMap globalOpSymMap; Fortran::lower::AggregateStoreMap storeMap; Fortran::lower::StatementContext stmtCtx; @@ -207,8 +208,19 @@ // context. if (hasDerivedTypeWithLengthParameters(sym)) TODO(loc, "initial-data-target with derived type length parameters"); - auto var = Fortran::lower::pft::Variable(sym, /*global=*/true); + if (couldBeInEquivalence) { + auto dependentVariableList = + Fortran::lower::pft::getDependentVariableList(sym); + for (Fortran::lower::pft::Variable var : dependentVariableList) { + if (!var.isAggregateStore()) + break; + instantiateVariable(converter, var, globalOpSymMap, storeMap); + } + var = dependentVariableList.back(); + assert(var.getSymbol().name() == sym->name() && + "missing symbol in dependence list"); + } Fortran::lower::instantiateVariable(converter, var, globalOpSymMap, storeMap); } @@ -538,7 +550,7 @@ // with `linkonce_odr` LLVM linkage. if (var.hasSymbol() && isRuntimeTypeInfoData(var.getSymbol())) return builder.createLinkOnceODRLinkage(); - if (var.isModuleVariable()) + if (var.isModuleOrSubmoduleVariable()) return {}; // external linkage // Otherwise, the variable is owned by a procedure and must not be visible in // other compilation units. @@ -558,7 +570,7 @@ mlir::Location loc = genLocation(converter, sym); fir::GlobalOp global = builder.getNamedGlobal(globalName); mlir::StringAttr linkage = getLinkageAttribute(builder, var); - if (var.isModuleVariable()) { + if (var.isModuleOrSubmoduleVariable()) { // A module global was or will be defined when lowering the module. Emit // only a declaration if the global does not exist at that point. global = declareGlobal(converter, var, globalName, linkage); @@ -713,7 +725,7 @@ getAggregateStore(Fortran::lower::AggregateStoreMap &storeMap, const Fortran::lower::pft::Variable &alias) { Fortran::lower::AggregateStoreKey key = {alias.getOwningScope(), - alias.getAlias()}; + alias.getAliasOffset()}; auto iter = storeMap.find(key); assert(iter != storeMap.end()); return iter->second; @@ -819,7 +831,7 @@ fir::GlobalOp global; auto &aggregate = var.getAggregateStore(); mlir::StringAttr linkage = getLinkageAttribute(builder, var); - if (var.isModuleVariable()) { + if (var.isModuleOrSubmoduleVariable()) { // A module global was or will be defined when lowering the module. Emit // only a declaration if the global does not exist at that point. global = declareGlobalAggregateStore(converter, loc, aggregate, aggName, @@ -871,18 +883,17 @@ const Fortran::semantics::Symbol &sym = var.getSymbol(); const mlir::Location loc = genLocation(converter, sym); mlir::IndexType idxTy = builder.getIndexType(); - std::size_t aliasOffset = var.getAlias(); - mlir::Value store = getAggregateStore(storeMap, var); mlir::IntegerType i8Ty = builder.getIntegerType(8); mlir::Type i8Ptr = builder.getRefType(i8Ty); - mlir::Value offset = builder.createIntegerConstant( - loc, idxTy, sym.GetUltimate().offset() - aliasOffset); - auto ptr = builder.create(loc, i8Ptr, store, - mlir::ValueRange{offset}); - mlir::Value preAlloc = - castAliasToPointer(builder, loc, converter.genType(sym), ptr); + mlir::Type symType = converter.genType(sym); + std::size_t off = sym.GetUltimate().offset() - var.getAliasOffset(); + mlir::Value storeAddr = getAggregateStore(storeMap, var); + mlir::Value offset = builder.createIntegerConstant(loc, idxTy, off); + mlir::Value bytePtr = builder.create( + loc, i8Ptr, storeAddr, mlir::ValueRange{offset}); + mlir::Value typedPtr = castAliasToPointer(builder, loc, symType, bytePtr); Fortran::lower::StatementContext stmtCtx; - mapSymbolAttributes(converter, var, symMap, stmtCtx, preAlloc); + mapSymbolAttributes(converter, var, symMap, stmtCtx, typedPtr); // Default initialization is possible for equivalence members: see // F2018 19.5.3.4. Note that if several equivalenced entities have // default initialization, they must have the same type, and the standard @@ -1814,19 +1825,19 @@ if (!IsDummy(sym) && !IsFunctionResult(sym) && symMap.lookupSymbol(sym)) return; } - if (var.isAggregateStore()) { + LLVM_DEBUG(llvm::dbgs() << "instantiateVariable: "; var.dump()); + if (var.isAggregateStore()) instantiateAggregateStore(converter, var, storeMap); - } else if (const Fortran::semantics::Symbol *common = - Fortran::semantics::FindCommonBlockContaining( - var.getSymbol().GetUltimate())) { + else if (const Fortran::semantics::Symbol *common = + Fortran::semantics::FindCommonBlockContaining( + var.getSymbol().GetUltimate())) instantiateCommon(converter, *common, var, symMap); - } else if (var.isAlias()) { + else if (var.isAlias()) instantiateAlias(converter, var, symMap, storeMap); - } else if (var.isGlobal()) { + else if (var.isGlobal()) instantiateGlobal(converter, var, symMap); - } else { + else instantiateLocal(converter, var, symMap); - } } void Fortran::lower::mapCallInterfaceSymbols( @@ -1835,45 +1846,47 @@ Fortran::lower::AggregateStoreMap storeMap; const Fortran::semantics::Symbol &result = caller.getResultSymbol(); for (Fortran::lower::pft::Variable var : - Fortran::lower::pft::buildFuncResultDependencyList(result)) { + Fortran::lower::pft::getDependentVariableList(result)) { if (var.isAggregateStore()) { instantiateVariable(converter, var, symMap, storeMap); - } else { - const Fortran::semantics::Symbol &sym = var.getSymbol(); - const auto *hostDetails = - sym.detailsIf(); - if (hostDetails && !var.isModuleVariable()) { - // The callee is an internal procedure `A` whose result properties - // depend on host variables. The caller may be the host, or another - // internal procedure `B` contained in the same host. In the first - // case, the host symbol is obviously mapped, in the second case, it - // must also be mapped because - // HostAssociations::internalProcedureBindings that was called when - // lowering `B` will have mapped all host symbols of captured variables - // to the tuple argument containing the composite of all host associated - // variables, whether or not the host symbol is actually referred to in - // `B`. Hence it is possible to simply lookup the variable associated to - // the host symbol without having to go back to the tuple argument. - Fortran::lower::SymbolBox hostValue = - symMap.lookupSymbol(hostDetails->symbol()); - assert(hostValue && "callee host symbol must be mapped on caller side"); - symMap.addSymbol(sym, hostValue.toExtendedValue()); - // The SymbolBox associated to the host symbols is complete, skip - // instantiateVariable that would try to allocate a new storage. - continue; - } - if (Fortran::semantics::IsDummy(sym) && sym.owner() == result.owner()) { - // Get the argument for the dummy argument symbols of the current call. - symMap.addSymbol(sym, caller.getArgumentValue(sym)); - // All the properties of the dummy variable may not come from the actual - // argument, let instantiateVariable handle this. - } - // If this is neither a host associated or dummy symbol, it must be a - // module or common block variable to satisfy specification expression - // requirements in 10.1.11, instantiateVariable will get its address and - // properties. - instantiateVariable(converter, var, symMap, storeMap); + continue; + } + const Fortran::semantics::Symbol &sym = var.getSymbol(); + if (&sym == &result) + continue; + const auto *hostDetails = + sym.detailsIf(); + if (hostDetails && !var.isModuleOrSubmoduleVariable()) { + // The callee is an internal procedure `A` whose result properties + // depend on host variables. The caller may be the host, or another + // internal procedure `B` contained in the same host. In the first + // case, the host symbol is obviously mapped, in the second case, it + // must also be mapped because + // HostAssociations::internalProcedureBindings that was called when + // lowering `B` will have mapped all host symbols of captured variables + // to the tuple argument containing the composite of all host associated + // variables, whether or not the host symbol is actually referred to in + // `B`. Hence it is possible to simply lookup the variable associated to + // the host symbol without having to go back to the tuple argument. + Fortran::lower::SymbolBox hostValue = + symMap.lookupSymbol(hostDetails->symbol()); + assert(hostValue && "callee host symbol must be mapped on caller side"); + symMap.addSymbol(sym, hostValue.toExtendedValue()); + // The SymbolBox associated to the host symbols is complete, skip + // instantiateVariable that would try to allocate a new storage. + continue; + } + if (Fortran::semantics::IsDummy(sym) && sym.owner() == result.owner()) { + // Get the argument for the dummy argument symbols of the current call. + symMap.addSymbol(sym, caller.getArgumentValue(sym)); + // All the properties of the dummy variable may not come from the actual + // argument, let instantiateVariable handle this. } + // If this is neither a host associated or dummy symbol, it must be a + // module or common block variable to satisfy specification expression + // requirements in 10.1.11, instantiateVariable will get its address and + // properties. + instantiateVariable(converter, var, symMap, storeMap); } } Index: flang/lib/Lower/IO.cpp =================================================================== --- flang/lib/Lower/IO.cpp +++ flang/lib/Lower/IO.cpp @@ -17,6 +17,7 @@ #include "flang/Lower/Bridge.h" #include "flang/Lower/ConvertExpr.h" #include "flang/Lower/ConvertVariable.h" +#include "flang/Lower/Mangler.h" #include "flang/Lower/PFTBuilder.h" #include "flang/Lower/Runtime.h" #include "flang/Lower/StatementContext.h" @@ -269,7 +270,7 @@ symbol.name().ToString() + '\0'); }; - // Define object names, and static descriptors for global objects. + // Define variable names, and static descriptors for global variables. bool groupIsLocal = false; stringAddress(symbol); for (const Fortran::semantics::Symbol &s : details.objects()) { @@ -278,18 +279,20 @@ groupIsLocal = true; continue; } - // We know we have a global item. It it's not a pointer or allocatable, - // create a static pointer to it. + // A global pointer or allocatable variable has a descriptor for typical + // accesses. Variables in multiple namelist groups may already have one. + // Create descriptors for other cases. if (!IsAllocatableOrPointer(s)) { - std::string mangleName = converter.mangleName(s) + ".desc"; + std::string mangleName = + Fortran::lower::mangle::globalNamelistDescriptorName(s); if (builder.getNamedGlobal(mangleName)) continue; const auto expr = Fortran::evaluate::AsGenericExpr(s); fir::BoxType boxTy = fir::BoxType::get(fir::PointerType::get(converter.genType(s))); auto descFunc = [&](fir::FirOpBuilder &b) { - auto box = - Fortran::lower::genInitialDataTarget(converter, loc, boxTy, *expr); + auto box = Fortran::lower::genInitialDataTarget( + converter, loc, boxTy, *expr, /*couldBeInEquivalence=*/true); b.create(loc, box); }; builder.createGlobalConstant(loc, boxTy, mangleName, descFunc, linkOnce); @@ -316,10 +319,8 @@ builder.getArrayAttr(idx)); idx[1] = one; mlir::Value descAddr; - // Items that we created end in ".desc". - std::string suffix = IsAllocatableOrPointer(s) ? "" : ".desc"; - if (auto desc = - builder.getNamedGlobal(converter.mangleName(s) + suffix)) { + if (auto desc = builder.getNamedGlobal( + Fortran::lower::mangle::globalNamelistDescriptorName(s))) { descAddr = builder.create(loc, desc.resultType(), desc.getSymbol()); } else if (Fortran::semantics::FindCommonBlockContaining(s) && Index: flang/lib/Lower/Mangler.cpp =================================================================== --- flang/lib/Lower/Mangler.cpp +++ flang/lib/Lower/Mangler.cpp @@ -55,21 +55,6 @@ return {}; } -static const Fortran::semantics::Symbol * -findInterfaceIfSeperateMP(const Fortran::semantics::Symbol &symbol) { - const Fortran::semantics::Scope &scope = symbol.owner(); - if (symbol.attrs().test(Fortran::semantics::Attr::MODULE) && - scope.IsSubmodule()) { - // FIXME symbol from MpSubprogramStmt do not seem to have - // Attr::MODULE set. - const Fortran::semantics::Symbol *iface = - scope.parent().FindSymbol(symbol.name()); - assert(iface && "Separate module procedure must be declared"); - return iface; - } - return nullptr; -} - // Mangle the name of `symbol` to make it unique within FIR's symbol table using // the FIR name mangler, `mangler` std::string @@ -100,18 +85,19 @@ [&](const Fortran::semantics::MainProgramDetails &) { return fir::NameUniquer::doProgramEntry().str(); }, - [&](const Fortran::semantics::SubprogramDetails &) { + [&](const Fortran::semantics::SubprogramDetails &subpDetails) { // Mangle external procedure without any scope prefix. if (!keepExternalInScope && Fortran::semantics::IsExternal(ultimateSymbol)) return fir::NameUniquer::doProcedure(std::nullopt, std::nullopt, symbolName); - // Separate module subprograms must be mangled according to the - // scope where they were declared (the symbol we have is the - // definition). + // A separate module procedure must be mangled according to its + // declaration scope, not its definition scope. const Fortran::semantics::Symbol *interface = &ultimateSymbol; - if (const auto *mpIface = findInterfaceIfSeperateMP(ultimateSymbol)) - interface = mpIface; + if (interface->attrs().test(Fortran::semantics::Attr::MODULE) && + interface->owner().IsSubmodule() && !subpDetails.isInterface()) + interface = subpDetails.moduleInterface(); + assert(interface && "Separate module procedure must be declared"); llvm::SmallVector modNames = moduleNames(*interface); return fir::NameUniquer::doProcedure(modNames, hostName(*interface), @@ -290,3 +276,9 @@ name.append(".").append(typeToString(funTy.getInput(i))); return name; } + +std::string Fortran::lower::mangle::globalNamelistDescriptorName( + const Fortran::semantics::Symbol &sym) { + std::string name = mangleName(sym); + return IsAllocatableOrPointer(sym) ? name : name + ".desc"s; +} Index: flang/lib/Lower/PFTBuilder.cpp =================================================================== --- flang/lib/Lower/PFTBuilder.cpp +++ flang/lib/Lower/PFTBuilder.cpp @@ -64,6 +64,8 @@ std::optional label; }; +void dumpScope(const semantics::Scope *scope, int depth = -1); + /// The instantiation of a parse tree visitor (Pre and Post) is extremely /// expensive in terms of compile and link time. So one goal here is to /// limit the bridge to one such instantiation. @@ -215,12 +217,13 @@ private: /// Initialize a new module-like unit and make it the builder's focus. template - bool enterModule(const A &func) { + bool enterModule(const A &mod) { Fortran::lower::pft::ModuleLikeUnit &unit = - addUnit(lower::pft::ModuleLikeUnit{func, pftParentStack.back()}); + addUnit(lower::pft::ModuleLikeUnit{mod, pftParentStack.back()}); functionList = &unit.nestedFunctions; pushEvaluationList(&unit.evaluationList); pftParentStack.emplace_back(unit); + LLVM_DEBUG(dumpScope(&unit.getScope())); return true; } @@ -290,6 +293,7 @@ functionList = &unit.nestedFunctions; pushEvaluationList(&unit.evaluationList); pftParentStack.emplace_back(unit); + LLVM_DEBUG(dumpScope(&unit.getScope())); return true; } @@ -1033,6 +1037,45 @@ lower::pft::Evaluation *lastLexicalEvaluation{}; }; +/// Dump all program scopes and symbols with addresses to disambiguate names. +/// This is static, unchanging front end information, so dump it only once. +void dumpScope(const semantics::Scope *scope, int depth) { + static int initialVisitCounter = 0; + if (depth < 0) { + if (++initialVisitCounter != 1) + return; + while (!scope->IsGlobal()) + scope = &scope->parent(); + LLVM_DEBUG(llvm::dbgs() << "Full program scope information.\n" + "Addresses in angle brackets are scopes. " + "Unbracketed addresses are symbols.\n"); + } + static const std::string white{" ++"}; + std::string w = white.substr(0, depth * 2); + if (depth >= 0) { + LLVM_DEBUG(llvm::dbgs() << w << "<" << scope << "> "); + if (auto *sym{scope->symbol()}) { + LLVM_DEBUG(llvm::dbgs() << sym << " " << *sym << "\n"); + } else { + if (scope->IsIntrinsicModules()) { + LLVM_DEBUG(llvm::dbgs() << "IntrinsicModules (no detail)\n"); + return; + } + LLVM_DEBUG(llvm::dbgs() << "[anonymous]\n"); + } + } + for (const auto &scp : scope->children()) + if (!scp.symbol()) + dumpScope(&scp, depth + 1); + for (auto iter = scope->begin(); iter != scope->end(); ++iter) { + common::Reference sym = iter->second; + if (auto scp = sym->scope()) + dumpScope(scp, depth + 1); + else + LLVM_DEBUG(llvm::dbgs() << w + " " << &*sym << " " << *sym << "\n"); + } +} + class PFTDumper { public: void dumpPFT(llvm::raw_ostream &outputStream, @@ -1162,13 +1205,31 @@ void dumpModuleLikeUnit(llvm::raw_ostream &outputStream, const lower::pft::ModuleLikeUnit &moduleLikeUnit) { outputStream << getNodeIndex(moduleLikeUnit) << " "; - outputStream << "ModuleLike:\n"; + llvm::StringRef unitKind; + llvm::StringRef name; + llvm::StringRef header; + moduleLikeUnit.beginStmt.visit(common::visitors{ + [&](const parser::Statement &stmt) { + unitKind = "Module"; + name = toStringRef(stmt.statement.v.source); + header = toStringRef(stmt.source); + }, + [&](const parser::Statement &stmt) { + unitKind = "Submodule"; + name = toStringRef(std::get(stmt.statement.t).source); + header = toStringRef(stmt.source); + }, + [&](const auto &) { + llvm_unreachable("not a valid module begin stmt"); + }, + }); + outputStream << unitKind << ' ' << name << ": " << header << '\n'; dumpEvaluationList(outputStream, moduleLikeUnit.evaluationList); outputStream << "Contains\n"; for (const lower::pft::FunctionLikeUnit &func : moduleLikeUnit.nestedFunctions) dumpFunctionLikeUnit(outputStream, func); - outputStream << "End Contains\nEnd ModuleLike\n\n"; + outputStream << "End Contains\nEnd " << unitKind << ' ' << name << "\n\n"; } // Top level directives @@ -1272,60 +1333,63 @@ } namespace { -/// This helper class is for sorting the symbols in the symbol table. We want -/// the symbols in an order such that a symbol will be visited after those it -/// depends upon. Otherwise this sort is stable and preserves the order of the -/// symbol table, which is sorted by name. -struct SymbolDependenceDepth { - explicit SymbolDependenceDepth( - std::vector> &vars) - : vars{vars} {} - - void analyzeAliasesInCurrentScope(const semantics::Scope &scope) { +/// This helper class sorts the symbols in a scope such that a symbol will +/// be placed after those it depends upon. Otherwise the sort is stable and +/// preserves the order of the symbol table, which is sorted by name. This +/// analysis may also be done for an individual symbol. +struct SymbolDependenceAnalysis { + explicit SymbolDependenceAnalysis(const semantics::Scope &scope) { + analyzeEquivalenceSets(scope); + for (const auto &iter : scope) + analyze(iter.second.get()); + finalize(); + } + explicit SymbolDependenceAnalysis(const semantics::Symbol &symbol) { + analyzeEquivalenceSets(symbol.owner()); + analyze(symbol); + finalize(); + } + Fortran::lower::pft::VariableList getVariableList() { + return std::move(layeredVarList[0]); + } + +private: + /// Analyze the equivalence sets defined in \p scope, plus the equivalence + /// sets in host module, submodule, and procedure scopes that may define + /// symbols referenced in \p scope. This analysis excludes equivalence sets + /// involving common blocks, which are handled elsewhere. + void analyzeEquivalenceSets(const semantics::Scope &scope) { // FIXME: When this function is called on the scope of an internal // procedure whose parent contains an EQUIVALENCE set and the internal // procedure uses variables from that EQUIVALENCE set, we end up creating // an AggregateStore for those variables unnecessarily. - // - /// If this is a function nested in a module no host associated - /// symbol are added to the function scope for module symbols used in this - /// scope. As a result, alias analysis in parent module scopes must be - /// preformed here. - const semantics::Scope *parentScope = &scope; - while (!parentScope->IsGlobal()) { - parentScope = &parentScope->parent(); - if (parentScope->IsModule()) - analyzeAliases(*parentScope); - } + + // A function defined in a [sub]module has no explicit USE of its ancestor + // [sub]modules. Analyze those scopes here to accommodate references to + // symbols in them. + for (auto *scp = &scope.parent(); !scp->IsGlobal(); scp = &scp->parent()) + if (scp->kind() == Fortran::semantics::Scope::Kind::Module) + analyzeLocalEquivalenceSets(*scp); + // Analyze local, USEd, and host procedure scope equivalences. for (const auto &iter : scope) { const semantics::Symbol &ultimate = iter.second.get().GetUltimate(); - if (skipSymbol(ultimate)) - continue; - analyzeAliases(ultimate.owner()); + if (!skipSymbol(ultimate)) + analyzeLocalEquivalenceSets(ultimate.owner()); } - // add all aggregate stores to the front of the work list + // Add all aggregate stores to the front of the variable list. adjustSize(1); // The copy in the loop matters, 'stores' will still be used. for (auto st : stores) - vars[0].emplace_back(std::move(st)); + layeredVarList[0].emplace_back(std::move(st)); } - // Compute the offset of the last byte that resides in the symbol. - inline static std::size_t offsetWidth(const Fortran::semantics::Symbol &sym) { - std::size_t width = sym.offset(); - if (std::size_t size = sym.size()) - width += size - 1; - return width; - } - - // Analyze the equivalence sets. This analysis need not be performed when the - // scope has no equivalence sets. - void analyzeAliases(const semantics::Scope &scope) { + /// Analyze the equivalence sets defined locally in \p scope that don't + /// involve common blocks. + void analyzeLocalEquivalenceSets(const semantics::Scope &scope) { if (scope.equivalenceSets().empty()) - return; - // Don't analyze a scope if it has already been analyzed. + return; // no equivalence sets to analyze if (analyzedScopes.find(&scope) != analyzedScopes.end()) - return; + return; // equivalence sets already analyzed analyzedScopes.insert(&scope); std::list> aggregates = @@ -1334,11 +1398,8 @@ const Fortran::semantics::Symbol *aggregateSym = nullptr; bool isGlobal = false; const semantics::Symbol &first = *aggregate.front(); - // Skip aggregates related to common blocks as they will be handled by - // instantiateCommon and the aggregate store information will not be used. - // Additionally, the AggregateStoreKeys for common block related aggregate - // stores can collide with non common block ones, potentially resulting in - // incorrect stores being used. + // Exclude equivalence sets involving common blocks. + // Those are handled in instantiateCommon. if (lower::definedInCommonBlock(first)) continue; std::size_t start = first.offset(); @@ -1372,15 +1433,16 @@ // other symbols. int analyze(const semantics::Symbol &sym) { auto done = seen.insert(&sym); - LLVM_DEBUG(llvm::dbgs() << "analyze symbol: " << sym << '\n'); if (!done.second) return 0; + LLVM_DEBUG(llvm::dbgs() << "analyze symbol " << &sym << " in <" + << &sym.owner() << ">: " << sym << '\n'); const bool isProcedurePointerOrDummy = semantics::IsProcedurePointer(sym) || (semantics::IsProcedure(sym) && IsDummy(sym)); // A procedure argument in a subprogram with multiple entry points might - // need a vars list entry to trigger creation of a symbol map entry in - // some cases. Non-dummy procedures don't. + // need a layeredVarList entry to trigger creation of a symbol map entry + // in some cases. Non-dummy procedures don't. if (semantics::IsProcedure(sym) && !isProcedurePointerOrDummy) return 0; semantics::Symbol ultimate = sym.GetUltimate(); @@ -1400,11 +1462,11 @@ // Symbol must be something lowering will have to allocate. int depth = 0; - // Analyze symbols appearing in object entity specification expression. This - // ensures these symbols will be instantiated before the current one. + // Analyze symbols appearing in object entity specification expressions. + // This ensures these symbols will be instantiated before the current one. // This is not done for object entities that are host associated because - // they must be instantiated from the value of the host symbols (the - // specification expressions should not be re-evaluated). + // they must be instantiated from the value of the host symbols. + // (The specification expressions should not be re-evaluated.) if (const auto *details = sym.detailsIf()) { const semantics::DeclTypeSpec *symTy = sym.GetType(); assert(symTy && "symbol must have a type"); @@ -1421,44 +1483,51 @@ depth = std::max(analyze(s) + 1, depth); } }; - // handle any symbols in array bound declarations + // Handle any symbols in array bound declarations. for (const semantics::ShapeSpec &subs : details->shape()) { doExplicit(subs.lbound()); doExplicit(subs.ubound()); } - // handle any symbols in coarray bound declarations + // Handle any symbols in coarray bound declarations. for (const semantics::ShapeSpec &subs : details->coshape()) { doExplicit(subs.lbound()); doExplicit(subs.ubound()); } - // handle any symbols in initialization expressions + // Handle any symbols in initialization expressions. if (auto e = details->init()) for (const auto &s : evaluate::CollectSymbols(*e)) depth = std::max(analyze(s) + 1, depth); } adjustSize(depth + 1); bool global = lower::symbolIsGlobal(sym); - vars[depth].emplace_back(sym, global, depth); + layeredVarList[depth].emplace_back(sym, global, depth); if (semantics::IsAllocatable(sym)) - vars[depth].back().setHeapAlloc(); + layeredVarList[depth].back().setHeapAlloc(); if (semantics::IsPointer(sym)) - vars[depth].back().setPointer(); + layeredVarList[depth].back().setPointer(); if (ultimate.attrs().test(semantics::Attr::TARGET)) - vars[depth].back().setTarget(); + layeredVarList[depth].back().setTarget(); // If there are alias sets, then link the participating variables to their // aggregate stores when constructing the new variable on the list. if (lower::pft::Variable::AggregateStore *store = findStoreIfAlias(sym)) - vars[depth].back().setAlias(store->getOffset()); + layeredVarList[depth].back().setAlias(store->getOffset()); return depth; } - /// Save the final list of variable allocations as a single vector and free - /// the rest. - void finalize() { - for (int i = 1, end = vars.size(); i < end; ++i) - vars[0].insert(vars[0].end(), vars[i].begin(), vars[i].end()); - vars.resize(1); + /// Skip symbol in alias analysis. + bool skipSymbol(const semantics::Symbol &sym) { + // Common block equivalences are largely managed by the front end. + // Compiler generated symbols ('.' names) cannot be equivalenced. + // FIXME: Equivalence code generation may need to be revisited. + return !sym.has() || + lower::definedInCommonBlock(sym) || sym.name()[0] == '.'; + } + + // Make sure the table is of appropriate size. + void adjustSize(std::size_t size) { + if (layeredVarList.size() < size) + layeredVarList.resize(size); } Fortran::lower::pft::Variable::AggregateStore * @@ -1468,8 +1537,9 @@ // Expect the total number of EQUIVALENCE sets to be small for a typical // Fortran program. if (aliasSyms.find(&ultimate) != aliasSyms.end()) { - LLVM_DEBUG(llvm::dbgs() << "symbol: " << ultimate << '\n'); - LLVM_DEBUG(llvm::dbgs() << "scope: " << scope << '\n'); + LLVM_DEBUG(llvm::dbgs() << "found aggregate containing " << &ultimate + << " " << ultimate.name() << " in <" << &scope + << "> " << scope.GetName() << '\n'); std::size_t off = ultimate.offset(); std::size_t symSize = ultimate.size(); for (lower::pft::Variable::AggregateStore &v : stores) { @@ -1499,41 +1569,23 @@ return nullptr; } -private: - /// Skip symbol in alias analysis. - bool skipSymbol(const semantics::Symbol &sym) { - // Common block equivalences are largely managed by the front end. - // Compiler generated symbols ('.' names) cannot be equivalenced. - // FIXME: Equivalence code generation may need to be revisited. - return !sym.has() || - lower::definedInCommonBlock(sym) || sym.name()[0] == '.'; - } - - // Make sure the table is of appropriate size. - void adjustSize(std::size_t size) { - if (vars.size() < size) - vars.resize(size); + /// Flatten the result VariableList. + void finalize() { + for (int i = 1, end = layeredVarList.size(); i < end; ++i) + layeredVarList[0].insert(layeredVarList[0].end(), + layeredVarList[i].begin(), + layeredVarList[i].end()); } llvm::SmallSet seen; - std::vector> &vars; + std::vector layeredVarList; llvm::SmallSet aliasSyms; - /// Set of Scope that have been analyzed for aliases. + /// Set of scopes that have been analyzed for aliases. llvm::SmallSet analyzedScopes; std::vector stores; }; } // namespace -static void processSymbolTable( - const semantics::Scope &scope, - std::vector> &varList) { - SymbolDependenceDepth sdd{varList}; - sdd.analyzeAliasesInCurrentScope(scope); - for (const auto &iter : scope) - sdd.analyze(iter.second.get()); - sdd.finalize(); -} - //===----------------------------------------------------------------------===// // FunctionLikeUnit implementation //===----------------------------------------------------------------------===// @@ -1550,12 +1602,10 @@ beginStmt = FunctionStatement(programStmt.value()); const semantics::Symbol *symbol = getSymbol(*beginStmt); entryPointList[0].first = symbol; - processSymbolTable(*symbol->scope(), varList); + scope = symbol->scope(); } else { - processSymbolTable( - semanticsContext.FindScope( - std::get>(func.t).source), - varList); + scope = &semanticsContext.FindScope( + std::get>(func.t).source); } } @@ -1567,7 +1617,7 @@ endStmt{getFunctionStmt(func)} { const semantics::Symbol *symbol = getSymbol(*beginStmt); entryPointList[0].first = symbol; - processSymbolTable(*symbol->scope(), varList); + scope = symbol->scope(); } Fortran::lower::pft::FunctionLikeUnit::FunctionLikeUnit( @@ -1578,7 +1628,7 @@ endStmt{getFunctionStmt(func)} { const semantics::Symbol *symbol = getSymbol(*beginStmt); entryPointList[0].first = symbol; - processSymbolTable(*symbol->scope(), varList); + scope = symbol->scope(); } Fortran::lower::pft::FunctionLikeUnit::FunctionLikeUnit( @@ -1589,7 +1639,7 @@ endStmt{getFunctionStmt(func)} { const semantics::Symbol *symbol = getSymbol(*beginStmt); entryPointList[0].first = symbol; - processSymbolTable(*symbol->scope(), varList); + scope = symbol->scope(); } Fortran::lower::HostAssociations & @@ -1621,19 +1671,13 @@ Fortran::lower::pft::ModuleLikeUnit::ModuleLikeUnit( const parser::Module &m, const lower::pft::PftNode &parent) : ProgramUnit{m, parent}, beginStmt{getModuleStmt(m)}, - endStmt{getModuleStmt(m)} { - const semantics::Symbol *symbol = getSymbol(beginStmt); - processSymbolTable(*symbol->scope(), varList); -} + endStmt{getModuleStmt(m)} {} Fortran::lower::pft::ModuleLikeUnit::ModuleLikeUnit( const parser::Submodule &m, const lower::pft::PftNode &parent) : ProgramUnit{m, parent}, beginStmt{getModuleStmt( m)}, - endStmt{getModuleStmt(m)} { - const semantics::Symbol *symbol = getSymbol(beginStmt); - processSymbolTable(*symbol->scope(), varList); -} + endStmt{getModuleStmt(m)} {} parser::CharBlock Fortran::lower::pft::ModuleLikeUnit::getStartingSourceLoc() const { @@ -1682,7 +1726,7 @@ void Fortran::lower::pft::Variable::dump() const { if (auto *s = std::get_if(&var)) { - llvm::errs() << "symbol: " << s->symbol->name(); + llvm::errs() << s->symbol << " " << *s->symbol; llvm::errs() << " (depth: " << s->depth << ')'; if (s->global) llvm::errs() << ", global"; @@ -1708,6 +1752,16 @@ llvm::errs() << '\n'; } +void Fortran::lower::pft::dump(Fortran::lower::pft::VariableList &variableList, + std::string s) { + llvm::errs() << (s.empty() ? "VariableList" : s) << " " << &variableList + << " size=" << variableList.size() << "\n"; + for (auto var : variableList) { + llvm::errs() << " "; + var.dump(); + } +} + void Fortran::lower::pft::FunctionLikeUnit::dump() const { PFTDumper{}.dumpFunctionLikeUnit(llvm::errs(), *this); } @@ -1721,21 +1775,41 @@ llvm::errs() << "block data {\n" << symTab << "\n}\n"; } -std::vector -Fortran::lower::pft::buildFuncResultDependencyList( - const Fortran::semantics::Symbol &symbol) { - std::vector> variableList; - SymbolDependenceDepth sdd(variableList); - sdd.analyzeAliasesInCurrentScope(symbol.owner()); - sdd.analyze(symbol); - sdd.finalize(); - // Remove the pft::variable for the result itself, only its dependencies - // should be returned in the list. - assert(!variableList[0].empty() && "must at least contain the result"); - assert(&variableList[0].back().getSymbol() == &symbol && - "result sym should be last"); - variableList[0].pop_back(); - return variableList[0]; +/// Find or create an ordered list of equivalences and variables in \p scope. +/// The result is cached in \p map. +const lower::pft::VariableList & +lower::pft::getScopeVariableList(const semantics::Scope &scope, + ScopeVariableListMap &map) { + LLVM_DEBUG(llvm::dbgs() << "\ngetScopeVariableList of [sub]module scope <" + << &scope << "> " << scope.GetName() << "\n"); + auto iter = map.find(&scope); + if (iter == map.end()) { + SymbolDependenceAnalysis sda(scope); + map.emplace(&scope, std::move(sda.getVariableList())); + iter = map.find(&scope); + } + return iter->second; +} + +/// Create an ordered list of equivalences and variables in \p scope. +/// The result is not cached. +lower::pft::VariableList +lower::pft::getScopeVariableList(const semantics::Scope &scope) { + LLVM_DEBUG( + llvm::dbgs() << "\ngetScopeVariableList of [sub]program|block scope <" + << &scope << "> " << scope.GetName() << "\n"); + SymbolDependenceAnalysis sda(scope); + return sda.getVariableList(); +} + +/// Create an ordered list of equivalences and variables that \p symbol +/// depends on (no caching). Include \p symbol at the end of the list. +lower::pft::VariableList +lower::pft::getDependentVariableList(const semantics::Symbol &symbol) { + LLVM_DEBUG(llvm::dbgs() << "\ngetDependentVariableList of " << &symbol + << " - " << symbol << "\n"); + SymbolDependenceAnalysis sda(symbol); + return sda.getVariableList(); } namespace { Index: flang/test/Lower/pre-fir-tree01.f90 =================================================================== --- flang/test/Lower/pre-fir-tree01.f90 +++ flang/test/Lower/pre-fir-tree01.f90 @@ -32,7 +32,7 @@ end ! CHECK: End BlockData -! CHECK: ModuleLike +! CHECK: Module test_mod module test_mod interface ! check specification parts are not part of the PFT. @@ -75,9 +75,9 @@ end function ! CHECK: End Function foo2 end module -! CHECK: End ModuleLike +! CHECK: End Module test_mod -! CHECK: ModuleLike +! CHECK: Submodule test_mod_impl: submodule(test_mod) test_mod_impl submodule (test_mod) test_mod_impl contains ! CHECK: Subroutine foo @@ -114,7 +114,7 @@ ! CHECK: <> end procedure end submodule -! CHECK: End ModuleLike +! CHECK: End Submodule test_mod_impl ! CHECK: BlockData block data named_block Index: flang/test/Lower/pre-fir-tree02.f90 =================================================================== --- flang/test/Lower/pre-fir-tree02.f90 +++ flang/test/Lower/pre-fir-tree02.f90 @@ -144,7 +144,7 @@ deallocate(x) end -! CHECK: ModuleLike +! CHECK: Module test module test !! When derived type processing is implemented, remove all instances of: !! - !![disable] Index: flang/test/Lower/pre-fir-tree06.f90 =================================================================== --- flang/test/Lower/pre-fir-tree06.f90 +++ flang/test/Lower/pre-fir-tree06.f90 @@ -2,15 +2,15 @@ ! Test structure of the Pre-FIR tree with OpenMP declarative construct -! CHECK: ModuleLike +! CHECK: Module m module m real, dimension(10) :: x ! CHECK-NEXT: OpenMPDeclarativeConstruct !$omp threadprivate(x) end -! CHECK: End ModuleLike +! CHECK: End Module m -! CHECK: ModuleLike +! CHECK: Module m2 module m2 integer, save :: i ! CHECK-NEXT: OpenMPDeclarativeConstruct @@ -23,7 +23,7 @@ i = 2; end end -! CHECK: End ModuleLike +! CHECK: End Module m2 ! CHECK: Program main program main Index: flang/test/Lower/pre-fir-tree07.f90 =================================================================== --- flang/test/Lower/pre-fir-tree07.f90 +++ flang/test/Lower/pre-fir-tree07.f90 @@ -2,11 +2,10 @@ ! Test structure of the Pre-FIR tree with OpenACC declarative construct -! CHECK: ModuleLike +! CHECK: Module m: module m module m real, dimension(10) :: x ! CHECK-NEXT: OpenACCDeclarativeConstruct !$acc declare create(x) end -! CHECK: End ModuleLike - +! CHECK: End Module m Index: flang/test/Lower/submodule.f90 =================================================================== --- /dev/null +++ flang/test/Lower/submodule.f90 @@ -0,0 +1,138 @@ +! RUN: bbc -emit-fir %s -o - | FileCheck %s + +module mm + integer :: vv = 20 + interface + module function ff1(nn) + integer ff1(nn+1) + end function ff1 + module function ff2(nn) + integer ff2(nn+2) + end function ff2 + module function ff3(nn) + integer ff3(nn+3) + end function ff3 + end interface +end module mm + +submodule(mm) ss1 + integer :: ww = 20 + interface + module function fff(nn) + integer fff + end function fff + end interface +contains + ! CHECK-LABEL: func @_QMmmPff2 + ! CHECK: %[[V_0:[0-9]+]] = fir.load %arg0 : !fir.ref + ! CHECK: %[[V_1:[0-9]+]] = arith.addi %[[V_0]], %c2{{.*}} : i32 + ! CHECK: %[[V_2:[0-9]+]] = fir.convert %[[V_1]] : (i32) -> i64 + ! CHECK: %[[V_3:[0-9]+]] = fir.convert %[[V_2]] : (i64) -> index + ! CHECK: %[[V_4:[0-9]+]] = arith.cmpi sgt, %[[V_3]], %c0{{.*}} : index + ! CHECK: %[[V_5:[0-9]+]] = arith.select %[[V_4]], %[[V_3]], %c0{{.*}} : index + ! CHECK: %[[V_6:[0-9]+]] = fir.alloca !fir.array, %[[V_5]] {bindc_name = "ff2", uniq_name = "_QMmmSss1Fff2Eff2"} + ! CHECK: %[[V_7:[0-9]+]] = fir.shape %[[V_5]] : (index) -> !fir.shape<1> + ! CHECK: %[[V_8:[0-9]+]] = fir.array_load %[[V_6]](%[[V_7]]) : (!fir.ref>, !fir.shape<1>) -> !fir.array + ! CHECK: %[[V_9:[0-9]+]] = fir.call @_QMmmSss1Pfff(%arg0) {{.*}} : (!fir.ref) -> i32 + ! CHECK: %[[V_10:[0-9]+]] = arith.subi %[[V_5]], %c1{{.*}} : index + ! CHECK: %[[V_11:[0-9]+]] = fir.do_loop %arg1 = %c0{{.*}} to %[[V_10]] step %c1{{.*}} unordered iter_args(%arg2 = %[[V_8]]) -> (!fir.array) { + ! CHECK: %[[V_13:[0-9]+]] = fir.array_update %arg2, %[[V_9]], %arg1 : (!fir.array, i32, index) -> !fir.array + ! CHECK: fir.result %[[V_13]] : !fir.array + ! CHECK: } + ! CHECK: fir.array_merge_store %[[V_8]], %[[V_11]] to %[[V_6]] : !fir.array, !fir.array, !fir.ref> + ! CHECK: %[[V_12:[0-9]+]] = fir.load %[[V_6]] : !fir.ref> + ! CHECK: return %[[V_12]] : !fir.array + ! CHECK: } + module procedure ff2 + ff2 = fff(nn) + end procedure ff2 +end submodule ss1 + +submodule(mm:ss1) ss2 +contains + ! CHECK-LABEL: func @_QMmmPff1 + ! CHECK-DAG: %[[V_0:[0-9]+]] = fir.address_of(@_QMmmEvv) : !fir.ref + ! CHECK-DAG: %[[V_1:[0-9]+]] = fir.load %arg0 : !fir.ref + ! CHECK: %[[V_2:[0-9]+]] = arith.addi %[[V_1]], %c1{{.*}} : i32 + ! CHECK: %[[V_3:[0-9]+]] = fir.convert %[[V_2]] : (i32) -> i64 + ! CHECK: %[[V_4:[0-9]+]] = fir.convert %[[V_3]] : (i64) -> index + ! CHECK: %[[V_5:[0-9]+]] = arith.cmpi sgt, %[[V_4]], %c0{{.*}} : index + ! CHECK: %[[V_6:[0-9]+]] = arith.select %[[V_5]], %[[V_4]], %c0{{.*}} : index + ! CHECK: %[[V_7:[0-9]+]] = fir.alloca !fir.array, %[[V_6]] {bindc_name = "ff1", uniq_name = "_QMmmSss1Sss2Fff1Eff1"} + ! CHECK: %[[V_8:[0-9]+]] = fir.shape %[[V_6]] : (index) -> !fir.shape<1> + ! CHECK: %[[V_9:[0-9]+]] = fir.array_load %[[V_7]](%[[V_8]]) : (!fir.ref>, !fir.shape<1>) -> !fir.array + ! CHECK: %[[V_10:[0-9]+]] = fir.load %[[V_0]] : !fir.ref + ! CHECK: %[[V_11:[0-9]+]] = arith.addi %[[V_10]], %c2{{.*}} : i32 + ! CHECK: %[[V_12:[0-9]+]] = arith.subi %[[V_6]], %c1{{.*}} : index + ! CHECK: %[[V_13:[0-9]+]] = fir.do_loop %arg1 = %c0{{.*}} to %[[V_12]] step %c1{{.*}} unordered iter_args(%arg2 = %[[V_9]]) -> (!fir.array) { + ! CHECK: %[[V_15:[0-9]+]] = fir.array_update %arg2, %[[V_11]], %arg1 : (!fir.array, i32, index) -> !fir.array + ! CHECK: fir.result %[[V_15]] : !fir.array + ! CHECK: } + ! CHECK: fir.array_merge_store %[[V_9]], %[[V_13]] to %[[V_7]] : !fir.array, !fir.array, !fir.ref> + ! CHECK: %[[V_14:[0-9]+]] = fir.load %[[V_7]] : !fir.ref> + ! CHECK: return %[[V_14]] : !fir.array + ! CHECK: } + module function ff1(nn) + integer ff1(nn+1) + ff1 = vv + 2 + end function ff1 + + ! CHECK-LABEL: func @_QMmmSss1Pfff + ! CHECK-DAG: %[[V_0:[0-9]+]] = fir.address_of(@_QMmmSss1Eww) : !fir.ref + ! CHECK-DAG: %[[V_1:[0-9]+]] = fir.address_of(@_QMmmEvv) : !fir.ref + ! CHECK-DAG: %[[V_2:[0-9]+]] = fir.alloca i32 {bindc_name = "fff", uniq_name = "_QMmmSss1Sss2FfffEfff"} + ! CHECK-DAG: %[[V_3:[0-9]+]] = fir.load %[[V_1]] : !fir.ref + ! CHECK-DAG: %[[V_4:[0-9]+]] = fir.load %[[V_0]] : !fir.ref + ! CHECK: %[[V_5:[0-9]+]] = arith.addi %[[V_3]], %[[V_4]] : i32 + ! CHECK: %[[V_6:[0-9]+]] = arith.addi %[[V_5]], %c4{{.*}} : i32 + ! CHECK: fir.store %[[V_6]] to %[[V_2]] : !fir.ref + ! CHECK: %[[V_7:[0-9]+]] = fir.load %[[V_2]] : !fir.ref + ! CHECK: return %[[V_7]] : i32 + ! CHECK: } + module procedure fff + fff = vv + ww + 4 + end procedure fff +end submodule ss2 + +submodule(mm) sss +contains + ! CHECK-LABEL: func @_QMmmPff3 + ! CHECK-DAG: %[[V_0:[0-9]+]] = fir.address_of(@_QMmmEvv) : !fir.ref + ! CHECK-DAG: %[[V_1:[0-9]+]] = fir.load %arg0 : !fir.ref + ! CHECK: %[[V_2:[0-9]+]] = arith.addi %[[V_1]], %c3{{.*}} : i32 + ! CHECK: %[[V_3:[0-9]+]] = fir.convert %[[V_2]] : (i32) -> i64 + ! CHECK: %[[V_4:[0-9]+]] = fir.convert %[[V_3]] : (i64) -> index + ! CHECK: %[[V_5:[0-9]+]] = arith.cmpi sgt, %[[V_4]], %c0{{.*}} : index + ! CHECK: %[[V_6:[0-9]+]] = arith.select %[[V_5]], %[[V_4]], %c0{{.*}} : index + ! CHECK: %[[V_7:[0-9]+]] = fir.alloca !fir.array, %[[V_6]] {bindc_name = "ff3", uniq_name = "_QMmmSsssFff3Eff3"} + ! CHECK: %[[V_8:[0-9]+]] = fir.shape %[[V_6]] : (index) -> !fir.shape<1> + ! CHECK: %[[V_9:[0-9]+]] = fir.array_load %[[V_7]](%[[V_8]]) : (!fir.ref>, !fir.shape<1>) -> !fir.array + ! CHECK-DAG: %[[V_10:[0-9]+]] = fir.load %arg0 : !fir.ref + ! CHECK-DAG: %[[V_11:[0-9]+]] = fir.load %[[V_0]] : !fir.ref + ! CHECK: %[[V_12:[0-9]+]] = arith.muli %[[V_10]], %[[V_11]] : i32 + ! CHECK: %[[V_13:[0-9]+]] = arith.addi %[[V_12]], %c6{{.*}} : i32 + ! CHECK: %[[V_14:[0-9]+]] = arith.subi %[[V_6]], %c1{{.*}} : index + ! CHECK: %[[V_15:[0-9]+]] = fir.do_loop %arg1 = %c0{{.*}} to %[[V_14]] step %c1{{.*}} unordered iter_args(%arg2 = %[[V_9]]) -> (!fir.array) { + ! CHECK: %[[V_17:[0-9]+]] = fir.array_update %arg2, %[[V_13]], %arg1 : (!fir.array, i32, index) -> !fir.array + ! CHECK: fir.result %[[V_17]] : !fir.array + ! CHECK: } + ! CHECK: fir.array_merge_store %[[V_9]], %[[V_15]] to %[[V_7]] : !fir.array, !fir.array, !fir.ref> + ! CHECK: %[[V_16:[0-9]+]] = fir.load %[[V_7]] : !fir.ref> + ! CHECK: return %[[V_16]] : !fir.array + ! CHECK: } + module function ff3(nn) + integer ff3(nn+3) + ff3 = nn*vv + 6 + end function ff3 +end submodule sss + +! CHECK-LABEL: func @_QQmain +program pp + use mm + ! CHECK: fir.call @_QMmmPff1(%{{[0-9]+}}) {{.*}} : (!fir.ref) -> !fir.array + print*, ff1(1) ! expect: 22 22 + ! CHECK: fir.call @_QMmmPff2(%{{[0-9]+}}) {{.*}} : (!fir.ref) -> !fir.array + print*, ff2(2) ! expect: 44 44 44 44 + ! CHECK: fir.call @_QMmmPff3(%{{[0-9]+}}) {{.*}} : (!fir.ref) -> !fir.array + print*, ff3(3) ! expect: 66 66 66 66 66 66 +end program pp