Revert "[C++20][Modules] Build module static initializers per P1874R1."

This reverts commit ac507102d2.

reverting while we figuere out why one of the green dragon lldb test fails.
This commit is contained in:
Iain Sandoe 2022-07-11 19:50:31 +01:00
parent e7c8ded6df
commit b19d3ee712
10 changed files with 10 additions and 516 deletions

View File

@ -472,9 +472,6 @@ class ASTContext : public RefCountedBase<ASTContext> {
};
llvm::DenseMap<Module*, PerModuleInitializers*> ModuleInitializers;
/// For module code-gen cases, this is the top-level module we are building.
Module *TopLevelModule = nullptr;
static constexpr unsigned ConstantArrayTypesLog2InitSize = 8;
static constexpr unsigned GeneralTypesLog2InitSize = 9;
static constexpr unsigned FunctionProtoTypesLog2InitSize = 12;
@ -1078,12 +1075,6 @@ public:
/// Get the initializations to perform when importing a module, if any.
ArrayRef<Decl*> getModuleInitializers(Module *M);
/// Set the (C++20) module we are building.
void setModuleForCodeGen(Module *M) { TopLevelModule = M; }
/// Get module under construction, nullptr if this is not a C++20 module.
Module *getModuleForCodeGen() const { return TopLevelModule; }
TranslationUnitDecl *getTranslationUnitDecl() const {
return TUDecl->getMostRecentDecl();
}

View File

@ -665,18 +665,6 @@ public:
Module *findSubmodule(StringRef Name) const;
Module *findOrInferSubmodule(StringRef Name);
/// Get the Global Module Fragment (sub-module) for this module, it there is
/// one.
///
/// \returns The GMF sub-module if found, or NULL otherwise.
Module *getGlobalModuleFragment() { return findSubmodule("<global>"); }
/// Get the Private Module Fragment (sub-module) for this module, it there is
/// one.
///
/// \returns The PMF sub-module if found, or NULL otherwise.
Module *getPrivateModuleFragment() { return findSubmodule("<private>"); }
/// Determine whether the specified module would be visible to
/// a lookup at the end of this module.
///

View File

