Skip to content

Commit 0ceb517

Browse files
authored
Merge pull request #81649 from eeckstein/global-inlinearray-initialization
Allow more complex InlineArray initializations to end up in a statically initialized global
2 parents 490edfa + 47db3fc commit 0ceb517

25 files changed

+653
-392
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/InitializeStaticGlobals.swift

Lines changed: 389 additions & 259 deletions
Large diffs are not rendered by default.

SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyLoad.swift

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,3 +376,75 @@ private extension Instruction {
376376
return shiftValue > 0
377377
}
378378
}
379+
380+
/// Analyses the global initializer function and returns the `alloc_global` and `store`
381+
/// instructions which initialize the global.
382+
/// Returns nil if `function` has any side-effects beside initializing the global.
383+
///
384+
/// The function's single basic block must contain following code pattern:
385+
/// ```
386+
/// alloc_global @the_global
387+
/// %a = global_addr @the_global
388+
/// %i = some_const_initializer_insts
389+
/// store %i to %a
390+
/// ```
391+
///
392+
/// For all other instructions `handleUnknownInstruction` is called and such an instruction
393+
/// is accepted if `handleUnknownInstruction` returns true.
394+
private func getGlobalInitialization(
395+
of function: Function,
396+
_ context: some Context,
397+
handleUnknownInstruction: (Instruction) -> Bool
398+
) -> (allocInst: AllocGlobalInst, storeToGlobal: StoreInst)? {
399+
guard let block = function.blocks.singleElement else {
400+
return nil
401+
}
402+
403+
var allocInst: AllocGlobalInst? = nil
404+
var globalAddr: GlobalAddrInst? = nil
405+
var store: StoreInst? = nil
406+
407+
for inst in block.instructions {
408+
switch inst {
409+
case is ReturnInst,
410+
is DebugValueInst,
411+
is DebugStepInst,
412+
is BeginAccessInst,
413+
is EndAccessInst:
414+
continue
415+
case let agi as AllocGlobalInst:
416+
if allocInst == nil {
417+
allocInst = agi
418+
continue
419+
}
420+
case let ga as GlobalAddrInst:
421+
if let agi = allocInst, agi.global == ga.global {
422+
globalAddr = ga
423+
}
424+
continue
425+
case let si as StoreInst:
426+
if store == nil,
427+
let ga = globalAddr,
428+
si.destination == ga
429+
{
430+
store = si
431+
continue
432+
}
433+
// Note that the initializer must not contain a `global_value` because `global_value` needs to
434+
// initialize the class metadata at runtime.
435+
default:
436+
if inst.isValidInStaticInitializerOfGlobal(context) {
437+
continue
438+
}
439+
}
440+
if handleUnknownInstruction(inst) {
441+
continue
442+
}
443+
return nil
444+
}
445+
if let store = store {
446+
return (allocInst: allocInst!, storeToGlobal: store)
447+
}
448+
return nil
449+
}
450+

SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyPointerToAddress.swift

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -293,13 +293,6 @@ private extension Value {
293293
return self
294294
}
295295
}
296-
297-
var lookThroughTruncOrBitCast: Value {
298-
if let truncOrBitCast = self as? BuiltinInst, truncOrBitCast.id == .TruncOrBitCast {
299-
return truncOrBitCast.arguments[0]
300-
}
301-
return self
302-
}
303296
}
304297

