@@ -37,43 +37,109 @@ let computeSideEffects = FunctionPass(name: "compute-side-effects") {
37
37
return
38
38
}
39
39
40
- if function. effectAttribute != . none {
41
- // Don't try to infer side effects if there are defined effect attributes.
42
- return
43
- }
44
-
45
40
var collectedEffects = CollectedEffects ( function: function, context)
46
41
47
- // First step: collect effects from all instructions.
48
- //
42
+ // Collect effects from all instructions.
49
43
for block in function. blocks {
50
44
for inst in block. instructions {
51
45
collectedEffects. addInstructionEffects ( inst)
52
46
}
53
47
}
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.
58
51
for argument in function. arguments {
59
52
collectedEffects. addEffectsForEscapingArgument ( argument: argument)
60
53
collectedEffects. addEffectsForConsumingArgument ( argument: argument)
61
54
}
62
55
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
+
63
129
// Don't modify the effects if they didn't change. This avoids sending a change notification
64
130
// which can trigger unnecessary other invalidations.
65
131
if let existingEffects = function. effects. sideEffects,
66
- existingEffects. arguments == collectedEffects . argumentEffects,
67
- existingEffects. global == collectedEffects . globalEffects {
132
+ existingEffects. arguments == argumentEffects,
133
+ existingEffects. global == globalEffects {
68
134
return
69
135
}
70
136
71
137
// Finally replace the function's side effects.
72
138
function. modifyEffects ( context) { ( effects: inout FunctionEffects ) in
73
139
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)
77
143
}
78
144
}
79
145
0 commit comments