@@ -63,4 +63,69 @@ public void DoesNotInlineAcrossJoinWhenLocalHasMultipleDefinitions()
6363 Assert . That ( ReferenceEquals ( writeLineCall . Operands [ 1 ] , selected ) , Is . True ,
6464 "join-point call must keep the selected local, not a branch-specific constant" ) ;
6565 }
66+
67+ [ Test ]
68+ public void DoesNotCorruptUnrelatedConstantMemoryAddends ( )
69+ {
70+ var aLocal = new LocalVariable ( "a" , new Register ( null , "a" ) ) ;
71+ var bLocal = new LocalVariable ( "b" , new Register ( null , "b" ) ) ;
72+
73+ // a := [0xAAAA]; f(a); b := [0xBBBB]; g(b). Inlining a's constant load must not touch the
74+ // unrelated constant address [0xBBBB] - they are distinct absolute addresses.
75+ var instructions = new List < Instruction >
76+ {
77+ new ( 0 , OpCode . Move , aLocal , new MemoryOperand ( null , null , 0xAAAA , 0 ) ) ,
78+ new ( 1 , OpCode . CallVoid , "f" , aLocal , 0 ) ,
79+ new ( 2 , OpCode . Move , bLocal , new MemoryOperand ( null , null , 0xBBBB , 0 ) ) ,
80+ new ( 3 , OpCode . CallVoid , "g" , bLocal , 0 ) ,
81+ new ( 4 , OpCode . Return ) ,
82+ } ;
83+
84+ var graph = new ISILControlFlowGraph ( instructions ) ;
85+ var method = CreateMethod ( graph , aLocal , bLocal ) ;
86+
87+ Simplifier . Simplify ( method ) ;
88+
89+ var live = graph . Blocks . SelectMany ( b => b . Instructions ) . ToList ( ) ;
90+
91+ var gCall = live . Single ( i => i . OpCode == OpCode . CallVoid && i . Operands [ 0 ] is "g" ) ;
92+ Assert . That ( gCall . Operands [ 1 ] , Is . InstanceOf < MemoryOperand > ( ) ) ;
93+ Assert . That ( ( ( MemoryOperand ) gCall . Operands [ 1 ] ) . Addend , Is . EqualTo ( 0xBBBBL ) ,
94+ "an unrelated constant address must not be rewritten by another inline" ) ;
95+
96+ // The intended inline still happens.
97+ var fCall = live . Single ( i => i . OpCode == OpCode . CallVoid && i . Operands [ 0 ] is "f" ) ;
98+ Assert . That ( fCall . Operands [ 1 ] , Is . InstanceOf < MemoryOperand > ( ) ) ;
99+ Assert . That ( ( ( MemoryOperand ) fCall . Operands [ 1 ] ) . Addend , Is . EqualTo ( 0xAAAAL ) ) ;
100+ }
101+
102+ [ Test ]
103+ public void PropagatesCopyThroughMemoryBase ( )
104+ {
105+ var x = new LocalVariable ( "x" , new Register ( null , "x" ) ) ;
106+ var y = new LocalVariable ( "y" , new Register ( null , "y" ) ) ;
107+ var z = new LocalVariable ( "z" , new Register ( null , "z" ) ) ;
108+
109+ // x := y; z := [x]; f(z). Copy propagation must rewrite the load's base x -> y (a base must
110+ // stay a local), so the surviving load reads [y].
111+ var instructions = new List < Instruction >
112+ {
113+ new ( 0 , OpCode . Move , x , y ) ,
114+ new ( 1 , OpCode . Move , z , new MemoryOperand ( x , null , 0 , 0 ) ) ,
115+ new ( 2 , OpCode . CallVoid , "f" , z , 0 ) ,
116+ new ( 3 , OpCode . Return ) ,
117+ } ;
118+
119+ var graph = new ISILControlFlowGraph ( instructions ) ;
120+ var method = CreateMethod ( graph , x , y , z ) ;
121+
122+ Simplifier . Simplify ( method ) ;
123+
124+ var live = graph . Blocks . SelectMany ( b => b . Instructions ) . ToList ( ) ;
125+ var load = live . Single ( i => i . Operands . Any ( o => o is MemoryOperand ) ) ;
126+ var memory = ( MemoryOperand ) load . Operands . First ( o => o is MemoryOperand ) ;
127+ Assert . That ( ReferenceEquals ( memory . Base , y ) , Is . True , "the copy's source must be propagated into the memory base" ) ;
128+ Assert . That ( live . Any ( i => i . OpCode == OpCode . Move && ReferenceEquals ( i . Operands [ 0 ] , x ) ) , Is . False ,
129+ "the now-dead copy is removed" ) ;
130+ }
66131}
0 commit comments