/* 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.
+ might be included transitively. This arranged by using a guard variable.
+
+ If there are no initalizers at all (and also no imported modules) we reduce
+ this to an empty function (since the Itanium ABI requires that this function
+ be available to a caller, which might be produced by a different
+ implementation).
First we call any initializers for imported modules.
We then call initializers for the Global Module Fragment (if present)
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).
-
+ // As noted above, we create the function, even if it is empty.
// Module initializers for imported modules are emitted first.
- // Collect the modules that we import
+
+ // Collect all the modules that we import
SmallVector<Module *> AllImports;
// Ones that we export
for (auto I : Primary->Exports)
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().
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();
// 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);
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");
+ // If we have a completely empty initializer then we do not want to create
+ // the guard variable.
+ ConstantAddress GuardAddr = ConstantAddress::invalid();
+ if (!AllImports.empty() || !PrioritizedCXXGlobalInits.empty() ||
+ !CXXGlobalInits.empty()) {
+ // Create the guard var.
+ llvm::GlobalVariable *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());
+ GuardAddr = ConstantAddress(Guard, Int8Ty, GuardAlign);
+ }
+ CodeGenFunction(*this).GenerateCXXGlobalInitFunc(Fn, ModuleInits,
+ GuardAddr);
}
- 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 initializer. This also ensure that
+ // 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 ensures that
// implementation partition initializers are called when the partition
// is not imported as an interface.
AddGlobalCtor(Fn);
Fn->addFnAttr("device-init");
}
+ // We are done with the inits.
+ AllImports.clear();
+ PrioritizedCXXGlobalInits.clear();
+ CXXGlobalInits.clear();
ModuleInits.clear();
}
--- /dev/null
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: cd %t
+
+// 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 P.cpp \
+// RUN: -emit-module-interface -fmodule-file=O.pcm -o P.pcm
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 P.pcm -S -emit-llvm \
+// RUN: -o - | FileCheck %s --check-prefix=CHECK-P
+
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 Q.cpp \
+// RUN: -emit-module-interface -fmodule-file=O.pcm -o Q.pcm
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 Q.pcm -S -emit-llvm \
+// RUN: -o - | FileCheck %s --check-prefix=CHECK-Q
+
+// Testing cases where we can elide the module initializer guard variable.
+
+// This module has no global inits and does not import any other module
+//--- O.cpp
+
+export module O;
+
+export int foo ();
+
+// CHECK-O: define void @_ZGIW1O
+// CHECK-O-LABEL: entry
+// CHECK-O-NEXT: ret void
+// CHECK-O-NOT: @_ZGIW1O__in_chrg
+
+// This has no global inits but imports a module, and therefore needs a guard
+// variable.
+//--- P.cpp
+
+export module P;
+
+export import O;
+export int bar ();
+
+// CHECK-P: define void @_ZGIW1P
+// CHECK-P-LABEL: init
+// CHECK-P: store i8 1, ptr @_ZGIW1P__in_chrg
+// CHECK-P: call void @_ZGIW1O()
+// CHECK-P-NOT: call void @__cxx_global_var_init
+
+// This imports a module and has global inits, so needs a guard.
+//--- Q.cpp
+
+export module Q;
+export import O;
+
+export struct Quack {
+ Quack(){};
+};
+
+export Quack Duck;
+
+export int baz ();
+
+// CHECK-Q: define internal void @__cxx_global_var_init
+// CHECK-Q: call {{.*}} @_ZNW1Q5QuackC1Ev
+// CHECK-Q: define void @_ZGIW1Q
+// CHECK-Q: store i8 1, ptr @_ZGIW1Q__in_chrg
+// CHECK-Q: call void @_ZGIW1O()
+// CHECK-Q: call void @__cxx_global_var_init
+