Skip to content

Commit b75a892

Browse files
authored
Merge branch 'main' into windows-ci
2 parents 805dc30 + bc88160 commit b75a892

File tree

434 files changed

+10918
-4763
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

434 files changed

+10918
-4763
lines changed

Runtimes/Core/core/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ add_library(swiftCore
8080
FlatMap.swift
8181
Flatten.swift
8282
FloatingPoint.swift
83-
FloatingPointToString.swift
8483
Hashable.swift
8584
AnyHashable.swift # ORDER DEPENDENCY
8685
Hasher.swift

Runtimes/Core/runtime/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ add_library(swiftRuntime OBJECT
5252
RefCount.cpp
5353
ReflectionMirror.cpp
5454
RuntimeInvocationsTracking.cpp
55+
SwiftDtoa.cpp
5556
SwiftTLSContext.cpp
5657
ThreadingError.cpp
5758
Tracing.cpp

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/AllocBoxToStack.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,8 @@ private extension ApplySite {
504504
{
505505
if self is FullApplySite,
506506
// If the function is inlined later, there is no point in specializing it.
507-
!callee.shouldOptimize || callee.inlineStrategy == .always
507+
!callee.shouldOptimize || callee.inlineStrategy == .heuristicAlways ||
508+
callee.inlineStrategy == .always
508509
{
509510
return nil
510511
}

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ConstantCapturePropagation.swift

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ let constantCapturePropagation = FunctionPass(name: "constant-capture-propagatio
6666
continue
6767
}
6868

69+
if !context.continueWithNextSubpassRun(for: partialApply) {
70+
return
71+
}
72+
6973
optimizeClosureWithDeadCaptures(of: partialApply, context)
7074

7175
if partialApply.isDeleted {
@@ -301,9 +305,15 @@ private extension PartialApplyInst {
301305
var nonConstArgs = [Operand]()
302306
var hasKeypath = false
303307
for argOp in argumentOperands {
304-
if argOp.value.isConstant(hasKeypath: &hasKeypath) {
308+
// In non-OSSA we don't know where to insert the compensating release for a propagated keypath.
309+
// Therefore bail if a keypath has multiple uses.
310+
switch argOp.value.isConstant(requireSingleUse: !parentFunction.hasOwnership && !isOnStack) {
311+
case .constant:
312+
constArgs.append(argOp)
313+
case .constantWithKeypath:
305314
constArgs.append(argOp)
306-
} else {
315+
hasKeypath = true
316+
case .notConstant:
307317
nonConstArgs.append(argOp)
308318
}
309319
}
@@ -333,33 +343,49 @@ private extension FullApplySite {
333343
}
334344
}
335345

346+
private enum ConstantKind {
347+
case notConstant
348+
case constant
349+
case constantWithKeypath
350+
351+
func merge(with other: ConstantKind) -> ConstantKind {
352+
switch (self, other) {
353+
case (.notConstant, _): return .notConstant
354+
case (_, .notConstant): return .notConstant
355+
case (.constant, .constant): return .constant
356+
default: return .constantWithKeypath
357+
}
358+
}
359+
}
360+
336361
private extension Value {
337-
func isConstant(hasKeypath: inout Bool) -> Bool {
362+
func isConstant(requireSingleUse: Bool) -> ConstantKind {
338363
// All instructions handled here must also be handled in
339364
// `FunctionSignatureSpecializationMangler::mangleConstantProp`.
365+
let result: ConstantKind
340366
switch self {
341367
case let si as StructInst:
342-
for op in si.operands {
343-
if !op.value.isConstant(hasKeypath: &hasKeypath) {
344-
return false
345-
}
346-
}
347-
return true
368+
result = si.operands.reduce(.constant, {
369+
$0.merge(with: $1.value.isConstant(requireSingleUse: requireSingleUse))
370+
})
348371
case is ThinToThickFunctionInst, is ConvertFunctionInst, is UpcastInst, is OpenExistentialRefInst:
349-
return (self as! UnaryInstruction).operand.value.isConstant(hasKeypath: &hasKeypath)
372+
result = (self as! UnaryInstruction).operand.value.isConstant(requireSingleUse: requireSingleUse)
350373
case is StringLiteralInst, is IntegerLiteralInst, is FloatLiteralInst, is FunctionRefInst, is GlobalAddrInst:
351-
return true
374+
result = .constant
352375
case let keyPath as KeyPathInst:
353-
hasKeypath = true
354376
guard keyPath.operands.isEmpty,
355377
keyPath.hasPattern,
356378
!keyPath.substitutionMap.hasAnySubstitutableParams
357379
else {
358-
return false
380+
return .notConstant
359381
}
360-
return true
382+
result = .constantWithKeypath
361383
default:
362-
return false
384+
return .notConstant
385+
}
386+
if requireSingleUse, result == .constantWithKeypath, !uses.ignoreDebugUses.isSingleUse {
387+
return .notConstant
363388
}
389+
return result
364390
}
365391
}

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/DeadStoreElimination.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,15 @@ private extension StoreInst {
167167

168168
var hasValidOwnershipForDeadStoreElimination: Bool {
169169
switch storeOwnership {
170-
case .unqualified, .trivial:
170+
case .unqualified:
171171
return true
172+
case .trivial:
173+
// Storing a trivial enum case in a non-trivial enum must be treated like a non-trivial
174+
// init or assign, e.g.
175+
// %1 = enum $Optional<String>, #Optional.none!enumelt
176+
// store %1 to [trivial] %0 // <- cannot delete this store!
177+
// store %2 to [assign] %0
178+
return source.type.isTrivial(in: parentFunction)
172179
case .initialize, .assign:
173180
// In OSSA, non-trivial values cannot be dead-store eliminated because that could shrink
174181
// the lifetime of the original stored value (because it's not kept in memory anymore).

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LoopInvariantCodeMotion.swift

Lines changed: 93 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,12 @@ private extension MovableInstructions {
693693
accessPath: AccessPath,
694694
context: FunctionPassContext
695695
) -> Bool {
696+
697+
// If the memory is not initialized at all exits, it would be wrong to insert stores at exit blocks.
698+
guard memoryIsInitializedAtAllExits(of: loop, accessPath: accessPath, context) else {
699+
return false
700+
}
701+
696702
// Initially load the value in the loop pre header.
697703
let builder = Builder(before: loop.preheader!.terminator, context)
698704
var firstStore: StoreInst?
@@ -721,7 +727,18 @@ private extension MovableInstructions {
721727
guard let firstStore else {
722728
return false
723729
}
724-
730+
731+
// We currently don't support split `load [take]`, i.e. `load [take]` which does _not_ load all
732+
// non-trivial fields of the initial value.
733+
for case let load as LoadInst in loadsAndStores {
734+
if load.loadOwnership == .take,
735+
let path = accessPath.getProjection(to: load.address.accessPath),
736+
!firstStore.destination.type.isProjectingEntireNonTrivialMembers(path: path, in: load.parentFunction)
737+
{
738+
return false
739+
}
740+
}
741+
725742
var ssaUpdater = SSAUpdater(
726743
function: firstStore.parentFunction,
727744
type: firstStore.destination.type.objectType,
@@ -792,7 +809,13 @@ private extension MovableInstructions {
792809
let rootVal = currentVal ?? ssaUpdater.getValue(inMiddleOf: block)
793810

794811
if loadInst.operand.value.accessPath == accessPath {
795-
loadInst.replace(with: rootVal, context)
812+
if loadInst.loadOwnership == .copy {
813+
let builder = Builder(before: loadInst, context)
814+
let copy = builder.createCopyValue(operand: rootVal)
815+
loadInst.replace(with: copy, context)
816+
} else {
817+
loadInst.replace(with: rootVal, context)
818+
}
796819
changed = true
797820
continue
798821
}
@@ -802,7 +825,11 @@ private extension MovableInstructions {
802825
}
803826

804827
let builder = Builder(before: loadInst, context)
805-
let projection = rootVal.createProjection(path: projectionPath, builder: builder)
828+
let projection = if loadInst.loadOwnership == .copy {
829+
rootVal.createProjectionAndCopy(path: projectionPath, builder: builder)
830+
} else {
831+
rootVal.createProjection(path: projectionPath, builder: builder)
832+
}
806833
loadInst.replace(with: projection, context)
807834

808835
changed = true
@@ -831,6 +858,69 @@ private extension MovableInstructions {
831858

832859
return changed
833860
}
861+
862+
func memoryIsInitializedAtAllExits(of loop: Loop, accessPath: AccessPath, _ context: FunctionPassContext) -> Bool {
863+
864+
// Perform a simple dataflow analysis which checks if there is a path from a `load [take]`
865+
// (= the only kind of instruction which can de-initialize the memory) to a loop exit without
866+
// a `store` in between.
867+
868+
var stores = InstructionSet(context)
869+
defer { stores.deinitialize() }
870+
for case let store as StoreInst in loadsAndStores where store.storesTo(accessPath) {
871+
stores.insert(store)
872+
}
873+
874+
var exitInsts = InstructionSet(context)
875+
defer { exitInsts.deinitialize() }
876+
exitInsts.insert(contentsOf: loop.exitBlocks.lazy.map { $0.instructions.first! })
877+
878+
var worklist = InstructionWorklist(context)
879+
defer { worklist.deinitialize() }
880+
for case let load as LoadInst in loadsAndStores where load.loadOwnership == .take && load.loadsFrom(accessPath) {
881+
worklist.pushIfNotVisited(load)
882+
}
883+
884+
while let inst = worklist.pop() {
885+
if stores.contains(inst) {
886+
continue
887+
}
888+
if exitInsts.contains(inst) {
889+
return false
890+
}
891+
worklist.pushSuccessors(of: inst)
892+
}
893+
return true
894+
}
895+
}
896+
897+
private extension Type {
898+
func isProjectingEntireNonTrivialMembers(path: SmallProjectionPath, in function: Function) -> Bool {
899+
let (kind, index, subPath) = path.pop()
900+
switch kind {
901+
case .root:
902+
return true
903+
case .structField:
904+
guard let fields = getNominalFields(in: function) else {
905+
return false
906+
}
907+
for (fieldIdx, fieldType) in fields.enumerated() {
908+
if fieldIdx != index && !fieldType.isTrivial(in: function) {
909+
return false
910+
}
911+
}
912+
return fields[index].isProjectingEntireNonTrivialMembers(path: subPath, in: function)
913+
case .tupleField:
914+
for (elementIdx, elementType) in tupleElements.enumerated() {
915+
if elementIdx != index && !elementType.isTrivial(in: function) {
916+
return false
917+
}
918+
}
919+
return tupleElements[index].isProjectingEntireNonTrivialMembers(path: subPath, in: function)
920+
default:
921+
fatalError("path is not materializable")
922+
}
923+
}
834924
}
835925

836926
private extension Instruction {

SwiftCompilerSources/Sources/Optimizer/ModulePasses/MandatoryPerformanceOptimizations.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,9 @@ private func shouldInline(apply: FullApplySite, callee: Function, alreadyInlined
302302
return false
303303
}
304304

305-
if apply.parentFunction.isGlobalInitOnceFunction && callee.inlineStrategy == .always {
305+
if apply.parentFunction.isGlobalInitOnceFunction && (
306+
callee.inlineStrategy == .heuristicAlways ||
307+
callee.inlineStrategy == .always) {
306308
// Some arithmetic operations, like integer conversions, are not transparent but `inline(__always)`.
307309
// Force inlining them in global initializers so that it's possible to statically initialize the global.
308310
return true

SwiftCompilerSources/Sources/Optimizer/PassManager/ContextCommon.swift

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -96,23 +96,26 @@ extension MutatingContext {
9696
func inlineFunction(apply: FullApplySite, mandatoryInline: Bool) {
9797
// This is only a best-effort attempt to notify the new cloned instructions as changed.
9898
// TODO: get a list of cloned instructions from the `inlineFunction`
99-
let instAfterInling: Instruction?
99+
let instBeforeInlining = apply.previous
100+
let instAfterInlining: Instruction?
100101
switch apply {
101102
case is ApplyInst:
102-
instAfterInling = apply.next
103+
instAfterInlining = apply.next
103104
case let beginApply as BeginApplyInst:
104105
let next = beginApply.next!
105-
instAfterInling = (next is EndApplyInst ? nil : next)
106+
instAfterInlining = (next is EndApplyInst ? nil : next)
106107
case is TryApplyInst:
107-
instAfterInling = apply.parentBlock.next?.instructions.first
108+
instAfterInlining = apply.parentBlock.next?.instructions.first
108109
default:
109-
instAfterInling = nil
110+
instAfterInlining = nil
110111
}
111112

112113
bridgedPassContext.inlineFunction(apply.bridged, mandatoryInline)
113114

114-
if let instAfterInling = instAfterInling {
115-
notifyNewInstructions(from: apply, to: instAfterInling)
115+
if let instBeforeInlining = instBeforeInlining?.next,
116+
let instAfterInlining = instAfterInlining,
117+
!instAfterInlining.isDeleted {
118+
notifyNewInstructions(from: instBeforeInlining, to: instAfterInlining)
116119
}
117120
}
118121

SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,20 +125,32 @@ extension Value {
125125
return true
126126
}
127127

128+
/// Project out a sub-field of this value according to `path`.
129+
/// If this is an "owned" value the result is an "owned" value which forwards the original value.
130+
/// This only works if _all_ non-trivial fields are projected. Otherwise some non-trivial results of
131+
/// `destructure_struct` or `destructure_tuple` will be leaked.
128132
func createProjection(path: SmallProjectionPath, builder: Builder) -> Value {
129133
let (kind, index, subPath) = path.pop()
134+
let result: Value
130135
switch kind {
131136
case .root:
132137
return self
133138
case .structField:
134-
let structExtract = builder.createStructExtract(struct: self, fieldIndex: index)
135-
return structExtract.createProjection(path: subPath, builder: builder)
139+
if ownership == .owned {
140+
result = builder.createDestructureStruct(struct: self).results[index]
141+
} else {
142+
result = builder.createStructExtract(struct: self, fieldIndex: index)
143+
}
136144
case .tupleField:
137-
let tupleExtract = builder.createTupleExtract(tuple: self, elementIndex: index)
138-
return tupleExtract.createProjection(path: subPath, builder: builder)
145+
if ownership == .owned {
146+
result = builder.createDestructureTuple(tuple: self).results[index]
147+
} else {
148+
result = builder.createTupleExtract(tuple: self, elementIndex: index)
149+
}
139150
default:
140151
fatalError("path is not materializable")
141152
}
153+
return result.createProjection(path: subPath, builder: builder)
142154
}
143155

144156
func createAddressProjection(path: SmallProjectionPath, builder: Builder) -> Value {

SwiftCompilerSources/Sources/SIL/DataStructures/Worklist.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public typealias ValueWorklist = Worklist<ValueSet>
7777
public typealias OperandWorklist = Worklist<OperandSet>
7878

7979
extension InstructionWorklist {
80-
public mutating func pushPredecessors(of inst: Instruction, ignoring ignoreInst: Instruction) {
80+
public mutating func pushPredecessors(of inst: Instruction, ignoring ignoreInst: Instruction? = nil) {
8181
if let prev = inst.previous {
8282
if prev != ignoreInst {
8383
pushIfNotVisited(prev)
@@ -92,7 +92,7 @@ extension InstructionWorklist {
9292
}
9393
}
9494

95-
public mutating func pushSuccessors(of inst: Instruction, ignoring ignoreInst: Instruction) {
95+
public mutating func pushSuccessors(of inst: Instruction, ignoring ignoreInst: Instruction? = nil) {
9696
if let succ = inst.next {
9797
if succ != ignoreInst {
9898
pushIfNotVisited(succ)

0 commit comments

Comments
 (0)