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