@@ -37,43 +37,109 @@ let computeSideEffects = FunctionPass(name: "compute-side-effects") {
3737 return
3838 }
3939
40- if function. effectAttribute != . none {
41- // Don't try to infer side effects if there are defined effect attributes.
42- return
43- }
44-
4540 var collectedEffects = CollectedEffects ( function: function, context)
4641
47- // First step: collect effects from all instructions.
48- //
42+ // Collect effects from all instructions.
4943 for block in function. blocks {
5044 for inst in block. instructions {
5145 collectedEffects. addInstructionEffects ( inst)
5246 }
5347 }
54-
55- // Second step: If an argument has unknown uses, we must add all previously collected
56- // global effects to the argument, because we don't know to which "global" side-effect
57- // instruction the argument might have escaped.
48+ // If an argument has unknown uses, we must add all previously collected
49+ // global effects to the argument, because we don't know to which "global"
50+ // side-effect instruction the argument might have escaped.
5851 for argument in function. arguments {
5952 collectedEffects. addEffectsForEscapingArgument ( argument: argument)
6053 collectedEffects. addEffectsForConsumingArgument ( argument: argument)
6154 }
6255
56+ let globalEffects : SideEffects . GlobalEffects
57+ do {
58+ let computed = collectedEffects. globalEffects
59+
60+ // Combine computed global effects with effects defined by the function's effect attribute, if it has one.
61+
62+ // The defined and computed global effects of a function with an effect attribute should be treated as
63+ // worst case global effects of the function.
64+ // This means a global effect should only occur iff it is computed AND defined to occur.
65+
66+ let defined = function. definedGlobalEffects
67+
68+ globalEffects = SideEffects . GlobalEffects (
69+ memory: SideEffects . Memory ( read: defined. memory. read && computed. memory. read,
70+ write: defined. memory. write && computed. memory. write) ,
71+ ownership: SideEffects . Ownership ( copy: defined. ownership. copy && computed. ownership. copy,
72+ destroy: defined. ownership. destroy && computed. ownership. destroy) ,
73+ allocates: defined. allocates && computed. allocates,
74+ isDeinitBarrier: defined. isDeinitBarrier && computed. isDeinitBarrier
75+ )
76+ }
77+
78+ // Obtain the argument effects of the function.
79+ var argumentEffects = collectedEffects. argumentEffects
80+
81+ // `[readnone]` and `[readonly]` functions can still access the value fields
82+ // of their indirect arguments, permitting v** read and write effects. If
83+ // additional read or write effects are computed, we can replace.
84+ switch function. effectAttribute {
85+ case . readNone:
86+ for i in ( 0 ..< argumentEffects. count) {
87+ // Even a `[readnone]` function can read from indirect arguments.
88+ if !function. argumentConventions [ i] . isIndirectIn {
89+ argumentEffects [ i] . read = nil
90+ } else if argumentEffects [ i] . read? . mayHaveClassProjection ?? false {
91+ argumentEffects [ i] . read = SmallProjectionPath ( . anyValueFields)
92+ }
93+
94+ // Even a `[readnone]` function can write to indirect results.
95+ if !function. argument ( at: i) . isIndirectResult {
96+ argumentEffects [ i] . write = nil
97+ } else if argumentEffects [ i] . write? . mayHaveClassProjection ?? false {
98+ argumentEffects [ i] . write = SmallProjectionPath ( . anyValueFields)
99+ }
100+
101+ argumentEffects [ i] . copy = nil
102+ argumentEffects [ i] . destroy = nil
103+ }
104+
105+ case . readOnly:
106+ for i in ( 0 ..< argumentEffects. count) {
107+ // Even a `[readonly]` function can write to indirect results.
108+ if !function. argument ( at: i) . isIndirectResult {
109+ argumentEffects [ i] . write = nil
110+ } else if argumentEffects [ i] . write? . mayHaveClassProjection ?? false {
111+ argumentEffects [ i] . write = SmallProjectionPath ( . anyValueFields)
112+ }
113+
114+ argumentEffects [ i] . destroy = nil
115+ }
116+
117+ case . releaseNone:
118+ for i in ( 0 ..< argumentEffects. count) {
119+ // A `[releasenone]` function can do anything except destroy an argument.
120+ argumentEffects [ i] . destroy = nil
121+ }
122+
123+ case . none:
124+ // The user makes no additional guarantees about the effects of the function.
125+ break
126+ }
127+
128+
63129 // Don't modify the effects if they didn't change. This avoids sending a change notification
64130 // which can trigger unnecessary other invalidations.
65131 if let existingEffects = function. effects. sideEffects,
66- existingEffects. arguments == collectedEffects . argumentEffects,
67- existingEffects. global == collectedEffects . globalEffects {
132+ existingEffects. arguments == argumentEffects,
133+ existingEffects. global == globalEffects {
68134 return
69135 }
70136
71137 // Finally replace the function's side effects.
72138 function. modifyEffects ( context) { ( effects: inout FunctionEffects ) in
73139 let globalEffects = function. isProgramTerminationPoint ?
74- collectedEffects . globalEffects. forProgramTerminationPoints
75- : collectedEffects . globalEffects
76- effects. sideEffects = SideEffects ( arguments: collectedEffects . argumentEffects, global: globalEffects)
140+ globalEffects. forProgramTerminationPoints
141+ : globalEffects
142+ effects. sideEffects = SideEffects ( arguments: argumentEffects, global: globalEffects)
77143 }
78144}
79145
0 commit comments