Skip to content

Commit dfde580

Browse files
committed
Effects: bail if effects are requested for not supported projection paths
Effects are only defined for operations which don't involve a load. In case the argument's path involves a load we need to return the global effects.
1 parent 6226d56 commit dfde580

File tree

3 files changed

+82
-18
lines changed

3 files changed

+82
-18
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ComputeSideEffects.swift

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ private struct CollectedEffects {
299299
if let calleePath = calleeEffect.destroy { addEffects(.destroy, to: argument, fromInitialPath: calleePath) }
300300
} else {
301301
let convention = callee.getArgumentConvention(for: calleeArgIdx)
302-
let wholeArgument = argument.at(SmallProjectionPath(.anything))
302+
let wholeArgument = argument.at(defaultPath(for: argument))
303303
let calleeEffects = callee.getSideEffects(forArgument: wholeArgument,
304304
atIndex: calleeArgIdx,
305305
withConvention: convention)
@@ -313,8 +313,12 @@ private struct CollectedEffects {
313313
///
314314
/// If the value comes from an argument (or mutliple arguments), then the effects are added
315315
/// 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+
316320
private mutating func addEffects(_ effects: SideEffects.GlobalEffects, to value: Value,
317-
fromInitialPath: SmallProjectionPath? = nil) {
321+
fromInitialPath: SmallProjectionPath) {
318322

319323
/// Collects the (non-address) roots of a value.
320324
struct GetRootsWalker : ValueUseDefWalker {
@@ -346,10 +350,7 @@ private struct CollectedEffects {
346350

347351
var findRoots = GetRootsWalker(context)
348352
if value.type.isAddress {
349-
// If there is no initial path provided, select all value fields.
350-
let path = fromInitialPath ?? SmallProjectionPath(.anyValueFields)
351-
352-
let accessPath = value.getAccessPath(fromInitialPath: path)
353+
let accessPath = value.getAccessPath(fromInitialPath: fromInitialPath)
353354
switch accessPath.base {
354355
case .stack:
355356
// We don't care about read and writes from/to stack locations (because they are
@@ -369,16 +370,7 @@ private struct CollectedEffects {
369370
}
370371
}
371372
} else {
372-
// Handle non-address `value`s which are projections from a direct arguments.
373-
let path: SmallProjectionPath
374-
if let fromInitialPath = fromInitialPath {
375-
path = fromInitialPath
376-
} else if value.type.isClass {
377-
path = SmallProjectionPath(.anyValueFields).push(.anyClassField)
378-
} else {
379-
path = SmallProjectionPath(.anyValueFields).push(.anyClassField).push(.anyValueFields)
380-
}
381-
_ = findRoots.walkUp(value: value, path: path)
373+
_ = findRoots.walkUp(value: value, path: fromInitialPath)
382374
}
383375
// Because of phi-arguments, a single (non-address) `value` can come from multiple arguments.
384376
while let (arg, path) = findRoots.roots.pop() {
@@ -391,6 +383,16 @@ private struct CollectedEffects {
391383
}
392384
}
393385

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+
394396
/// Checks if an argument escapes to some unknown user.
395397
private struct ArgumentEscapingWalker : ValueDefUseWalker, AddressDefUseWalker {
396398
var walkDownCache = WalkerCache<UnusedWalkingPath>()

SwiftCompilerSources/Sources/SIL/Effects.swift

Lines changed: 33 additions & 1 deletion
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

test/SILOptimizer/mem-behavior.sil

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class X {
1616
class Derived : X { }
1717

1818
class C {
19-
@_hasStorage @_hasInitialValue final let prop: Builtin.Int32 { get }
19+
@_hasStorage @_hasInitialValue final var prop: Builtin.Int32 { get }
2020
}
2121

2222
class Parent {
@@ -950,3 +950,33 @@ bb0 (%0 : $*C):
950950
return %3 : $()
951951
}
952952

953+
sil @do_store : $@convention(thin) (@guaranteed Parent) -> () {
954+
[%0: noescape **, read c0.v**]
955+
[global: write]
956+
}
957+
958+
// CHECK-LABEL: @test_double_class_indirection
959+
// CHECK: PAIR #4.
960+
// CHECK-NEXT: %8 = apply %7(%1) : $@convention(thin) (@guaranteed Parent) -> ()
961+
// CHECK-NEXT: %3 = ref_element_addr %1 : $Parent, #Parent.child
962+
// CHECK-NEXT: r=1,w=0
963+
// CHECK-NEXT: PAIR #5.
964+
// CHECK-NEXT: %8 = apply %7(%1) : $@convention(thin) (@guaranteed Parent) -> ()
965+
// CHECK-NEXT: %5 = ref_element_addr %2 : $C, #C.prop
966+
// CHECK-NEXT: r=1,w=1
967+
sil @test_double_class_indirection : $@convention(thin) (Builtin.Int32) -> () {
968+
bb0(%0 : $Builtin.Int32):
969+
%1 = alloc_ref $Parent
970+
%2 = alloc_ref $C
971+
%3 = ref_element_addr %1 : $Parent, #Parent.child
972+
store %2 to %3 : $*C
973+
%5 = ref_element_addr %2 : $C, #C.prop
974+
store %0 to %5 : $*Builtin.Int32
975+
976+
%f = function_ref @do_store : $@convention(thin) (@guaranteed Parent) -> ()
977+
%a = apply %f(%1) : $@convention(thin) (@guaranteed Parent) -> ()
978+
979+
%r = tuple ()
980+
return %r : $()
981+
}
982+

0 commit comments

Comments
 (0)