305298
private extension BuiltinInst {

SwiftCompilerSources/Sources/Optimizer/Utilities/AddressUtils.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ extension AddressUseVisitor {
159159
.GenericFDiv, .GenericMul, .GenericFMul, .GenericSDiv,
160160
.GenericExactSDiv, .GenericShl, .GenericSRem, .GenericSub,
161161
.GenericFSub, .GenericUDiv, .GenericExactUDiv, .GenericURem,
162-
.GenericFRem, .GenericXor, .TaskRunInline, .ZeroInitializer,
162+
.GenericFRem, .GenericXor, .TaskRunInline, .ZeroInitializer, .PrepareInitialization,
163163
.GetEnumTag, .InjectEnumTag:
164164
return leafAddressUse(of: operand)
165165
default:

SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift

Lines changed: 7 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,13 @@ extension Value {
4545
}
4646
}
4747

48+
var lookThroughTruncOrBitCast: Value {
49+
if let truncOrBitCast = self as? BuiltinInst, truncOrBitCast.id == .TruncOrBitCast {
50+
return truncOrBitCast.arguments[0]
51+
}
52+
return self
53+
}
54+
4855
func isInLexicalLiverange(_ context: some Context) -> Bool {
4956
var worklist = ValueWorklist(context)
5057
defer { worklist.deinitialize() }
@@ -846,77 +853,6 @@ extension InstructionRange {
846853
}
847854
}
848855

849-
/// Analyses the global initializer function and returns the `alloc_global` and `store`
850-
/// instructions which initialize the global.
851-
/// Returns nil if `function` has any side-effects beside initializing the global.
852-
///
853-
/// The function's single basic block must contain following code pattern:
854-
/// ```
855-
/// alloc_global @the_global
856-
/// %a = global_addr @the_global
857-
/// %i = some_const_initializer_insts
858-
/// store %i to %a
859-
/// ```
860-
///
861-
/// For all other instructions `handleUnknownInstruction` is called and such an instruction
862-
/// is accepted if `handleUnknownInstruction` returns true.
863-
func getGlobalInitialization(
864-
of function: Function,
865-
_ context: some Context,
866-
handleUnknownInstruction: (Instruction) -> Bool
867-
) -> (allocInst: AllocGlobalInst, storeToGlobal: StoreInst)? {
868-
guard let block = function.blocks.singleElement else {
869-
return nil
870-
}
871-
872-
var allocInst: AllocGlobalInst? = nil
873-
var globalAddr: GlobalAddrInst? = nil
874-
var store: StoreInst? = nil
875-
876-
for inst in block.instructions {
877-
switch inst {
878-
case is ReturnInst,
879-
is DebugValueInst,
880-
is DebugStepInst,
881-
is BeginAccessInst,
882-
is EndAccessInst:
883-
continue
884-
case let agi as AllocGlobalInst:
885-
if allocInst == nil {
886-
allocInst = agi
887-
continue
888-
}
889-
case let ga as GlobalAddrInst:
890-
if let agi = allocInst, agi.global == ga.global {
891-
globalAddr = ga
892-
}
893-
continue
894-
case let si as StoreInst:
895-
if store == nil,
896-
let ga = globalAddr,
897-
si.destination == ga
898-
{
899-
store = si
900-
continue
901-
}
902-
// Note that the initializer must not contain a `global_value` because `global_value` needs to
903-
// initialize the class metadata at runtime.
904-
default:
905-
if inst.isValidInStaticInitializerOfGlobal(context) {
906-
continue
907-
}
908-
}
909-
if handleUnknownInstruction(inst) {
910-
continue
911-
}
912-
return nil
913-
}
914-
if let store = store {
915-
return (allocInst: allocInst!, storeToGlobal: store)
916-
}
917-
return nil
918-
}
919-
920856
func canDynamicallyCast(from sourceType: CanonicalType, to destType: CanonicalType,
921857
in function: Function, sourceTypeIsExact: Bool
922858
) -> Bool? {

SwiftCompilerSources/Sources/SIL/GlobalVariable.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ final public class GlobalVariable : CustomStringConvertible, HasShortDescription
2929

3030
public var shortDescription: String { name.string }
3131

32+
public var type: Type { Type(bridged: bridged.getType()) }
33+
3234
public var isLet: Bool { bridged.isLet() }
3335

3436
public var linkage: Linkage { bridged.getLinkage().linkage }

include/swift/AST/Builtins.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -851,6 +851,10 @@ BUILTIN_MISC_OPERATION_WITH_SILGEN(Alignof, "alignof", "n", Special)
851851
/// own rules.
852852
BUILTIN_MISC_OPERATION_WITH_SILGEN(ZeroInitializer, "zeroInitializer", "n", Special)
853853

854+
/// Like `zeroInitializer`, but does not actually initialize the memory.
855+
/// It only indicates to mandatory passes that the memory is going to be initialized.
856+
BUILTIN_MISC_OPERATION(PrepareInitialization, "prepareInitialization", "n", Special)
857+
854858
// getCurrentExecutor: () async -> Builtin.Executor?
855859
//
856860
// Retrieve the SerialExecutorRef on which the current asynchronous

include/swift/SIL/AddressWalker.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,7 @@ TransitiveAddressWalker<Impl>::walk(SILValue projectedAddress) {
288288
case BuiltinValueKind::GenericXor:
289289
case BuiltinValueKind::TaskRunInline:
290290
case BuiltinValueKind::ZeroInitializer:
291+
case BuiltinValueKind::PrepareInitialization:
291292
case BuiltinValueKind::GetEnumTag:
292293
case BuiltinValueKind::InjectEnumTag:
293294
case BuiltinValueKind::AddressOfRawLayout:

include/swift/SIL/SILBridging.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,7 @@ struct BridgedGlobalVar {
587587
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedStringRef getName() const;
588588
BRIDGED_INLINE bool isLet() const;
589589
BRIDGED_INLINE void setLet(bool value) const;
590+
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedType getType() const;
590591
BRIDGED_INLINE BridgedLinkage getLinkage() const;
591592
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedSourceLoc getSourceLocation() const;
592593
BRIDGED_INLINE bool isPossiblyUsedExternally() const;

include/swift/SIL/SILBridgingImpl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -918,6 +918,10 @@ bool BridgedGlobalVar::isLet() const { return getGlobal()->isLet(); }
918918

919919
void BridgedGlobalVar::setLet(bool value) const { getGlobal()->setLet(value); }
920920

921+
BridgedType BridgedGlobalVar::getType() const {
922+
return getGlobal()->getLoweredType();
923+
}
924+
921925
BridgedLinkage BridgedGlobalVar::getLinkage() const {
922926
return (BridgedLinkage)getGlobal()->getLinkage();
923927
}

lib/AST/Builtins.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3132,6 +3132,7 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) {
31323132
return getUnreachableOperation(Context, Id);
31333133

31343134
case BuiltinValueKind::ZeroInitializer:
3135+
case BuiltinValueKind::PrepareInitialization:
31353136
return getZeroInitializerOperation(Context, Id);
31363137

31373138
case BuiltinValueKind::Once:

lib/IRGen/GenBuiltin.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1353,7 +1353,13 @@ void irgen::emitBuiltinCall(IRGenFunction &IGF, const BuiltinInfo &Builtin,
13531353
}
13541354
return;
13551355
}
1356-
1356+
1357+
if (Builtin.ID == BuiltinValueKind::PrepareInitialization) {
1358+
ASSERT(args.size() > 0 && "only address-variant of prepareInitialization is supported");
1359+
(void)args.claimNext();
1360+
return;
1361+
}
1362+
13571363
if (Builtin.ID == BuiltinValueKind::GetObjCTypeEncoding) {
13581364
(void)args.claimAll();
13591365
Type valueTy = substitutions.getReplacementTypes()[0];

lib/SIL/IR/OperandOwnership.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -917,6 +917,7 @@ BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GenericXor)
917917
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, ZExt)
918918
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, ZExtOrBitCast)
919919
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, ZeroInitializer)
920+
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, PrepareInitialization)
920921
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, PoundAssert)
921922
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, GlobalStringTablePointer)
922923
BUILTIN_OPERAND_OWNERSHIP(InstantaneousUse, TypePtrAuthDiscriminator)

