Skip to content

Commit 17c5d6f

Browse files
authored
Add a flag to disable compile-time preallocated instantiation caches (for type metadata and protocol conformances) (#41148)
1 parent f05f231 commit 17c5d6f

File tree

9 files changed

+144
-5
lines changed

9 files changed

+144
-5
lines changed

include/swift/AST/IRGenOptions.h

+5
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,10 @@ class IRGenOptions {
370370

371371
unsigned InternalizeAtLink : 1;
372372

373+
/// Whether to avoid emitting zerofill globals as preallocated type metadata
374+
/// and prototol conformance caches.
375+
unsigned NoPreallocatedInstantiationCaches : 1;
376+
373377
/// The number of threads for multi-threaded code generation.
374378
unsigned NumThreads = 0;
375379

@@ -433,6 +437,7 @@ class IRGenOptions {
433437
EnableGlobalISel(false), VirtualFunctionElimination(false),
434438
WitnessMethodElimination(false), ConditionalRuntimeRecords(false),
435439
InternalizeAtLink(false),
440+
NoPreallocatedInstantiationCaches(false),
436441
CmdArgs(),
437442
SanitizeCoverage(llvm::SanitizerCoverageOptions()),
438443
TypeInfoFilter(TypeInfoDumpFilter::All) {}

include/swift/Option/FrontendOptions.td

+4
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,10 @@ def conditional_runtime_records : Flag<["-"], "conditional-runtime-records">,
579579
Flags<[FrontendOption, NoInteractiveOption, HelpHidden]>,
580580
HelpText<"Allow removal of runtime metadata records (public types, protocol conformances) based on whether they're used or unused">;
581581

582+
def disable_preallocated_instantiation_caches : Flag<["-"], "disable-preallocated-instantiation-caches">,
583+
Flags<[FrontendOption, NoInteractiveOption, HelpHidden]>,
584+
HelpText<"Avoid preallocating metadata instantiation caches in globals">;
585+
582586
def disable_previous_implementation_calls_in_dynamic_replacements :
583587
Flag<["-"], "disable-previous-implementation-calls-in-dynamic-replacements">,
584588
Flags<[FrontendOption, NoInteractiveOption, HelpHidden]>,

lib/Frontend/CompilerInvocation.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -2182,6 +2182,10 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args,
21822182
Opts.InternalizeAtLink = true;
21832183
}
21842184

2185+
if (Args.hasArg(OPT_disable_preallocated_instantiation_caches)) {
2186+
Opts.NoPreallocatedInstantiationCaches = true;
2187+
}
2188+
21852189
// Default to disabling swift async extended frame info on anything but
21862190
// darwin. Other platforms are unlikely to have support for extended frame
21872191
// pointer information.

lib/IRGen/GenMeta.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -1267,7 +1267,7 @@ namespace {
12671267
}
12681268

12691269
void addMetadataInstantiationCache() {
1270-
if (!HasMetadata) {
1270+
if (!HasMetadata || IGM.getOptions().NoPreallocatedInstantiationCaches) {
12711271
B.addInt32(0);
12721272
return;
12731273
}
@@ -2569,6 +2569,8 @@ namespace {
25692569

25702570
/// Emit the instantiation cache variable for the template.
25712571
void emitInstantiationCache() {
2572+
if (IGM.IRGen.Opts.NoPreallocatedInstantiationCaches) return;
2573+
25722574
auto cache = cast<llvm::GlobalVariable>(
25732575
IGM.getAddrOfTypeMetadataInstantiationCache(Target, ForDefinition));
25742576
auto init =

lib/IRGen/GenProto.cpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -1978,8 +1978,11 @@ namespace {
19781978
Description.requiresSpecialization);
19791979
// Instantiation function
19801980
B.addRelativeAddressOrNull(Description.instantiationFn);
1981+
19811982
// Private data
1982-
{
1983+
if (IGM.IRGen.Opts.NoPreallocatedInstantiationCaches) {
1984+
B.addInt32(0);
1985+
} else {
19831986
auto privateDataTy =
19841987
llvm::ArrayType::get(IGM.Int8PtrTy,
19851988
swift::NumGenericMetadataPrivateDataWords);

stdlib/public/runtime/Metadata.cpp

+67-3
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,35 @@ namespace {
389389
};
390390

391391
using LazyGenericMetadataCache = Lazy<GenericMetadataCache>;
392-
}
392+
393+
class GlobalMetadataCacheEntry {
394+
public:
395+
const TypeContextDescriptor *Description;
396+
GenericMetadataCache Cache;
397+
398+
GlobalMetadataCacheEntry(const TypeContextDescriptor *description)
399+
: Description(description), Cache(*description->getGenericContext()) {}
400+
401+
intptr_t getKeyIntValueForDump() {
402+
return reinterpret_cast<intptr_t>(Description);
403+
}
404+
bool matchesKey(const TypeContextDescriptor *description) const {
405+
return description == Description;
406+
}
407+
friend llvm::hash_code hash_value(const GlobalMetadataCacheEntry &value) {
408+
return llvm::hash_value(value.Description);
409+
}
410+
static size_t
411+
getExtraAllocationSize(const TypeContextDescriptor *description) {
412+
return 0;
413+
}
414+
size_t getExtraAllocationSize() const { return 0; }
415+
};
416+
417+
static SimpleGlobalCache<GlobalMetadataCacheEntry, GlobalMetadataCacheTag>
418+
GlobalMetadataCache;
419+
420+
} // end anonymous namespace
393421

394422
/// Fetch the metadata cache for a generic metadata structure.
395423
static GenericMetadataCache &getCache(
@@ -401,6 +429,11 @@ static GenericMetadataCache &getCache(
401429
sizeof(GenericMetadataInstantiationCache::PrivateData),
402430
"metadata cache is larger than the allowed space");
403431

432+
auto *cacheStorage = generics.getInstantiationCache();
433+
if (cacheStorage == nullptr) {
434+
return GlobalMetadataCache.getOrInsert(&description).first->Cache;
435+
}
436+
404437
auto lazyCache =
405438
reinterpret_cast<LazyGenericMetadataCache*>(
406439
generics.getInstantiationCache()->PrivateData);
@@ -4606,19 +4639,50 @@ class WitnessTableCacheEntry :
46064639
const void * const *instantiationArgs);
46074640
};
46084641

4609-
} // end anonymous namespace
4610-
46114642
using GenericWitnessTableCache =
46124643
MetadataCache<WitnessTableCacheEntry, GenericWitnessTableCacheTag>;
46134644
using LazyGenericWitnessTableCache = Lazy<GenericWitnessTableCache>;
46144645

4646+
class GlobalWitnessTableCacheEntry {
4647+
public:
4648+
const GenericWitnessTable *Gen;
4649+
GenericWitnessTableCache Cache;
4650+
4651+
GlobalWitnessTableCacheEntry(const GenericWitnessTable *gen)
4652+
: Gen(gen), Cache() {}
4653+
4654+
intptr_t getKeyIntValueForDump() {
4655+
return reinterpret_cast<intptr_t>(Gen);
4656+
}
4657+
bool matchesKey(const GenericWitnessTable *gen) const {
4658+
return gen == Gen;
4659+
}
4660+
friend llvm::hash_code hash_value(const GlobalWitnessTableCacheEntry &value) {
4661+
return llvm::hash_value(value.Gen);
4662+
}
4663+
static size_t
4664+
getExtraAllocationSize(const GenericWitnessTable *gen) {
4665+
return 0;
4666+
}
4667+
size_t getExtraAllocationSize() const { return 0; }
4668+
};
4669+
4670+
static SimpleGlobalCache<GlobalWitnessTableCacheEntry, GlobalWitnessTableCacheTag>
4671+
GlobalWitnessTableCache;
4672+
4673+
} // end anonymous namespace
4674+
46154675
/// Fetch the cache for a generic witness-table structure.
46164676
static GenericWitnessTableCache &getCache(const GenericWitnessTable *gen) {
46174677
// Keep this assert even if you change the representation above.
46184678
static_assert(sizeof(LazyGenericWitnessTableCache) <=
46194679
sizeof(GenericWitnessTable::PrivateDataType),
46204680
"metadata cache is larger than the allowed space");
46214681

4682+
if (gen->PrivateData == nullptr) {
4683+
return GlobalWitnessTableCache.getOrInsert(gen).first->Cache;
4684+
}
4685+
46224686
auto lazyCache =
46234687
reinterpret_cast<LazyGenericWitnessTableCache*>(gen->PrivateData.get());
46244688
return lazyCache->get();

stdlib/public/runtime/MetadataAllocatorTags.def

+2
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,7 @@ TAG(GenericWitnessTableCache, 16)
4242
TAG(GenericClassMetadata, 17)
4343
TAG(GenericValueMetadata, 18)
4444
TAG(SingletonGenericWitnessTableCache, 19)
45+
TAG(GlobalMetadataCache, 20)
46+
TAG(GlobalWitnessTableCache, 21)
4547

4648
#undef TAG
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift %s -Xfrontend -disable-preallocated-instantiation-caches -o %t/a.out
3+
// RUN: %target-codesign %t/a.out
4+
// RUN: %target-run %t/a.out
5+
6+
// REQUIRES: executable_test
7+
8+
public class Generic<T> {
9+
public func m1(t: T) -> T { return t }
10+
public func m2(t: T) -> T { return t }
11+
}
12+
13+
protocol MyProtocol {
14+
associatedtype T
15+
func foo() -> T
16+
}
17+
18+
public struct MyStruct<T>: MyProtocol {
19+
func foo() -> T { fatalError() }
20+
}
21+
22+
print(Generic<Int>())
23+
print(MyStruct<Int>())
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -module-name main %s -emit-ir | %FileCheck %s --check-prefix=CHECK-CACHE
3+
// RUN: %target-swift-frontend -module-name main %s -emit-ir -disable-preallocated-instantiation-caches | %FileCheck %s --check-prefix=CHECK-NOCACHE
4+
5+
public class Generic<T> {
6+
public func m1(t: T) -> T { return t }
7+
public func m2(t: T) -> T { return t }
8+
}
9+
10+
protocol MyProtocol {
11+
associatedtype T
12+
func foo() -> T
13+
}
14+
15+
public struct MyStruct<T>: MyProtocol {
16+
func foo() -> T { fatalError() }
17+
}
18+
19+
// "metadata instantiation cache for protocol conformance descriptor for main.MyStruct<A> : main.MyProtocol in main"
20+
// CHECK-CACHE: @"$s4main8MyStructVyxGAA0B8ProtocolAAMcMK" = internal global [{{.*}} x i8*] zeroinitializer
21+
// CHECK-CACHE: @"$s4main8MyStructVyxGAA0B8ProtocolAAMc" = {{.*}} @"$s4main8MyStructVyxGAA0B8ProtocolAAMcMK" {{.*}}
22+
// CHECK-NOCACHE-NOT: @"$s4main8MyStructVyxGAA0B8ProtocolAAMcMK"
23+
24+
// "type metadata instantiation cache for main.Generic"
25+
// CHECK-CACHE: @"$s4main7GenericCMI" = internal global [{{.*}} x i8*] zeroinitializer
26+
// CHECK-CACHE: @"$s4main7GenericCMn" = {{.*}} @"$s4main7GenericCMI" {{.*}}
27+
// CHECK-NOCACHE-NOT: @"$s4main7GenericCMI"
28+
29+
// "type metadata instantiation cache for main.MyStruct"
30+
// CHECK-CACHE: @"$s4main8MyStructVMI" = internal global [{{.*}} x i8*] zeroinitializer
31+
// CHECK-CACHE: @"$s4main8MyStructVMn" = {{.*}} @"$s4main8MyStructVMI" {{.*}}
32+
// CHECK-NOCACHE-NOT: @"$s4main8MyStructVMI"

0 commit comments

Comments
 (0)