@ -2281,11 +2281,6 @@ public:
return ModuleScopes.empty() ? nullptr : ModuleScopes.back().Module;
}
/// Is the module scope we are an interface?
bool currentModuleIsInterface() const {
return ModuleScopes.empty() ? false : ModuleScopes.back().ModuleInterface;
}
/// Get the module owning an entity.
Module *getOwningModule(const Decl *Entity) {
return Entity->getOwningModule();

View File

@ -618,127 +618,6 @@ void CodeGenModule::EmitCXXThreadLocalInitFunc() {
CXXThreadLocals.clear();
}
/* Build the initializer for a C++20 module:
This is arranged to be run only once regardless of how many times the module
might be included transitively. This arranged by using a control variable.
First we call any initializers for imported modules.
We then call initializers for the Global Module Fragment (if present)
We then call initializers for the current module.
We then call initializers for the Private Module Fragment (if present)
*/
void CodeGenModule::EmitCXXModuleInitFunc(Module *Primary) {
while (!CXXGlobalInits.empty() && !CXXGlobalInits.back())
CXXGlobalInits.pop_back();
// We create the function, even if it is empty, since an importer of this
// module will refer to it unconditionally (for the current implementation
// there is no way for the importer to know that an importee does not need
// an initializer to be run).
// Module initializers for imported modules are emitted first.
// Collect the modules that we import
SmallVector<Module *> AllImports;
// Ones that we export
for (auto I : Primary->Exports)
AllImports.push_back(I.getPointer());
// Ones that we only import.
for (Module *M : Primary->Imports)
AllImports.push_back(M);
SmallVector<llvm::Function *, 8> ModuleInits;
for (Module *M : AllImports) {
llvm::FunctionType *FTy = llvm::FunctionType::get(VoidTy, false);
SmallString<256> FnName;
{
llvm::raw_svector_ostream Out(FnName);
cast<ItaniumMangleContext>(getCXXABI().getMangleContext())
.mangleModuleInitializer(M, Out);
}
assert(!GetGlobalValue(FnName.str()) &&
"We should only have one use of the initializer call");
llvm::Function *Fn = llvm::Function::Create(
FTy, llvm::Function::ExternalLinkage, FnName.str(), &getModule());
ModuleInits.push_back(Fn);
}
AllImports.clear();
// Add any initializers with specified priority; this uses the same approach
// as EmitCXXGlobalInitFunc().
if (!PrioritizedCXXGlobalInits.empty()) {
SmallVector<llvm::Function *, 8> LocalCXXGlobalInits;
llvm::array_pod_sort(PrioritizedCXXGlobalInits.begin(),
PrioritizedCXXGlobalInits.end());
for (SmallVectorImpl<GlobalInitData>::iterator
I = PrioritizedCXXGlobalInits.begin(),
E = PrioritizedCXXGlobalInits.end();
I != E;) {
SmallVectorImpl<GlobalInitData>::iterator PrioE =
std::upper_bound(I + 1, E, *I, GlobalInitPriorityCmp());
for (; I < PrioE; ++I)
ModuleInits.push_back(I->second);
}
PrioritizedCXXGlobalInits.clear();
}
// Now append the ones without specified priority.
for (auto F : CXXGlobalInits)
ModuleInits.push_back(F);
CXXGlobalInits.clear();
llvm::FunctionType *FTy = llvm::FunctionType::get(VoidTy, false);
const CGFunctionInfo &FI = getTypes().arrangeNullaryFunction();
// We now build the initializer for this module, which has a mangled name
// as per the Itanium ABI . The action of the initializer is guarded so that
// each init is run just once (even though a module might be imported
// multiple times via nested use).
llvm::Function *Fn;
llvm::GlobalVariable *Guard = nullptr;
{
SmallString<256> InitFnName;
llvm::raw_svector_ostream Out(InitFnName);
cast<ItaniumMangleContext>(getCXXABI().getMangleContext())
.mangleModuleInitializer(Primary, Out);
Fn = CreateGlobalInitOrCleanUpFunction(
FTy, llvm::Twine(InitFnName), FI, SourceLocation(), false,
llvm::GlobalVariable::ExternalLinkage);
Guard = new llvm::GlobalVariable(getModule(), Int8Ty, /*isConstant=*/false,
llvm::GlobalVariable::InternalLinkage,
llvm::ConstantInt::get(Int8Ty, 0),
InitFnName.str() + "__in_chrg");
}
CharUnits GuardAlign = CharUnits::One();
Guard->setAlignment(GuardAlign.getAsAlign());
CodeGenFunction(*this).GenerateCXXGlobalInitFunc(
Fn, ModuleInits, ConstantAddress(Guard, Int8Ty, GuardAlign));
// We allow for the case that a module object is added to a linked binary
// without a specific call to the the initializer. This also ensure that
// implementation partition initializers are called when the partition
// is not imported as an interface.
AddGlobalCtor(Fn);
// See the comment in EmitCXXGlobalInitFunc about OpenCL global init
// functions.
if (getLangOpts().OpenCL) {
GenKernelArgMetadata(Fn);
Fn->setCallingConv(llvm::CallingConv::SPIR_KERNEL);
}
assert(!getLangOpts().CUDA || !getLangOpts().CUDAIsDevice ||
getLangOpts().GPUAllowDeviceInit);
if (getLangOpts().HIP && getLangOpts().CUDAIsDevice) {
Fn->setCallingConv(llvm::CallingConv::AMDGPU_KERNEL);
Fn->addFnAttr("device-init");
}
ModuleInits.clear();
}
static SmallString<128> getTransformedFileName(llvm::Module &M) {
SmallString<128> FileName = llvm::sys::path::filename(M.getName());
@ -771,26 +650,7 @@ CodeGenModule::EmitCXXGlobalInitFunc() {
while (!CXXGlobalInits.empty() && !CXXGlobalInits.back())
CXXGlobalInits.pop_back();
// When we import C++20 modules, we must run their initializers first.
SmallVector<llvm::Function *, 8> ModuleInits;
if (CXX20ModuleInits)
for (Module *M : ImportedModules) {
llvm::FunctionType *FTy = llvm::FunctionType::get(VoidTy, false);
SmallString<256> FnName;
{
llvm::raw_svector_ostream Out(FnName);
cast<ItaniumMangleContext>(getCXXABI().getMangleContext())
.mangleModuleInitializer(M, Out);
}
assert(!GetGlobalValue(FnName.str()) &&
"We should only have one use of the initializer call");
llvm::Function *Fn = llvm::Function::Create(
FTy, llvm::Function::ExternalLinkage, FnName.str(), &getModule());
ModuleInits.push_back(Fn);
}
if (ModuleInits.empty() && CXXGlobalInits.empty() &&
PrioritizedCXXGlobalInits.empty())
if (CXXGlobalInits.empty() && PrioritizedCXXGlobalInits.empty())
return;
llvm::FunctionType *FTy = llvm::FunctionType::get(VoidTy, false);
@ -816,13 +676,6 @@ CodeGenModule::EmitCXXGlobalInitFunc() {
llvm::Function *Fn = CreateGlobalInitOrCleanUpFunction(
FTy, "_GLOBAL__I_" + getPrioritySuffix(Priority), FI);
// Prepend the module inits to the highest priority set.
if (!ModuleInits.empty()) {
for (auto F : ModuleInits)
LocalCXXGlobalInits.push_back(F);
ModuleInits.clear();
}
for (; I < PrioE; ++I)
LocalCXXGlobalInits.push_back(I->second);
@ -832,33 +685,17 @@ CodeGenModule::EmitCXXGlobalInitFunc() {
PrioritizedCXXGlobalInits.clear();
}
if (getCXXABI().useSinitAndSterm() && ModuleInits.empty() &&
CXXGlobalInits.empty())
if (getCXXABI().useSinitAndSterm() && CXXGlobalInits.empty())
return;
for (auto F : CXXGlobalInits)
ModuleInits.push_back(F);
CXXGlobalInits.clear();
// Include the filename in the symbol name. Including "sub_" matches gcc
// and makes sure these symbols appear lexicographically behind the symbols
// with priority emitted above.
llvm::Function *Fn;
if (CXX20ModuleInits && getContext().getModuleForCodeGen()) {
SmallString<256> InitFnName;
llvm::raw_svector_ostream Out(InitFnName);
cast<ItaniumMangleContext>(getCXXABI().getMangleContext())
.mangleModuleInitializer(getContext().getModuleForCodeGen(), Out);
Fn = CreateGlobalInitOrCleanUpFunction(
FTy, llvm::Twine(InitFnName), FI, SourceLocation(), false,
llvm::GlobalVariable::ExternalLinkage);
} else
Fn = CreateGlobalInitOrCleanUpFunction(
FTy,
llvm::Twine("_GLOBAL__sub_I_", getTransformedFileName(getModule())),
FI);
llvm::Function *Fn = CreateGlobalInitOrCleanUpFunction(
FTy, llvm::Twine("_GLOBAL__sub_I_", getTransformedFileName(getModule())),
FI);
CodeGenFunction(*this).GenerateCXXGlobalInitFunc(Fn, ModuleInits);
CodeGenFunction(*this).GenerateCXXGlobalInitFunc(Fn, CXXGlobalInits);
AddGlobalCtor(Fn);
// In OpenCL global init functions must be converted to kernels in order to
@ -881,7 +718,7 @@ CodeGenModule::EmitCXXGlobalInitFunc() {
Fn->addFnAttr("device-init");
}
ModuleInits.clear();
CXXGlobalInits.clear();
}
void CodeGenModule::EmitCXXGlobalCleanUpFunc() {

View File

@ -136,13 +136,6 @@ CodeGenModule::CodeGenModule(ASTContext &C, const HeaderSearchOptions &HSO,
GlobalsInt8PtrTy = Int8Ty->getPointerTo(DL.getDefaultGlobalsAddressSpace());
ASTAllocaAddressSpace = getTargetCodeGenInfo().getASTAllocaAddressSpace();
// Build C++20 Module initializers.
// TODO: Add Microsoft here once we know the mangling required for the
// initializers.
CXX20ModuleInits =
LangOpts.CPlusPlusModules && getCXXABI().getMangleContext().getKind() ==
ItaniumMangleContext::MK_Itanium;
RuntimeCC = getTargetCodeGenInfo().getABIInfo().getRuntimeCC();
if (LangOpts.ObjC)
@ -516,18 +509,12 @@ static void setVisibilityFromDLLStorageClass(const clang::LangOptions &LO,
}
void CodeGenModule::Release() {
Module *Primary = getContext().getModuleForCodeGen();
if (CXX20ModuleInits && Primary)
EmitModuleInitializers(Primary);
EmitDeferred();
EmitVTablesOpportunistically();
applyGlobalValReplacements();
applyReplacements();
emitMultiVersionFunctions();
if (CXX20ModuleInits && Primary && Primary->isInterfaceOrPartition())
EmitCXXModuleInitFunc(Primary);
else
EmitCXXGlobalInitFunc();
EmitCXXGlobalInitFunc();
EmitCXXGlobalCleanUpFunc();
registerGlobalDtorsWithAtExit();
EmitCXXThreadLocalInitFunc();
@ -2504,31 +2491,6 @@ static void addLinkOptionsPostorder(CodeGenModule &CGM, Module *Mod,
}
}
void CodeGenModule::EmitModuleInitializers(clang::Module *Primary) {
// Emit the initializers in the order that sub-modules appear in the
// source, first Global Module Fragments, if present.
if (auto GMF = Primary->getGlobalModuleFragment()) {
for (Decl *D : getContext().getModuleInitializers(GMF)) {
assert(D->getKind() == Decl::Var && "GMF initializer decl is not a var?");
EmitTopLevelDecl(D);
}
}
// Second any associated with the module, itself.
for (Decl *D : getContext().getModuleInitializers(Primary)) {
// Skip import decls, the inits for those are called explicitly.
if (D->getKind() == Decl::Import)
continue;
EmitTopLevelDecl(D);
}
// Third any associated with the Privat eMOdule Fragment, if present.
if (auto PMF = Primary->getPrivateModuleFragment()) {
for (Decl *D : getContext().getModuleInitializers(PMF)) {
assert(D->getKind() == Decl::Var && "PMF initializer decl is not a var?");
EmitTopLevelDecl(D);
}
}
}
void CodeGenModule::EmitModuleLinkOptions() {
// Collect the set of all of the modules we want to visit to emit link
// options, which is essentially the imported modules and all of their
@ -2934,19 +2896,12 @@ bool CodeGenModule::MayBeEmittedEagerly(const ValueDecl *Global) {
// explicitly instantiated, so they should not be emitted eagerly.
return false;
}
if (const auto *VD = dyn_cast<VarDecl>(Global)) {
if (const auto *VD = dyn_cast<VarDecl>(Global))
if (Context.getInlineVariableDefinitionKind(VD) ==
ASTContext::InlineVariableDefinitionKind::WeakUnknown)
// A definition of an inline constexpr static data member may change
// linkage later if it's redeclared outside the class.
return false;
if (CXX20ModuleInits && VD->getOwningModule()) {
// For CXX20, module-owned initializers need to be deferred, since it is
// not known at this point if they will be run for the current module or
// as part of the initializer for an imported one.
return false;
}
}
// If OpenMP is enabled and threadprivates must be generated like TLS, delay
// codegen for global variables, because they may be marked as threadprivate.
if (LangOpts.OpenMP && LangOpts.OpenMPUseTLS &&
@ -6243,12 +6198,6 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) {
DI->EmitImportDecl(*Import);
}
// For CXX20 we are done - we will call the module initializer for the
// imported module, and that will likewise call those for any imports it
// has.
if (CXX20ModuleInits)
break;
// Find all of the submodules and emit the module initializers.
llvm::SmallPtrSet<clang::Module *, 16> Visited;
SmallVector<clang::Module *, 16> Stack;

View File

@ -303,7 +303,7 @@ private:
std::unique_ptr<CGCXXABI> ABI;
llvm::LLVMContext &VMContext;
std::string ModuleNameHash;
bool CXX20ModuleInits = false;
std::unique_ptr<CodeGenTBAA> TBAA;
mutable std::unique_ptr<TargetCodeGenInfo> TheTargetCodeGenInfo;
@ -1574,9 +1574,6 @@ private:
/// Emit the function that initializes C++ thread_local variables.
void EmitCXXThreadLocalInitFunc();
/// Emit the function that initializes global variables for a C++ Module.
void EmitCXXModuleInitFunc(clang::Module *Primary);
/// Emit the function that initializes C++ globals.
void EmitCXXGlobalInitFunc();
@ -1644,9 +1641,6 @@ private:
/// Emit the llvm.used and llvm.compiler.used metadata.
void emitLLVMUsed();
/// For C++20 Itanium ABI, emit the initializers for the module.
void EmitModuleInitializers(clang::Module *Primary);
/// Emit the link options introduced by imported modules.
void EmitModuleLinkOptions();

View File

@ -172,29 +172,6 @@ void clang::ParseAST(Sema &S, bool PrintStats, bool SkipFunctionBodies) {
for (Decl *D : S.WeakTopLevelDecls())
Consumer->HandleTopLevelDecl(DeclGroupRef(D));
// For C++20 modules, the codegen for module initializers needs to be altered
// and to be able to use a name based on the module name.
// At this point, we should know if we are building a non-header C++20 module.
if (S.getLangOpts().CPlusPlusModules && !S.getLangOpts().IsHeaderFile &&
!S.getLangOpts().CurrentModule.empty()) {
// If we are building the module from source, then the top level module
// will be here.
Module *CodegenModule = S.getCurrentModule();
bool Interface = true;
if (CodegenModule)
// We only use module initializers for interfaces (including partition
// implementation units).
Interface = S.currentModuleIsInterface();
else
// If we are building the module from a PCM file, then the module can be
// found here.
CodegenModule = S.getPreprocessor().getCurrentModule();
// If neither. then ....
assert(CodegenModule && "codegen for a module, but don't know which?");
if (Interface)
S.getASTContext().setModuleForCodeGen(CodegenModule);
}
Consumer->HandleTranslationUnit(S.getASTContext());
// Finalize the template instantiation observer chain.

View File

@ -344,16 +344,6 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
// statements, so imports are allowed.
ImportState = ModuleImportState::ImportAllowed;
// For an implementation, We already made an implicit import (its interface).
// Make and return the import decl to be added to the current TU.
if (MDK == ModuleDeclKind::Implementation) {
// Make the import decl for the interface.
ImportDecl *Import =
ImportDecl::Create(Context, CurContext, ModuleLoc, Mod, Path[0].second);
// and return it to be added.
return ConvertDeclToDeclGroup(Import);
}
// FIXME: Create a ModuleDecl.
return nullptr;
}

View File

@ -1,41 +0,0 @@
// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 %s \
// RUN: -emit-module-interface -o HasPMF.pcm
// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 HasPMF.pcm \
// RUN: -S -emit-llvm -o - | FileCheck %s
module;
struct Glob {
Glob(){};
};
Glob G;
export module HasPMF;
export struct InMod {
InMod(){};
};
export InMod IM;
module :private;
struct InPMF {
InPMF(){};
};
InPMF P;
// CHECK: define internal void @__cxx_global_var_init
// CHECK: call void @_ZN4GlobC1Ev
// CHECK: define internal void @__cxx_global_var_init
// CHECK: call void @_ZNW6HasPMF5InPMFC1Ev
// CHECK: define internal void @__cxx_global_var_init
// CHECK: call void @_ZNW6HasPMF5InModC1Ev
// CHECK: define void @_ZGIW6HasPMF
// CHECK: store i8 1, ptr @_ZGIW6HasPMF__in_chrg
// CHECK: call void @__cxx_global_var_init
// CHECK: call void @__cxx_global_var_init
// CHECK: call void @__cxx_global_var_init

View File

@ -1,186 +0,0 @@
// RUN: rm -rf %t
// RUN: split-file %s %t
// RUN: cd %t
// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 N.cpp \
// RUN: -emit-module-interface -o N.pcm
// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 N.pcm -S -emit-llvm \
// RUN: -o - | FileCheck %s --check-prefix=CHECK-N
// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 O.cpp \
// RUN: -emit-module-interface -o O.pcm
// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 O.pcm -S -emit-llvm \
// RUN: -o - | FileCheck %s --check-prefix=CHECK-O
// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 M-part.cpp \
// RUN: -emit-module-interface -o M-part.pcm
// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 M-part.pcm -S \
// RUN: -emit-llvm -o - | FileCheck %s --check-prefix=CHECK-P
// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 M.cpp \
// RUN: -fmodule-file=N.pcm -fmodule-file=O.pcm -fmodule-file=M-part.pcm \
// RUN: -emit-module-interface -o M.pcm
// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 M.pcm -S -emit-llvm \
// RUN: -o - | FileCheck %s --check-prefix=CHECK-M
// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 useM.cpp \
// RUN: -fmodule-file=M.pcm -S -emit-llvm -o - \
// RUN: | FileCheck %s --check-prefix=CHECK-USE
// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 M-impl.cpp \
// RUN: -fmodule-file=M.pcm -S -emit-llvm -o - \
// RUN: | FileCheck %s --check-prefix=CHECK-IMPL
//--- N-h.h
struct Oink {
Oink(){};
};
Oink Hog;
//--- N.cpp
module;
#include "N-h.h"
export module N;
export struct Quack {
Quack(){};
};
export Quack Duck;
// CHECK-N: define internal void @__cxx_global_var_init
// CHECK-N: call void @_ZN4OinkC1Ev
// CHECK-N: define internal void @__cxx_global_var_init
// CHECK-N: call void @_ZNW1N5QuackC1Ev
// CHECK-N: define void @_ZGIW1N
// CHECK-N: store i8 1, ptr @_ZGIW1N__in_chrg
// CHECK-N: call void @__cxx_global_var_init
// CHECK-N: call void @__cxx_global_var_init
//--- O-h.h
struct Meow {
Meow(){};
};
Meow Cat;
//--- O.cpp
module;
#include "O-h.h"
export module O;
export struct Bark {
Bark(){};
};
export Bark Dog;
// CHECK-O: define internal void @__cxx_global_var_init
// CHECK-O: call void @_ZN4MeowC2Ev
// CHECK-O: define internal void @__cxx_global_var_init
// CHECK-O: call void @_ZNW1O4BarkC1Ev
// CHECK-O: define void @_ZGIW1O
// CHECK-O: store i8 1, ptr @_ZGIW1O__in_chrg
// CHECK-O: call void @__cxx_global_var_init
// CHECK-O: call void @__cxx_global_var_init
//--- P-h.h
struct Croak {
Croak(){};
};
Croak Frog;
//--- M-part.cpp
module;
#include "P-h.h"
module M:Part;
struct Squawk {
Squawk(){};
};
Squawk parrot;
// CHECK-P: define internal void @__cxx_global_var_init
// CHECK-P: call void @_ZN5CroakC1Ev
// CHECK-P: define internal void @__cxx_global_var_init
// CHECK-P: call void @_ZNW1M6SquawkC1Ev
// CHECK-P: define void @_ZGIW1MWP4Part
// CHECK-P: store i8 1, ptr @_ZGIW1MWP4Part__in_chrg
// CHECK-P: call void @__cxx_global_var_init
// CHECK-P: call void @__cxx_global_var_init
//--- M-h.h
struct Moo {
Moo(){};
};
Moo Cow;
//--- M.cpp
module;
#include "M-h.h"
export module M;
import N;
export import O;
import :Part;
export struct Baa {
int x;
Baa(){};
Baa(int x) : x(x) {}
int getX() { return x; }
};
export Baa Sheep(10);
// CHECK-M: define internal void @__cxx_global_var_init
// CHECK-M: call void @_ZN3MooC1Ev
// CHECK-M: define internal void @__cxx_global_var_init
// CHECK-M: call void @_ZNW1M3BaaC1Ei
// CHECK-M: declare void @_ZGIW1O()
// CHECK-M: declare void @_ZGIW1N()
// CHECK-M: declare void @_ZGIW1MWP4Part()
// CHECK-M: define void @_ZGIW1M
// CHECK-M: store i8 1, ptr @_ZGIW1M__in_chrg
// CHECK-M: call void @_ZGIW1O()
// CHECK-M: call void @_ZGIW1N()
// CHECK-M: call void @_ZGIW1MWP4Part()
// CHECK-M: call void @__cxx_global_var_init
// CHECK-M: call void @__cxx_global_var_init
//--- useM.cpp
import M;
int main() {
return Sheep.getX();
}
// CHECK-USE: declare void @_ZGIW1M
// CHECK-USE: define internal void @_GLOBAL__sub_I_useM.cpp
// CHECK-USE: call void @_ZGIW1M()
//--- M-impl.cpp
module M;
int foo(int i) { return i + 1; }
// CHECK-IMPL: declare void @_ZGIW1M
// CHECK-IMPL: define internal void @_GLOBAL__sub_I_M_impl.cpp
// CHECK-IMPL: call void @_ZGIW1M()