lib/SIL/IR/SILInstruction.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1009,7 +1009,8 @@ MemoryBehavior SILInstruction::getMemoryBehavior() const {
10091009
if (auto *BI = dyn_cast<BuiltinInst>(this)) {
10101010
// Handle Swift builtin functions.
10111011
const BuiltinInfo &BInfo = BI->getBuiltinInfo();
1012-
if (BInfo.ID == BuiltinValueKind::ZeroInitializer) {
1012+
if (BInfo.ID == BuiltinValueKind::ZeroInitializer ||
1013+
BInfo.ID == BuiltinValueKind::PrepareInitialization) {
10131014
// The address form of `zeroInitializer` writes to its argument to
10141015
// initialize it. The value form has no side effects.
10151016
return BI->getArguments().size() > 0

lib/SIL/IR/ValueOwnership.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,7 @@ UNOWNED_OR_NONE_DEPENDING_ON_RESULT(ShuffleVector)
694694
// fields. The initialized value is immediately consumed by an assignment, so it
695695
// must be owned.
696696
OWNED_OR_NONE_DEPENDING_ON_RESULT(ZeroInitializer)
697+
OWNED_OR_NONE_DEPENDING_ON_RESULT(PrepareInitialization)
697698
#undef OWNED_OR_NONE_DEPENDING_ON_RESULT
698699

699700
#define BUILTIN(X,Y,Z)

lib/SIL/Utils/MemAccessUtils.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2657,6 +2657,7 @@ static void visitBuiltinAddress(BuiltinInst *builtin,
26572657

26582658
// zeroInitializer with an address operand zeroes the address.
26592659
case BuiltinValueKind::ZeroInitializer:
2660+
case BuiltinValueKind::PrepareInitialization:
26602661
if (builtin->getAllOperands().size() > 0) {
26612662
visitor(&builtin->getAllOperands()[0]);
26622663
}

lib/SIL/Verifier/SILVerifier.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2387,7 +2387,8 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
23872387
auto builtinKind = BI->getBuiltinKind();
23882388
auto arguments = BI->getArguments();
23892389

2390-
if (builtinKind == BuiltinValueKind::ZeroInitializer) {
2390+
if (builtinKind == BuiltinValueKind::ZeroInitializer ||
2391+
builtinKind == BuiltinValueKind::PrepareInitialization) {
23912392
require(!BI->getSubstitutions(),
23922393
"zeroInitializer has no generic arguments as a SIL builtin");
23932394
if (arguments.size() == 0) {

lib/SILGen/SILGenBuiltin.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2165,11 +2165,13 @@ static ManagedValue emitBuiltinEmplace(SILGenFunction &SGF,
21652165

21662166
auto buffer = dest->getAddressForInPlaceInitialization(SGF, loc);
21672167

2168-
// Zero-initialize the buffer.
2169-
// Aside from providing a modicum of predictability if the memory isn't
2170-
// actually initialized, this also serves to communicate to DI that the memory
2168+
// Mark the buffer as initializedto communicate to DI that the memory
21712169
// is considered initialized from this point.
2172-
SGF.B.createZeroInitAddr(loc, buffer);
2170+
auto markInit = getBuiltinValueDecl(Ctx, Ctx.getIdentifier("prepareInitialization"));
2171+
SGF.B.createBuiltin(loc, markInit->getBaseIdentifier(),
2172+
SILType::getEmptyTupleType(Ctx),
2173+
SubstitutionMap(),
2174+
buffer);
21732175

21742176
SILValue bufferPtr = SGF.B.createAddressToPointer(loc, buffer,
21752177
SILType::getPrimitiveObjectType(SGF.getASTContext().TheRawPointerType),

lib/SILOptimizer/Mandatory/MoveOnlyUtils.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,8 @@ bool noncopyable::memInstMustInitialize(Operand *memOper) {
218218
}
219219
case SILInstructionKind::BuiltinInst: {
220220
auto bi = cast<BuiltinInst>(memInst);
221-
if (bi->getBuiltinKind() == BuiltinValueKind::ZeroInitializer) {
221+
if (bi->getBuiltinKind() == BuiltinValueKind::ZeroInitializer ||
222+
bi->getBuiltinKind() == BuiltinValueKind::PrepareInitialization) {
222223
// `zeroInitializer` with an address operand zeroes out the address operand
223224
return true;
224225
}

lib/SILOptimizer/Transforms/AccessEnforcementReleaseSinking.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ static bool isBarrier(SILInstruction *inst) {
135135
case BuiltinValueKind::IsNegative:
136136
case BuiltinValueKind::WordAtIndex:
137137
case BuiltinValueKind::ZeroInitializer:
138+
case BuiltinValueKind::PrepareInitialization:
138139
case BuiltinValueKind::Once:
139140
case BuiltinValueKind::OnceWithContext:
140141
case BuiltinValueKind::GetObjCTypeEncoding:

lib/SILOptimizer/Transforms/PerformanceInliner.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,10 @@ llvm::cl::opt<int> OSizeClassMethodBenefit(
145145
llvm::cl::desc("The benefit of inlining class methods with -Osize. We only "
146146
"inline very small class methods with -Osize."));
147147

148+
llvm::cl::opt<int> GlobalInitBenefit(
149+
"sil-inline-global-init-benefit", llvm::cl::init(100),
150+
llvm::cl::desc("The benefit of inlining constructors into global initializers."));
151+
148152
llvm::cl::opt<int> TrivialFunctionThreshold(
149153
"sil-inline-trivial-function-threshold", llvm::cl::init(18),
150154
llvm::cl::desc("Approximately up to this cost level a function can be "
@@ -445,6 +449,14 @@ bool isFunctionAutodiffVJP(SILFunction *callee) {
445449
return false;
446450
}
447451

452+
bool isAllocator(SILFunction *callee) {
453+
swift::Demangle::Context Ctx;
454+
if (auto *Root = Ctx.demangleSymbolAsNode(callee->getName())) {
455+
return Root->findByKind(swift::Demangle::Node::Kind::Allocator, 3) != nullptr;
456+
}
457+
return false;
458+
}
459+
448460
bool isProfitableToInlineAutodiffVJP(SILFunction *vjp, SILFunction *caller,
449461
InlineSelection whatToInline,
450462
StringRef stageName) {
@@ -785,6 +797,12 @@ bool SILPerformanceInliner::isProfitableToInline(
785797
Benefit = std::max(Benefit, ExclusivityBenefitWeight);
786798
}
787799

800+
if (AI.getFunction()->isGlobalInitOnceFunction() && isAllocator(Callee)) {
801+
// Inlining constructors into global initializers increase the changes that
802+
// the global can be initialized statically.
803+
CallerWeight.updateBenefit(Benefit, GlobalInitBenefit);
804+
}
805+
788806
if (AI.getFunction()->isThunk()) {
789807
// Only inline trivial functions into thunks (which will not increase the
790808
// code size).

0 commit comments

Comments
 (0)