@@ -17,6 +17,7 @@ import {
17
17
InstructionKind ,
18
18
LabelTerminal ,
19
19
Place ,
20
+ isStatementBlockKind ,
20
21
makeInstructionId ,
21
22
promoteTemporary ,
22
23
reversePostorderBlocks ,
@@ -90,100 +91,106 @@ export function inlineImmediatelyInvokedFunctionExpressions(
90
91
*/
91
92
const queue = Array . from ( fn . body . blocks . values ( ) ) ;
92
93
queue: for ( const block of queue ) {
93
- for ( let ii = 0 ; ii < block . instructions . length ; ii ++ ) {
94
- const instr = block . instructions [ ii ] ! ;
95
- switch ( instr . value . kind ) {
96
- case 'FunctionExpression' : {
97
- if ( instr . lvalue . identifier . name === null ) {
98
- functions . set ( instr . lvalue . identifier . id , instr . value ) ;
99
- }
100
- break ;
101
- }
102
- case 'CallExpression' : {
103
- if ( instr . value . args . length !== 0 ) {
104
- // We don't support inlining when there are arguments
105
- continue ;
106
- }
107
- const body = functions . get ( instr . value . callee . identifier . id ) ;
108
- if ( body === undefined ) {
109
- // Not invoking a local function expression, can't inline
110
- continue ;
94
+ /*
95
+ * We can't handle labels inside expressions yet, so we don't inline IIFEs if they are in an
96
+ * expression block.
97
+ */
98
+ if ( isStatementBlockKind ( block . kind ) ) {
99
+ for ( let ii = 0 ; ii < block . instructions . length ; ii ++ ) {
100
+ const instr = block . instructions [ ii ] ! ;
101
+ switch ( instr . value . kind ) {
102
+ case 'FunctionExpression' : {
103
+ if ( instr . lvalue . identifier . name === null ) {
104
+ functions . set ( instr . lvalue . identifier . id , instr . value ) ;
105
+ }
106
+ break ;
111
107
}
108
+ case 'CallExpression' : {
109
+ if ( instr . value . args . length !== 0 ) {
110
+ // We don't support inlining when there are arguments
111
+ continue ;
112
+ }
113
+ const body = functions . get ( instr . value . callee . identifier . id ) ;
114
+ if ( body === undefined ) {
115
+ // Not invoking a local function expression, can't inline
116
+ continue ;
117
+ }
112
118
113
- if (
114
- body . loweredFunc . func . params . length > 0 ||
115
- body . loweredFunc . func . async ||
116
- body . loweredFunc . func . generator
117
- ) {
118
- // Can't inline functions with params, or async/generator functions
119
- continue ;
120
- }
119
+ if (
120
+ body . loweredFunc . func . params . length > 0 ||
121
+ body . loweredFunc . func . async ||
122
+ body . loweredFunc . func . generator
123
+ ) {
124
+ // Can't inline functions with params, or async/generator functions
125
+ continue ;
126
+ }
121
127
122
- // We know this function is used for an IIFE and can prune it later
123
- inlinedFunctions . add ( instr . value . callee . identifier . id ) ;
128
+ // We know this function is used for an IIFE and can prune it later
129
+ inlinedFunctions . add ( instr . value . callee . identifier . id ) ;
124
130
125
- // Create a new block which will contain code following the IIFE call
126
- const continuationBlockId = fn . env . nextBlockId ;
127
- const continuationBlock : BasicBlock = {
128
- id : continuationBlockId ,
129
- instructions : block . instructions . slice ( ii + 1 ) ,
130
- kind : block . kind ,
131
- phis : new Set ( ) ,
132
- preds : new Set ( ) ,
133
- terminal : block . terminal ,
134
- } ;
135
- fn . body . blocks . set ( continuationBlockId , continuationBlock ) ;
131
+ // Create a new block which will contain code following the IIFE call
132
+ const continuationBlockId = fn . env . nextBlockId ;
133
+ const continuationBlock : BasicBlock = {
134
+ id : continuationBlockId ,
135
+ instructions : block . instructions . slice ( ii + 1 ) ,
136
+ kind : block . kind ,
137
+ phis : new Set ( ) ,
138
+ preds : new Set ( ) ,
139
+ terminal : block . terminal ,
140
+ } ;
141
+ fn . body . blocks . set ( continuationBlockId , continuationBlock ) ;
136
142
137
- /*
138
- * Trim the original block to contain instructions up to (but not including)
139
- * the IIFE
140
- */
141
- block . instructions . length = ii ;
143
+ /*
144
+ * Trim the original block to contain instructions up to (but not including)
145
+ * the IIFE
146
+ */
147
+ block . instructions . length = ii ;
142
148
143
- /*
144
- * To account for complex control flow within the lambda, we treat the lambda
145
- * as if it were a single labeled statement, and replace all returns with gotos
146
- * to the label fallthrough.
147
- */
148
- const newTerminal : LabelTerminal = {
149
- block : body . loweredFunc . func . body . entry ,
150
- id : makeInstructionId ( 0 ) ,
151
- kind : 'label' ,
152
- fallthrough : continuationBlockId ,
153
- loc : block . terminal . loc ,
154
- } ;
155
- block . terminal = newTerminal ;
149
+ /*
150
+ * To account for complex control flow within the lambda, we treat the lambda
151
+ * as if it were a single labeled statement, and replace all returns with gotos
152
+ * to the label fallthrough.
153
+ */
154
+ const newTerminal : LabelTerminal = {
155
+ block : body . loweredFunc . func . body . entry ,
156
+ id : makeInstructionId ( 0 ) ,
157
+ kind : 'label' ,
158
+ fallthrough : continuationBlockId ,
159
+ loc : block . terminal . loc ,
160
+ } ;
161
+ block . terminal = newTerminal ;
156
162
157
- // We store the result in the IIFE temporary
158
- const result = instr . lvalue ;
163
+ // We store the result in the IIFE temporary
164
+ const result = instr . lvalue ;
159
165
160
- // Declare the IIFE temporary
161
- declareTemporary ( fn . env , block , result ) ;
166
+ // Declare the IIFE temporary
167
+ declareTemporary ( fn . env , block , result ) ;
162
168
163
- // Promote the temporary with a name as we require this to persist
164
- promoteTemporary ( result . identifier ) ;
169
+ // Promote the temporary with a name as we require this to persist
170
+ promoteTemporary ( result . identifier ) ;
165
171
166
- /*
167
- * Rewrite blocks from the lambda to replace any `return` with a
168
- * store to the result and `goto` the continuation block
169
- */
170
- for ( const [ id , block ] of body . loweredFunc . func . body . blocks ) {
171
- block . preds . clear ( ) ;
172
- rewriteBlock ( fn . env , block , continuationBlockId , result ) ;
173
- fn . body . blocks . set ( id , block ) ;
174
- }
172
+ /*
173
+ * Rewrite blocks from the lambda to replace any `return` with a
174
+ * store to the result and `goto` the continuation block
175
+ */
176
+ for ( const [ id , block ] of body . loweredFunc . func . body . blocks ) {
177
+ block . preds . clear ( ) ;
178
+ rewriteBlock ( fn . env , block , continuationBlockId , result ) ;
179
+ fn . body . blocks . set ( id , block ) ;
180
+ }
175
181
176
- /*
177
- * Ensure we visit the continuation block, since there may have been
178
- * sequential IIFEs that need to be visited.
179
- */
180
- queue . push ( continuationBlock ) ;
181
- continue queue;
182
- }
183
- default : {
184
- for ( const place of eachInstructionValueOperand ( instr . value ) ) {
185
- // Any other use of a function expression means it isn't an IIFE
186
- functions . delete ( place . identifier . id ) ;
182
+ /*
183
+ * Ensure we visit the continuation block, since there may have been
184
+ * sequential IIFEs that need to be visited.
185
+ */
186
+ queue . push ( continuationBlock ) ;
187
+ continue queue;
188
+ }
189
+ default : {
190
+ for ( const place of eachInstructionValueOperand ( instr . value ) ) {
191
+ // Any other use of a function expression means it isn't an IIFE
192
+ functions . delete ( place . identifier . id ) ;
193
+ }
187
194
}
188
195
}
189
196
}
0 commit comments