@@ -83,39 +83,108 @@ func DevirtualizeAndInlineFunc(fn *ir.Func, profile *pgo.Profile) {
83
83
fmt .Printf ("%v: function %v considered 'big'; reducing max cost of inlinees\n " , ir .Line (fn ), fn )
84
84
}
85
85
86
- // Walk fn's body and apply devirtualization and inlining.
87
- var inlCalls []* ir.InlinedCallExpr
88
- var edit func (ir.Node ) ir.Node
89
- edit = func (n ir.Node ) ir.Node {
86
+ match := func (n ir.Node ) bool {
90
87
switch n := n .(type ) {
88
+ case * ir.CallExpr :
89
+ return true
91
90
case * ir.TailCallStmt :
92
91
n .Call .NoInline = true // can't inline yet
93
92
}
93
+ return false
94
+ }
95
+
96
+ edit := func (n ir.Node ) ir.Node {
97
+ call , ok := n .(* ir.CallExpr )
98
+ if ! ok { // previously inlined
99
+ return nil
100
+ }
101
+
102
+ devirtualize .StaticCall (call )
103
+ if inlCall := inline .TryInlineCall (fn , call , bigCaller , profile ); inlCall != nil {
104
+ return inlCall
105
+ }
106
+ return nil
107
+ }
108
+
109
+ fixpoint (fn , match , edit )
110
+ })
111
+ }
112
+
113
+ // fixpoint repeatedly edits a function until it stabilizes.
114
+ //
115
+ // First, fixpoint applies match to every node n within fn. Then it
116
+ // iteratively applies edit to each node satisfying match(n).
117
+ //
118
+ // If edit(n) returns nil, no change is made. Otherwise, the result
119
+ // replaces n in fn's body, and fixpoint iterates at least once more.
120
+ //
121
+ // After an iteration where all edit calls return nil, fixpoint
122
+ // returns.
123
+ func fixpoint (fn * ir.Func , match func (ir.Node ) bool , edit func (ir.Node ) ir.Node ) {
124
+ // Consider the expression "f(g())". We want to be able to replace
125
+ // "g()" in-place with its inlined representation. But if we first
126
+ // replace "f(...)" with its inlined representation, then "g()" will
127
+ // instead appear somewhere within this new AST.
128
+ //
129
+ // To mitigate this, each matched node n is wrapped in a ParenExpr,
130
+ // so we can reliably replace n in-place by assigning ParenExpr.X.
131
+ // It's safe to use ParenExpr here, because typecheck already
132
+ // removed them all.
133
+
134
+ var parens []* ir.ParenExpr
135
+ var mark func (ir.Node ) ir.Node
136
+ mark = func (n ir.Node ) ir.Node {
137
+ if _ , ok := n .(* ir.ParenExpr ); ok {
138
+ return n // already visited n.X before wrapping
139
+ }
94
140
95
- ir . EditChildren ( n , edit )
141
+ ok := match ( n )
96
142
97
- if call , ok := n .(* ir.CallExpr ); ok {
98
- devirtualize .StaticCall (call )
143
+ ir .EditChildren (n , mark )
99
144
100
- if inlCall := inline .TryInlineCall (fn , call , bigCaller , profile ); inlCall != nil {
101
- inlCalls = append (inlCalls , inlCall )
102
- n = inlCall
103
- }
145
+ if ok {
146
+ paren := ir .NewParenExpr (n .Pos (), n )
147
+ paren .SetType (n .Type ())
148
+ paren .SetTypecheck (n .Typecheck ())
149
+
150
+ parens = append (parens , paren )
151
+ n = paren
152
+ }
153
+
154
+ return n
155
+ }
156
+ ir .EditChildren (fn , mark )
157
+
158
+ // Edit until stable.
159
+ for {
160
+ done := true
161
+
162
+ for i := 0 ; i < len (parens ); i ++ { // can't use "range parens" here
163
+ paren := parens [i ]
164
+ if new := edit (paren .X ); new != nil {
165
+ // Update AST and recursively mark nodes.
166
+ paren .X = new
167
+ ir .EditChildren (new , mark ) // mark may append to parens
168
+ done = false
104
169
}
170
+ }
105
171
106
- return n
172
+ if done {
173
+ break
107
174
}
108
- ir .EditChildren (fn , edit )
109
-
110
- // If we inlined any calls, we want to recursively visit their
111
- // bodies for further devirtualization and inlining. However, we
112
- // need to wait until *after* the original function body has been
113
- // expanded, or else inlCallee can have false positives (e.g.,
114
- // #54632).
115
- for len (inlCalls ) > 0 {
116
- call := inlCalls [0 ]
117
- inlCalls = inlCalls [1 :]
118
- ir .EditChildren (call , edit )
175
+ }
176
+
177
+ // Finally, remove any parens we inserted.
178
+ if len (parens ) == 0 {
179
+ return // short circuit
180
+ }
181
+ var unparen func (ir.Node ) ir.Node
182
+ unparen = func (n ir.Node ) ir.Node {
183
+ if paren , ok := n .(* ir.ParenExpr ); ok {
184
+ n = paren .X
119
185
}
120
- })
186
+ ir .EditChildren (n , unparen )
187
+ return n
188
+ }
189
+ ir .EditChildren (fn , unparen )
121
190
}
0 commit comments