@@ -13,14 +13,20 @@ import {
13
13
HIRFunction ,
14
14
IdentifierId ,
15
15
Instruction ,
16
+ InstructionId ,
17
+ MutableRange ,
18
+ Place ,
16
19
isExpressionBlockKind ,
20
+ makeInstructionId ,
17
21
markInstructionIds ,
18
22
} from "../HIR" ;
19
23
import { printInstruction } from "../HIR/PrintHIR" ;
20
24
import {
25
+ eachInstructionLValue ,
21
26
eachInstructionValueLValue ,
22
27
eachInstructionValueOperand ,
23
28
eachTerminalOperand ,
29
+ terminalFallthrough ,
24
30
} from "../HIR/visitors" ;
25
31
import { mayAllocate } from "../ReactiveScopes/InferReactiveScopeVariables" ;
26
32
import { getOrInsertWith } from "../Utils/utils" ;
@@ -69,8 +75,9 @@ import { getOrInsertWith } from "../Utils/utils";
69
75
export function instructionReordering ( fn : HIRFunction ) : void {
70
76
// Shared nodes are emitted when they are first used
71
77
const shared : Nodes = new Map ( ) ;
78
+ const references = findReferencedRangeOfTemporaries ( fn ) ;
72
79
for ( const [ , block ] of fn . body . blocks ) {
73
- reorderBlock ( fn . env , block , shared ) ;
80
+ reorderBlock ( fn . env , block , shared , references ) ;
74
81
}
75
82
CompilerError . invariant ( shared . size === 0 , {
76
83
reason : `InstructionReordering: expected all reorderable nodes to have been emitted` ,
@@ -91,10 +98,76 @@ type Node = {
91
98
depth : number | null ;
92
99
} ;
93
100
101
+ // Inclusive start and end
102
+ type References = {
103
+ accessedRanges : AccessedRanges ;
104
+ lastAssignments : LastAssignments ;
105
+ } ;
106
+ type LastAssignments = Map < string , InstructionId > ;
107
+ type AccessedRanges = Map < IdentifierId , Range > ;
108
+ type Range = { start : InstructionId ; end : InstructionId } ;
109
+ enum ReferenceKind {
110
+ Read ,
111
+ Write ,
112
+ }
113
+ function findReferencedRangeOfTemporaries ( fn : HIRFunction ) : References {
114
+ const accessedRanges : AccessedRanges = new Map ( ) ;
115
+ const lastAssignments : LastAssignments = new Map ( ) ;
116
+ function reference (
117
+ instr : InstructionId ,
118
+ place : Place ,
119
+ kind : ReferenceKind
120
+ ) : void {
121
+ if (
122
+ place . identifier . name !== null &&
123
+ place . identifier . name . kind === "named"
124
+ ) {
125
+ if ( kind === ReferenceKind . Write ) {
126
+ const name = place . identifier . name . value ;
127
+ const previous = lastAssignments . get ( name ) ;
128
+ if ( previous === undefined ) {
129
+ lastAssignments . set ( name , instr ) ;
130
+ } else {
131
+ lastAssignments . set (
132
+ name ,
133
+ makeInstructionId ( Math . max ( previous , instr ) )
134
+ ) ;
135
+ }
136
+ }
137
+ return ;
138
+ } else if ( kind === ReferenceKind . Read ) {
139
+ const range = getOrInsertWith (
140
+ accessedRanges ,
141
+ place . identifier . id ,
142
+ ( ) => ( {
143
+ start : instr ,
144
+ end : instr ,
145
+ } )
146
+ ) ;
147
+ range . end = instr ;
148
+ }
149
+ }
150
+ for ( const [ , block ] of fn . body . blocks ) {
151
+ for ( const instr of block . instructions ) {
152
+ for ( const operand of eachInstructionValueLValue ( instr . value ) ) {
153
+ reference ( instr . id , operand , ReferenceKind . Read ) ;
154
+ }
155
+ for ( const lvalue of eachInstructionLValue ( instr ) ) {
156
+ reference ( instr . id , lvalue , ReferenceKind . Write ) ;
157
+ }
158
+ }
159
+ for ( const operand of eachTerminalOperand ( block . terminal ) ) {
160
+ reference ( block . terminal . id , operand , ReferenceKind . Read ) ;
161
+ }
162
+ }
163
+ return { accessedRanges, lastAssignments } ;
164
+ }
165
+
94
166
function reorderBlock (
95
167
env : Environment ,
96
168
block : BasicBlock ,
97
- shared : Nodes
169
+ shared : Nodes ,
170
+ references : References
98
171
) : void {
99
172
const locals : Nodes = new Map ( ) ;
100
173
const named : Map < string , IdentifierId > = new Map ( ) ;
@@ -116,7 +189,7 @@ function reorderBlock(
116
189
* Ensure non-reoderable instructions have their order retained by
117
190
* adding explicit dependencies to the previous such instruction.
118
191
*/
119
- if ( getReoderability ( instr ) === Reorderability . Nonreorderable ) {
192
+ if ( getReoderability ( instr , references ) === Reorderability . Nonreorderable ) {
120
193
if ( previous !== null ) {
121
194
node . dependencies . add ( previous ) ;
122
195
}
@@ -220,7 +293,8 @@ function reorderBlock(
220
293
}
221
294
CompilerError . invariant (
222
295
node . instruction != null &&
223
- getReoderability ( node . instruction ) === Reorderability . Reorderable ,
296
+ getReoderability ( node . instruction , references ) ===
297
+ Reorderability . Reorderable ,
224
298
{
225
299
reason : `Expected all remaining instructions to be reorderable` ,
226
300
loc : node . instruction ?. loc ?? block . terminal . loc ,
@@ -334,7 +408,10 @@ enum Reorderability {
334
408
Reorderable ,
335
409
Nonreorderable ,
336
410
}
337
- function getReoderability ( instr : Instruction ) : Reorderability {
411
+ function getReoderability (
412
+ instr : Instruction ,
413
+ references : References
414
+ ) : Reorderability {
338
415
switch ( instr . value . kind ) {
339
416
case "JsxExpression" :
340
417
case "JsxFragment" :
@@ -346,6 +423,23 @@ function getReoderability(instr: Instruction): Reorderability {
346
423
case "UnaryExpression" : {
347
424
return Reorderability . Reorderable ;
348
425
}
426
+ case "LoadLocal" : {
427
+ const name = instr . value . place . identifier . name ;
428
+ if ( name !== null && name . kind === "named" ) {
429
+ const lastAssignment = references . lastAssignments . get ( name . value ) ;
430
+ const range = references . accessedRanges . get ( instr . lvalue . identifier . id ) ;
431
+ if (
432
+ lastAssignment !== undefined &&
433
+ lastAssignment < instr . id &&
434
+ range !== undefined &&
435
+ range . end === range . start // this LoadLocal is used exactly once
436
+ ) {
437
+ console . log ( `reorderable: ${ name . value } ` ) ;
438
+ return Reorderability . Reorderable ;
439
+ }
440
+ }
441
+ return Reorderability . Nonreorderable ;
442
+ }
349
443
default : {
350
444
return Reorderability . Nonreorderable ;
351
445
}
0 commit comments