Skip to content

Commit 8426a30

Browse files
Merge pull request #5288 from swiftwasm/main
[pull] swiftwasm from main
2 parents c99ec4c + c1a50cf commit 8426a30

File tree

186 files changed

+4621
-1586
lines changed

Some content is hidden

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

186 files changed

+4621
-1586
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ComputeSideEffects.swift

Lines changed: 73 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -95,15 +95,10 @@ private struct CollectedEffects {
9595
addEffects(.copy, to: inst.operands[0].value, fromInitialPath: SmallProjectionPath(.anyValueFields))
9696

9797
case is DestroyValueInst, is ReleaseValueInst, is StrongReleaseInst:
98-
addDestroyEffects(of: inst.operands[0].value)
98+
addDestroyEffects(ofValue: inst.operands[0].value)
9999

100100
case let da as DestroyAddrInst:
101-
// A destroy_addr also involves a read from the address. It's equivalent to a `%x = load [take]` and `destroy_value %x`.
102-
addEffects(.read, to: da.operand)
103-
// Conceptually, it's also a write, because the stored value is not available anymore after the destroy
104-
addEffects(.write, to: da.operand)
105-
106-
addDestroyEffects(of: da.operand)
101+
addDestroyEffects(ofAddress: da.operand)
107102

108103
case let copy as CopyAddrInst:
109104
addEffects(.read, to: copy.source)
@@ -113,17 +108,13 @@ private struct CollectedEffects {
113108
addEffects(.copy, to: copy.source)
114109
}
115110
if !copy.isInitializationOfDest {
116-
// Like for destroy_addr, the destroy also involves a read.
117-
addEffects(.read, to: copy.destination)
118-
addDestroyEffects(of: copy.destination)
111+
addDestroyEffects(ofAddress: copy.destination)
119112
}
120113

121114
case let store as StoreInst:
122115
addEffects(.write, to: store.destination)
123116
if store.destinationOwnership == .assign {
124-
// Like for destroy_addr, the destroy also involves a read.
125-
addEffects(.read, to: store.destination)
126-
addDestroyEffects(of: store.destination)
117+
addDestroyEffects(ofAddress: store.destination)
127118
}
128119

129120
case let store as StoreWeakInst:
@@ -137,9 +128,9 @@ private struct CollectedEffects {
137128
addEffects(.read, to: addr)
138129

139130
case let apply as FullApplySite:
140-
let calleeValue = apply.callee
141131
if apply.callee.type.isCalleeConsumedFunction {
142-
addDestroyEffects(of: calleeValue)
132+
addEffects(.destroy, to: apply.callee)
133+
globalEffects = .worstEffects
143134
}
144135
handleApply(apply)
145136
checkedIfDeinitBarrier = true
@@ -230,11 +221,58 @@ private struct CollectedEffects {
230221

231222
private mutating func handleApply(_ apply: ApplySite) {
232223
let callees = calleeAnalysis.getCallees(callee: apply.callee)
224+
let args = apply.arguments.enumerated().lazy.map {
225+
(calleeArgumentIndex: apply.calleeArgIndex(callerArgIndex: $0.0),
226+
callerArgument: $0.1)
227+
}
228+
addEffects(ofFunctions: callees, withArguments: args)
229+
}
230+
231+
private mutating func addDestroyEffects(ofValue value: Value) {
232+
// First thing: add the destroy effect itself.
233+
addEffects(.destroy, to: value)
234+
235+
if value.type.isClass {
236+
// Treat destroying a class value just like a call to it's destructor(s).
237+
let destructors = calleeAnalysis.getDestructors(of: value.type)
238+
let theSelfArgument = CollectionOfOne((calleeArgumentIndex: 0, callerArgument: value))
239+
addEffects(ofFunctions: destructors, withArguments: theSelfArgument)
240+
} else {
241+
// TODO: dig into the type and check for destructors of individual class fields
242+
addEffects(.worstEffects, to: value)
243+
globalEffects = .worstEffects
244+
}
245+
}
246+
247+
private mutating func addDestroyEffects(ofAddress address: Value) {
248+
// First thing: add the destroy effect itself.
249+
addEffects(.destroy, to: address)
250+
251+
// A destroy also involves a read from the address.
252+
// E.g. a `destroy_addr` is equivalent to a `%x = load [take]` and `destroy_value %x`.
253+
addEffects(.read, to: address)
254+
// Conceptually, it's also a write, because the stored value is not available anymore after the destroy
255+
addEffects(.write, to: address)
256+
257+
// Second: add all effects of (potential) destructors which might be called if the destroy deallocates an object.
258+
// Note that we don't need to add any effects specific to the `address`, because the memory location is not
259+
// affected by a destructor of the stored value (and effects don't include anything which is loaded from memory).
260+
if let destructors = calleeAnalysis.getDestructors(of: address.type) {
261+
for destructor in destructors {
262+
globalEffects.merge(with: destructor.getSideEffects())
263+
}
264+
} else {
265+
globalEffects = .worstEffects
266+
}
267+
}
233268

269+
private mutating func addEffects<Arguments: Sequence>(ofFunctions callees: FunctionArray?,
270+
withArguments arguments: Arguments)
271+
where Arguments.Element == (calleeArgumentIndex: Int, callerArgument: Value) {
234272
guard let callees = callees else {
235273
// We don't know which function(s) are called.
236274
globalEffects = .worstEffects
237-
for argument in apply.arguments {
275+
for (_, argument) in arguments {
238276
addEffects(.worstEffects, to: argument)
239277
}
240278
return
@@ -249,8 +287,7 @@ private struct CollectedEffects {
249287
}
250288
}
251289

252-
for (argumentIdx, argument) in apply.arguments.enumerated() {
253-
let calleeArgIdx = apply.calleeArgIndex(callerArgIndex: argumentIdx)
290+
for (calleeArgIdx, argument) in arguments {
254291
for callee in callees {
255292
if let sideEffects = callee.effects.sideEffects {
256293
let calleeEffect = sideEffects.getArgumentEffects(for: calleeArgIdx)
@@ -262,7 +299,7 @@ private struct CollectedEffects {
262299
if let calleePath = calleeEffect.destroy { addEffects(.destroy, to: argument, fromInitialPath: calleePath) }
263300
} else {
264301
let convention = callee.getArgumentConvention(for: calleeArgIdx)
265-
let wholeArgument = argument.at(SmallProjectionPath(.anything))
302+
let wholeArgument = argument.at(defaultPath(for: argument))
266303
let calleeEffects = callee.getSideEffects(forArgument: wholeArgument,
267304
atIndex: calleeArgIdx,
268305
withConvention: convention)
@@ -272,27 +309,16 @@ private struct CollectedEffects {
272309
}
273310
}
274311

275-
private mutating func addDestroyEffects(of addressOrValue: Value) {
276-
// First thing: add the destroy effect itself.
277-
addEffects(.destroy, to: addressOrValue)
278-
279-
// Second: add all effects of (potential) destructors which might be called if the destroy
280-
// deallocates an object.
281-
if let destructors = calleeAnalysis.getDestructors(of: addressOrValue.type) {
282-
for destructor in destructors {
283-
globalEffects.merge(with: destructor.getSideEffects())
284-
}
285-
} else {
286-
globalEffects = .worstEffects
287-
}
288-
}
289-
290312
/// Adds effects to a specific value.
291313
///
292314
/// If the value comes from an argument (or mutliple arguments), then the effects are added
293315
/// to the corrseponding `argumentEffects`. Otherwise they are added to the `global` effects.
316+
private mutating func addEffects(_ effects: SideEffects.GlobalEffects, to value: Value) {
317+
addEffects(effects, to: value, fromInitialPath: defaultPath(for: value))
318+
}
319+
294320
private mutating func addEffects(_ effects: SideEffects.GlobalEffects, to value: Value,
295-
fromInitialPath: SmallProjectionPath? = nil) {
321+
fromInitialPath: SmallProjectionPath) {
296322

297323
/// Collects the (non-address) roots of a value.
298324
struct GetRootsWalker : ValueUseDefWalker {
@@ -324,10 +350,7 @@ private struct CollectedEffects {
324350

325351
var findRoots = GetRootsWalker(context)
326352
if value.type.isAddress {
327-
// If there is no initial path provided, select all value fields.
328-
let path = fromInitialPath ?? SmallProjectionPath(.anyValueFields)
329-
330-
let accessPath = value.getAccessPath(fromInitialPath: path)
353+
let accessPath = value.getAccessPath(fromInitialPath: fromInitialPath)
331354
switch accessPath.base {
332355
case .stack:
333356
// We don't care about read and writes from/to stack locations (because they are
@@ -347,16 +370,7 @@ private struct CollectedEffects {
347370
}
348371
}
349372
} else {
350-
// Handle non-address `value`s which are projections from a direct arguments.
351-
let path: SmallProjectionPath
352-
if let fromInitialPath = fromInitialPath {
353-
path = fromInitialPath
354-
} else if value.type.isClass {
355-
path = SmallProjectionPath(.anyValueFields).push(.anyClassField)
356-
} else {
357-
path = SmallProjectionPath(.anyValueFields).push(.anyClassField).push(.anyValueFields)
358-
}
359-
_ = findRoots.walkUp(value: value, path: path)
373+
_ = findRoots.walkUp(value: value, path: fromInitialPath)
360374
}
361375
// Because of phi-arguments, a single (non-address) `value` can come from multiple arguments.
362376
while let (arg, path) = findRoots.roots.pop() {
@@ -369,6 +383,16 @@ private struct CollectedEffects {
369383
}
370384
}
371385

386+
private func defaultPath(for value: Value) -> SmallProjectionPath {
387+
if value.type.isAddress {
388+
return SmallProjectionPath(.anyValueFields)
389+
}
390+
if value.type.isClass {
391+
return SmallProjectionPath(.anyValueFields).push(.anyClassField)
392+
}
393+
return SmallProjectionPath(.anyValueFields).push(.anyClassField).push(.anyValueFields)
394+
}
395+
372396
/// Checks if an argument escapes to some unknown user.
373397
private struct ArgumentEscapingWalker : ValueDefUseWalker, AddressDefUseWalker {
374398
var walkDownCache = WalkerCache<UnusedWalkingPath>()

SwiftCompilerSources/Sources/Optimizer/Utilities/EscapeUtils.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ fileprivate struct EscapeWalker<V: EscapeVisitor> : ValueDefUseWalker,
382382

383383
// Class references, which are directly located in the array elements cannot escape,
384384
// because those are passed as `self` to their deinits - and `self` cannot escape in a deinit.
385-
if path.projectionPath.hasNoClassProjection {
385+
if !path.projectionPath.mayHaveClassProjection {
386386
return .continueWalk
387387
}
388388
return isEscaping
@@ -818,7 +818,7 @@ fileprivate struct EscapeWalker<V: EscapeVisitor> : ValueDefUseWalker,
818818
private func followLoads(at path: SmallProjectionPath) -> Bool {
819819
return visitor.followLoads ||
820820
// When part of a class field we have to follow loads.
821-
!path.hasNoClassProjection
821+
path.mayHaveClassProjection
822822
}
823823

824824
private func pathForArgumentEscapeChecking(_ path: SmallProjectionPath) -> SmallProjectionPath {

SwiftCompilerSources/Sources/SIL/Effects.swift

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,41 @@ extension Function {
8888
public func getSideEffects(forArgument argument: ProjectedValue,
8989
atIndex argumentIndex: Int,
9090
withConvention convention: ArgumentConvention) -> SideEffects.GlobalEffects {
91+
var result = SideEffects.GlobalEffects()
92+
93+
// Effects are only defined for operations which don't involve a load.
94+
// In case the argument's path involves a load we need to return the global effects.
95+
if argument.value.type.isAddress {
96+
// Indirect arguments:
97+
if argument.path.mayHaveClassProjection {
98+
// For example:
99+
// bb0(%0: $*C):
100+
// %1 = load %0 // the involved load
101+
// %2 = ref_element_addr %1 // class projection
102+
return getSideEffects()
103+
}
104+
} else {
105+
// Direct arguments:
106+
if argument.path.mayHaveTwoClassProjections {
107+
// For example:
108+
// bb0(%0: $C):
109+
// %1 = ref_element_addr %1 // first class projection
110+
// %2 = load %1 // the involved load
111+
// %3 = ref_element_addr %2 // second class projection
112+
return getSideEffects()
113+
114+
} else if argument.path.mayHaveClassProjection {
115+
// For example:
116+
// bb0(%0: $C):
117+
// %1 = ref_element_addr %1 // class projection
118+
// %2 = load [take] %1 // the involved load
119+
// destroy_value %2
120+
result.ownership = getSideEffects().ownership
121+
}
122+
}
123+
91124
if let sideEffects = effects.sideEffects {
92125
/// There are computed side effects.
93-
var result = SideEffects.GlobalEffects()
94126
let argEffect = sideEffects.getArgumentEffects(for: argumentIndex)
95127
if let effectPath = argEffect.read, effectPath.mayOverlap(with: argument.path) {
96128
result.memory.read = true
@@ -499,7 +531,7 @@ public struct SideEffects : CustomStringConvertible, NoReflectionChildren {
499531
case .directGuaranteed:
500532
// Note that `directGuaranteed` still has a "destroy" effect, because an object stored in
501533
// a class property could be destroyed.
502-
if argument.path.hasNoClassProjection {
534+
if !argument.path.mayHaveClassProjection {
503535
result.ownership.destroy = false
504536
}
505537
fallthrough

SwiftCompilerSources/Sources/SIL/SmallProjectionPath.swift

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -244,15 +244,6 @@ public struct SmallProjectionPath : Hashable, CustomStringConvertible, NoReflect
244244
}
245245
}
246246

247-
/// Returns true if the path does not have any class projections.
248-
/// For example:
249-
/// returns true for `v**`
250-
/// returns false for `c0`
251-
/// returns false for `**` (because '**' can have any number of class projections)
252-
public var hasNoClassProjection: Bool {
253-
return matches(pattern: Self(.anyValueFields))
254-
}
255-
256247
/// Returns true if the path has at least one class projection.
257248
/// For example:
258249
/// returns false for `v**`
@@ -268,6 +259,27 @@ public struct SmallProjectionPath : Hashable, CustomStringConvertible, NoReflect
268259
}
269260
}
270261

262+
/// Returns true if the path may have a class projection.
263+
/// For example:
264+
/// returns false for `v**`
265+
/// returns true for `c0`
266+
/// returns true for `**` (because '**' can have any number of class projections)
267+
public var mayHaveClassProjection: Bool {
268+
return !matches(pattern: Self(.anyValueFields))
269+
}
270+
271+
/// Returns true if the path may have a class projection.
272+
/// For example:
273+
/// returns false for `v**`
274+
/// returns false for `c0`
275+
/// returns true for `c0.c1`
276+
/// returns true for `c0.**` (because '**' can have any number of class projections)
277+
/// returns true for `**` (because '**' can have any number of class projections)
278+
public var mayHaveTwoClassProjections: Bool {
279+
return !matches(pattern: Self(.anyValueFields)) &&
280+
!matches(pattern: Self(.anyValueFields).push(.anyClassField).push(.anyValueFields))
281+
}
282+
271283
/// Pops all value field components from the beginning of the path.
272284
/// For example:
273285
/// `s0.e2.3.c4.s1` -> `c4.s1`
@@ -679,17 +691,24 @@ extension SmallProjectionPath {
679691
}
680692

681693
func predicates() {
682-
testPredicate("v**", \.hasNoClassProjection, expect: true)
683-
testPredicate("c0", \.hasNoClassProjection, expect: false)
684-
testPredicate("1", \.hasNoClassProjection, expect: true)
685-
testPredicate("**", \.hasNoClassProjection, expect: false)
686-
687694
testPredicate("v**", \.hasClassProjection, expect: false)
688695
testPredicate("v**.c0.s1.v**", \.hasClassProjection, expect: true)
689696
testPredicate("c0.**", \.hasClassProjection, expect: true)
690697
testPredicate("c0.c1", \.hasClassProjection, expect: true)
691698
testPredicate("ct", \.hasClassProjection, expect: true)
692699
testPredicate("s0", \.hasClassProjection, expect: false)
700+
701+
testPredicate("v**", \.mayHaveClassProjection, expect: false)
702+
testPredicate("c0", \.mayHaveClassProjection, expect: true)
703+
testPredicate("1", \.mayHaveClassProjection, expect: false)
704+
testPredicate("**", \.mayHaveClassProjection, expect: true)
705+
706+
testPredicate("v**", \.mayHaveTwoClassProjections, expect: false)
707+
testPredicate("c0", \.mayHaveTwoClassProjections, expect: false)
708+
testPredicate("**", \.mayHaveTwoClassProjections, expect: true)
709+
testPredicate("v**.c*.s2.1.c0", \.mayHaveTwoClassProjections, expect: true)
710+
testPredicate("c*.s2.1.c0.v**", \.mayHaveTwoClassProjections, expect: true)
711+
testPredicate("v**.c*.**", \.mayHaveTwoClassProjections, expect: true)
693712
}
694713

695714
func testPredicate(_ pathStr: String, _ property: (SmallProjectionPath) -> Bool, expect: Bool) {

docs/HowToGuides/GettingStarted.md

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -386,15 +386,16 @@ several more steps are necessary to set up this environment:
386386
387387
* Create an empty Xcode project in the workspace, using the
388388
_External Build System_ template.
389-
* For a Ninja target that you want to build (e.g. `swift-frontend`), add a
390-
target to the empty project, using the _External Build System_ template.
389+
* Add a target to the empty project, using the _External Build System_ template,
390+
and name it after the Ninja target that you want to build (e.g. `swift-frontend`
391+
is the compiler).
391392
* In the _Info_ pane of the target settings, set
392393
* _Build Tool_ to the absolute path of the `ninja` executable (the output of
393394
`which ninja` on the command line)
394-
* _Arguments_ to the Ninja target name (e.g. `swift-frontend`)
395-
* _Directory_ to the absolute path of the directory where the Ninja target
396-
lives. For Swift targets (the compiler, standard library, runtime, and
397-
related tooling), this is the `build/Ninja-*/swift-macosx-*` directory.
395+
* _Arguments_ to the Ninja target (e.g. `bin/swift-frontend`)
396+
* _Directory_ to the absolute path of the build directory where the Ninja
397+
target lives. For Swift targets such as the compiler or standard library,
398+
this is the `build/Ninja-*/swift-macosx-*` directory.
398399
* Add a scheme for the target. In the drop-down menu, be careful not to mistake
399400
your target for a similar one that belongs to a generated Xcode project.
400401
* > **Note**
@@ -406,7 +407,7 @@ several more steps are necessary to set up this environment:
406407
the appropriate `bin` directory (e.g. `build/Ninja-*/swift-macosx-*/bin/swift-frontend`).
407408
* In the _Arguments_ pane, add the command line arguments that you want to
408409
pass to the executable on launch (e.g. `path/to/file.swift -typecheck` for
409-
`swift-frontend`).
410+
`bin/swift-frontend`).
410411
* You can optionally set the working directory for debugging in the
411412
_Options_ pane.
412413
* Configure as many more target-scheme pairs as you need.

0 commit comments

Comments
 (0)