@@ -107,11 +107,9 @@ export function inferMutationAliasingRanges(fn: HIRFunction): void {
107107 for ( const effect of instr . effects ) {
108108 if ( effect . kind === 'Create' || effect . kind === 'CreateFunction' ) {
109109 state . create ( effect . into ) ;
110- } else if (
111- effect . kind === 'Assign' ||
112- effect . kind === 'Alias' ||
113- effect . kind === 'CreateFrom'
114- ) {
110+ } else if ( effect . kind === 'CreateFrom' ) {
111+ state . createFrom ( index ++ , effect . from , effect . into ) ;
112+ } else if ( effect . kind === 'Assign' || effect . kind === 'Alias' ) {
115113 state . assign ( index ++ , effect . from , effect . into ) ;
116114 } else if ( effect . kind === 'Capture' ) {
117115 state . capture ( index ++ , effect . from , effect . into ) ;
@@ -181,7 +179,7 @@ export function inferMutationAliasingRanges(fn: HIRFunction): void {
181179 for ( const mutation of mutations ) {
182180 state . mutate (
183181 mutation . index ,
184- mutation . place ,
182+ mutation . place . identifier ,
185183 makeInstructionId ( mutation . id + 1 ) ,
186184 mutation . transitive ,
187185 mutation . kind ,
@@ -191,8 +189,9 @@ export function inferMutationAliasingRanges(fn: HIRFunction): void {
191189 console . log ( state . debug ( ) ) ;
192190 }
193191 fn . aliasingEffects ??= [ ] ;
194- for ( const param of fn . context ) {
195- const node = state . nodes . get ( param . identifier ) ;
192+ for ( const param of [ ...fn . context , ...fn . params ] ) {
193+ const place = param . kind === 'Identifier' ? param : param . place ;
194+ const node = state . nodes . get ( place . identifier ) ;
196195 if ( node == null ) {
197196 continue ;
198197 }
@@ -201,30 +200,30 @@ export function inferMutationAliasingRanges(fn: HIRFunction): void {
201200 mutated = true ;
202201 fn . aliasingEffects . push ( {
203202 kind : 'MutateConditionally' ,
204- value : param ,
203+ value : place ,
205204 } ) ;
206205 } else if ( node . local === MutationKind . Definite ) {
207206 mutated = true ;
208207 fn . aliasingEffects . push ( {
209208 kind : 'Mutate' ,
210- value : param ,
209+ value : place ,
211210 } ) ;
212211 }
213212 if ( node . transitive === MutationKind . Conditional ) {
214213 mutated = true ;
215214 fn . aliasingEffects . push ( {
216215 kind : 'MutateTransitiveConditionally' ,
217- value : param ,
216+ value : place ,
218217 } ) ;
219218 } else if ( node . transitive === MutationKind . Definite ) {
220219 mutated = true ;
221220 fn . aliasingEffects . push ( {
222221 kind : 'MutateTransitive' ,
223- value : param ,
222+ value : place ,
224223 } ) ;
225224 }
226225 if ( mutated ) {
227- param . effect = Effect . Capture ;
226+ place . effect = Effect . Capture ;
228227 }
229228 }
230229
@@ -247,6 +246,21 @@ export function inferMutationAliasingRanges(fn: HIRFunction): void {
247246 ? Effect . Capture
248247 : Effect . Read ;
249248 }
249+ if (
250+ isPhiMutatedAfterCreation &&
251+ phi . place . identifier . mutableRange . start === 0
252+ ) {
253+ /*
254+ * TODO: ideally we'd construct a precise start range, but what really
255+ * matters is that the phi's range appears mutable (end > start + 1)
256+ * so we just set the start to the previous instruction before this block
257+ */
258+ const firstInstructionIdOfBlock =
259+ block . instructions . at ( 0 ) ?. id ?? block . terminal . id ;
260+ phi . place . identifier . mutableRange . start = makeInstructionId (
261+ firstInstructionIdOfBlock - 1 ,
262+ ) ;
263+ }
250264 }
251265 for ( const instr of block . instructions ) {
252266 for ( const lvalue of eachInstructionLValue ( instr ) ) {
@@ -357,6 +371,7 @@ export function inferMutationAliasingRanges(fn: HIRFunction): void {
357371
358372type Node = {
359373 id : Identifier ;
374+ createdFrom : Map < Identifier , number > ;
360375 captures : Map < Identifier , number > ;
361376 aliases : Map < Identifier , number > ;
362377 edges : Array < { index : number ; node : Identifier ; kind : 'capture' | 'alias' } > ;
@@ -369,6 +384,7 @@ class AliasingState {
369384 create ( place : Place ) : void {
370385 this . nodes . set ( place . identifier , {
371386 id : place . identifier ,
387+ createdFrom : new Map ( ) ,
372388 captures : new Map ( ) ,
373389 aliases : new Map ( ) ,
374390 edges : [ ] ,
@@ -377,6 +393,24 @@ class AliasingState {
377393 } ) ;
378394 }
379395
396+ createFrom ( index : number , from : Place , into : Place ) : void {
397+ this . create ( into ) ;
398+ const fromNode = this . nodes . get ( from . identifier ) ;
399+ const toNode = this . nodes . get ( into . identifier ) ;
400+ if ( fromNode == null || toNode == null ) {
401+ if ( DEBUG ) {
402+ console . log (
403+ `skip: createFrom ${ printPlace ( from ) } ${ ! ! fromNode } -> ${ printPlace ( into ) } ${ ! ! toNode } ` ,
404+ ) ;
405+ }
406+ return ;
407+ }
408+ fromNode . edges . push ( { index, node : into . identifier , kind : 'alias' } ) ;
409+ if ( ! toNode . createdFrom . has ( from . identifier ) ) {
410+ toNode . createdFrom . set ( from . identifier , index ) ;
411+ }
412+ }
413+
380414 capture ( index : number , from : Place , into : Place ) : void {
381415 const fromNode = this . nodes . get ( from . identifier ) ;
382416 const toNode = this . nodes . get ( into . identifier ) ;
@@ -406,22 +440,22 @@ class AliasingState {
406440 return ;
407441 }
408442 fromNode . edges . push ( { index, node : into . identifier , kind : 'alias' } ) ;
409- if ( ! toNode . captures . has ( from . identifier ) ) {
443+ if ( ! toNode . aliases . has ( from . identifier ) ) {
410444 toNode . aliases . set ( from . identifier , index ) ;
411445 }
412446 }
413447
414448 mutate (
415449 index : number ,
416- start : Place ,
450+ start : Identifier ,
417451 end : InstructionId ,
418452 transitive : boolean ,
419453 kind : MutationKind ,
420454 ) : void {
421455 const seen = new Set < Identifier > ( ) ;
422- const queue = [ start . identifier ] ;
456+ const queue = [ { place : start , transitive } ] ;
423457 while ( queue . length !== 0 ) {
424- const current = queue . pop ( ) ! ;
458+ const { place : current , transitive } = queue . pop ( ) ! ;
425459 if ( seen . has ( current ) ) {
426460 continue ;
427461 }
@@ -430,14 +464,14 @@ class AliasingState {
430464 if ( node == null ) {
431465 if ( DEBUG ) {
432466 console . log (
433- `no node! ${ printPlace ( start ) } for identifier ${ printIdentifier ( current ) } ` ,
467+ `no node! ${ printIdentifier ( start ) } for identifier ${ printIdentifier ( current ) } ` ,
434468 ) ;
435469 }
436470 continue ;
437471 }
438472 if ( DEBUG ) {
439473 console . log (
440- `mutate $${ node . id . id } via ${ printPlace ( start ) } at [${ end } ]` ,
474+ `mutate $${ node . id . id } via ${ printIdentifier ( start ) } at [${ end } ]` ,
441475 ) ;
442476 }
443477 node . id . mutableRange . end = makeInstructionId (
@@ -457,7 +491,13 @@ class AliasingState {
457491 if ( edge . index >= index ) {
458492 break ;
459493 }
460- queue . push ( edge . node ) ;
494+ queue . push ( { place : edge . node , transitive} ) ;
495+ }
496+ for ( const [ alias , when ] of node . createdFrom ) {
497+ if ( when >= index ) {
498+ continue ;
499+ }
500+ queue . push ( { place : alias , transitive : true } ) ;
461501 }
462502 /**
463503 * all mutations affect backward alias edges by the rules:
@@ -468,7 +508,7 @@ class AliasingState {
468508 if ( when >= index ) {
469509 continue ;
470510 }
471- queue . push ( alias ) ;
511+ queue . push ( { place : alias , transitive } ) ;
472512 }
473513 /**
474514 * but only transitive mutations affect captures
@@ -478,7 +518,7 @@ class AliasingState {
478518 if ( when >= index ) {
479519 continue ;
480520 }
481- queue . push ( capture ) ;
521+ queue . push ( { place : capture , transitive } ) ;
482522 }
483523 }
484524 }
@@ -489,7 +529,7 @@ class AliasingState {
489529 }
490530}
491531
492- function pretty ( v : any ) : string {
532+ export function pretty ( v : any ) : string {
493533 return prettyFormat ( v , {
494534 plugins : [
495535 {
0 commit comments