1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 public class Main {
18 
main(String[] args)19   public static void main(String[] args) {
20     testSimpleUse();
21     testTwoUses();
22     testFieldStores(doThrow);
23     testFieldStoreCycle();
24     testArrayStores();
25     testOnlyStoreUses();
26     testNoUse();
27     testPhiInput();
28     testVolatileStore();
29     doThrow = true;
30     try {
31       testInstanceSideEffects();
32     } catch (Error e) {
33       // expected
34       System.out.println(e.getMessage());
35     }
36     try {
37       testStaticSideEffects();
38     } catch (Error e) {
39       // expected
40       System.out.println(e.getMessage());
41     }
42 
43     try {
44       testStoreStore(doThrow);
45     } catch (Error e) {
46       // expected
47       System.out.println(e.getMessage());
48     }
49   }
50 
51   /// CHECK-START: void Main.testSimpleUse() code_sinking (before)
52   /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:java.lang.Object
53   /// CHECK: <<New:l\d+>>       NewInstance [<<LoadClass>>]
54   /// CHECK:                    ConstructorFence [<<New>>]
55   /// CHECK:                    If
56   /// CHECK:                    begin_block
57   /// CHECK:                    Throw
58 
59   /// CHECK-START: void Main.testSimpleUse() code_sinking (after)
60   /// CHECK-NOT:                NewInstance
61   /// CHECK:                    If
62   /// CHECK:                    begin_block
63   /// CHECK: <<Error:l\d+>>     LoadClass class_name:java.lang.Error
64   /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:java.lang.Object
65   /// CHECK-NOT:                begin_block
66   /// CHECK: <<New:l\d+>>       NewInstance [<<LoadClass>>]
67   /// CHECK:                    ConstructorFence [<<New>>]
68   /// CHECK-NOT:                begin_block
69   /// CHECK:                    NewInstance [<<Error>>]
70   /// CHECK:                    Throw
testSimpleUse()71   public static void testSimpleUse() {
72     Object o = new Object();
73     if (doThrow) {
74       throw new Error(o.toString());
75     }
76   }
77 
78   /// CHECK-START: void Main.testTwoUses() code_sinking (before)
79   /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:java.lang.Object
80   /// CHECK:                    NewInstance [<<LoadClass>>]
81   /// CHECK:                    If
82   /// CHECK:                    begin_block
83   /// CHECK:                    Throw
84 
85   /// CHECK-START: void Main.testTwoUses() code_sinking (after)
86   /// CHECK-NOT:                NewInstance
87   /// CHECK:                    If
88   /// CHECK:                    begin_block
89   /// CHECK: <<Error:l\d+>>     LoadClass class_name:java.lang.Error
90   /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:java.lang.Object
91   /// CHECK-NOT:                begin_block
92   /// CHECK:                    NewInstance [<<LoadClass>>]
93   /// CHECK-NOT:                begin_block
94   /// CHECK:                    NewInstance [<<Error>>]
95   /// CHECK:                    Throw
testTwoUses()96   public static void testTwoUses() {
97     Object o = new Object();
98     if (doThrow) {
99       throw new Error(o.toString() + o.toString());
100     }
101   }
102 
103   /// CHECK-START: void Main.testFieldStores(boolean) code_sinking (before)
104   /// CHECK: <<Int42:i\d+>>       IntConstant 42
105   /// CHECK: <<LoadClass:l\d+>>   LoadClass class_name:Main
106   /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>]
107   /// CHECK:                      InstanceFieldSet [<<NewInstance>>,<<Int42>>]
108   /// CHECK:                      If
109   /// CHECK:                      begin_block
110   /// CHECK:                      Throw
111 
112   /// CHECK-START: void Main.testFieldStores(boolean) code_sinking (after)
113   /// CHECK: <<Int42:i\d+>>       IntConstant 42
114   /// CHECK-NOT:                  NewInstance
115   /// CHECK:                      If
116   /// CHECK:                      begin_block
117   /// CHECK: <<Error:l\d+>>       LoadClass class_name:java.lang.Error
118   /// CHECK: <<LoadClass:l\d+>>   LoadClass class_name:Main
119   /// CHECK-NOT:                  begin_block
120   /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>]
121   /// CHECK-NOT:                  begin_block
122   /// CHECK:                      InstanceFieldSet [<<NewInstance>>,<<Int42>>]
123   /// CHECK-NOT:                  begin_block
124   /// CHECK:                      NewInstance [<<Error>>]
125   /// CHECK:                      Throw
testFieldStores(boolean doThrow)126   public static void testFieldStores(boolean doThrow) {
127     Main m = new Main();
128     m.intField = 42;
129     if (doThrow) {
130       throw new Error(m.toString());
131     }
132   }
133 
134   /// CHECK-START: void Main.testFieldStoreCycle() code_sinking (before)
135   /// CHECK: <<LoadClass:l\d+>>    LoadClass class_name:Main
136   /// CHECK: <<NewInstance1:l\d+>> NewInstance [<<LoadClass>>]
137   /// CHECK: <<NewInstance2:l\d+>> NewInstance [<<LoadClass>>]
138   /// CHECK:                       InstanceFieldSet [<<NewInstance1>>,<<NewInstance2>>]
139   /// CHECK:                       InstanceFieldSet [<<NewInstance2>>,<<NewInstance1>>]
140   /// CHECK:                       If
141   /// CHECK:                       begin_block
142   /// CHECK:                       Throw
143 
144   // TODO(ngeoffray): Handle allocation/store cycles.
145   /// CHECK-START: void Main.testFieldStoreCycle() code_sinking (after)
146   /// CHECK: begin_block
147   /// CHECK: <<LoadClass:l\d+>>    LoadClass class_name:Main
148   /// CHECK: <<NewInstance1:l\d+>> NewInstance [<<LoadClass>>]
149   /// CHECK: <<NewInstance2:l\d+>> NewInstance [<<LoadClass>>]
150   /// CHECK:                       InstanceFieldSet [<<NewInstance1>>,<<NewInstance2>>]
151   /// CHECK:                       InstanceFieldSet [<<NewInstance2>>,<<NewInstance1>>]
152   /// CHECK:                       If
153   /// CHECK:                       begin_block
154   /// CHECK:                       Throw
testFieldStoreCycle()155   public static void testFieldStoreCycle() {
156     Main m1 = new Main();
157     Main m2 = new Main();
158     m1.objectField = m2;
159     m2.objectField = m1;
160     if (doThrow) {
161       throw new Error(m1.toString() + m2.toString());
162     }
163   }
164 
165   /// CHECK-START: void Main.testArrayStores() code_sinking (before)
166   /// CHECK: <<Int1:i\d+>>        IntConstant 1
167   /// CHECK: <<Int0:i\d+>>        IntConstant 0
168   /// CHECK: <<LoadClass:l\d+>>   LoadClass class_name:java.lang.Object[]
169   /// CHECK: <<NewArray:l\d+>>    NewArray [<<LoadClass>>,<<Int1>>]
170   /// CHECK:                      ArraySet [<<NewArray>>,<<Int0>>,<<NewArray>>]
171   /// CHECK:                      If
172   /// CHECK:                      begin_block
173   /// CHECK:                      Throw
174 
175   /// CHECK-START: void Main.testArrayStores() code_sinking (after)
176   /// CHECK: <<Int1:i\d+>>        IntConstant 1
177   /// CHECK: <<Int0:i\d+>>        IntConstant 0
178   /// CHECK-NOT:                  NewArray
179   /// CHECK:                      If
180   /// CHECK:                      begin_block
181   /// CHECK: <<Error:l\d+>>       LoadClass class_name:java.lang.Error
182   /// CHECK: <<LoadClass:l\d+>>   LoadClass class_name:java.lang.Object[]
183   /// CHECK-NOT:                  begin_block
184   /// CHECK: <<NewArray:l\d+>>    NewArray [<<LoadClass>>,<<Int1>>]
185   /// CHECK-NOT:                  begin_block
186   /// CHECK:                      ArraySet [<<NewArray>>,<<Int0>>,<<NewArray>>]
187   /// CHECK-NOT:                  begin_block
188   /// CHECK:                      NewInstance [<<Error>>]
189   /// CHECK:                      Throw
testArrayStores()190   public static void testArrayStores() {
191     Object[] o = new Object[1];
192     o[0] = o;
193     if (doThrow) {
194       throw new Error(o.toString());
195     }
196   }
197 
198   // Make sure code sinking does not crash on dead allocations.
testOnlyStoreUses()199   public static void testOnlyStoreUses() {
200     Main m = new Main();
201     Object[] o = new Object[1];  // dead allocation, should eventually be removed b/35634932.
202     o[0] = m;
203     o = null;  // Avoid environment uses for the array allocation.
204     if (doThrow) {
205       throw new Error(m.toString());
206     }
207   }
208 
209   // Make sure code sinking does not crash on dead code.
testNoUse()210   public static void testNoUse() {
211     Main m = new Main();
212     boolean load = Main.doLoop;  // dead code, not removed because of environment use.
213     // Ensure one environment use for the static field
214     $opt$noinline$foo();
215     load = false;
216     if (doThrow) {
217       throw new Error(m.toString());
218     }
219   }
220 
221   // Make sure we can move code only used by a phi.
222   /// CHECK-START: void Main.testPhiInput() code_sinking (before)
223   /// CHECK: <<Null:l\d+>>        NullConstant
224   /// CHECK: <<LoadClass:l\d+>>   LoadClass class_name:java.lang.Object
225   /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>]
226   /// CHECK:                      If
227   /// CHECK:                      begin_block
228   /// CHECK:                      Phi [<<Null>>,<<NewInstance>>]
229   /// CHECK:                      Throw
230 
231   /// CHECK-START: void Main.testPhiInput() code_sinking (after)
232   /// CHECK: <<Null:l\d+>>        NullConstant
233   /// CHECK-NOT:                  NewInstance
234   /// CHECK:                      If
235   /// CHECK:                      begin_block
236   /// CHECK: <<LoadClass:l\d+>>   LoadClass class_name:java.lang.Object
237   /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>]
238   /// CHECK:                      begin_block
239   /// CHECK:                      Phi [<<Null>>,<<NewInstance>>]
240   /// CHECK: <<Error:l\d+>>       LoadClass class_name:java.lang.Error
241   /// CHECK:                      NewInstance [<<Error>>]
242   /// CHECK:                      Throw
testPhiInput()243   public static void testPhiInput() {
244     Object f = new Object();
245     if (doThrow) {
246       Object o = null;
247       int i = 2;
248       if (doLoop) {
249         o = f;
250         i = 42;
251       }
252       throw new Error(o.toString() + i);
253     }
254   }
255 
$opt$noinline$foo()256   static void $opt$noinline$foo() {}
257 
258   // Check that we do not move volatile stores.
259   /// CHECK-START: void Main.testVolatileStore() code_sinking (before)
260   /// CHECK: <<Int42:i\d+>>        IntConstant 42
261   /// CHECK: <<LoadClass:l\d+>>    LoadClass class_name:Main
262   /// CHECK: <<NewInstance:l\d+>>  NewInstance [<<LoadClass>>]
263   /// CHECK:                       InstanceFieldSet [<<NewInstance>>,<<Int42>>]
264   /// CHECK:                       If
265   /// CHECK:                       begin_block
266   /// CHECK:                       Throw
267 
268   /// CHECK-START: void Main.testVolatileStore() code_sinking (after)
269   /// CHECK: <<Int42:i\d+>>        IntConstant 42
270   /// CHECK: <<LoadClass:l\d+>>    LoadClass class_name:Main
271   /// CHECK: <<NewInstance:l\d+>>  NewInstance [<<LoadClass>>]
272   /// CHECK:                       InstanceFieldSet [<<NewInstance>>,<<Int42>>]
273   /// CHECK:                       If
274   /// CHECK:                       begin_block
275   /// CHECK:                       Throw
testVolatileStore()276   public static void testVolatileStore() {
277     Main m = new Main();
278     m.volatileField = 42;
279     if (doThrow) {
280       throw new Error(m.toString());
281     }
282   }
283 
testInstanceSideEffects()284   public static void testInstanceSideEffects() {
285     int a = mainField.intField;
286     $noinline$changeIntField();
287     if (doThrow) {
288       throw new Error("" + a);
289     }
290   }
291 
$noinline$changeIntField()292   static void $noinline$changeIntField() {
293     mainField.intField = 42;
294   }
295 
testStaticSideEffects()296   public static void testStaticSideEffects() {
297     Object o = obj;
298     $noinline$changeStaticObjectField();
299     if (doThrow) {
300       throw new Error(o.getClass().toString());
301     }
302   }
303 
$noinline$changeStaticObjectField()304   static void $noinline$changeStaticObjectField() {
305     obj = new Main();
306   }
307 
308   // Test that we preserve the order of stores.
309   /// CHECK-START: void Main.testStoreStore(boolean) code_sinking (before)
310   /// CHECK: <<Int42:i\d+>>       IntConstant 42
311   /// CHECK: <<Int43:i\d+>>       IntConstant 43
312   /// CHECK: <<LoadClass:l\d+>>   LoadClass class_name:Main
313   /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>]
314   /// CHECK:                      InstanceFieldSet [<<NewInstance>>,<<Int42>>]
315   /// CHECK:                      InstanceFieldSet [<<NewInstance>>,<<Int43>>]
316   /// CHECK:                      If
317   /// CHECK:                      begin_block
318   /// CHECK:                      Throw
319 
320   /// CHECK-START: void Main.testStoreStore(boolean) code_sinking (after)
321   /// CHECK: <<Int42:i\d+>>       IntConstant 42
322   /// CHECK: <<Int43:i\d+>>       IntConstant 43
323   /// CHECK-NOT:                  NewInstance
324   /// CHECK:                      If
325   /// CHECK:                      begin_block
326   /// CHECK: <<Error:l\d+>>       LoadClass class_name:java.lang.Error
327   /// CHECK: <<LoadClass:l\d+>>   LoadClass class_name:Main
328   /// CHECK-NOT:                  begin_block
329   /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>]
330   /// CHECK-NOT:                  begin_block
331   /// CHECK:                      InstanceFieldSet [<<NewInstance>>,<<Int42>>]
332   /// CHECK-NOT:                  begin_block
333   /// CHECK:                      InstanceFieldSet [<<NewInstance>>,<<Int43>>]
334   /// CHECK-NOT:                  begin_block
335   /// CHECK:                      NewInstance [<<Error>>]
336   /// CHECK:                      Throw
testStoreStore(boolean doThrow)337   public static void testStoreStore(boolean doThrow) {
338     Main m = new Main();
339     m.intField = 42;
340     m.intField2 = 43;
341     if (doThrow) {
342       throw new Error(m.$opt$noinline$toString());
343     }
344   }
345 
doStaticNativeCallLiveVreg()346   static native void doStaticNativeCallLiveVreg();
347 
348   //  Test ensures that 'o' has been moved into the if despite the InvokeStaticOrDirect.
349   //
350   /// CHECK-START: void Main.testSinkingOverInvoke() code_sinking (before)
351   /// CHECK: <<Int1:i\d+>>        IntConstant 1
352   /// CHECK: <<Int0:i\d+>>        IntConstant 0
353   /// CHECK: <<LoadClass:l\d+>>   LoadClass class_name:java.lang.Object[]
354   /// CHECK-NOT:                  begin_block
355   /// CHECK:                      NewArray [<<LoadClass>>,<<Int1>>]
356   /// CHECK:                      If
357   /// CHECK:                      begin_block
358   /// CHECK:                      Throw
359 
360   /// CHECK-START: void Main.testSinkingOverInvoke() code_sinking (after)
361   /// CHECK: <<Int1:i\d+>>        IntConstant 1
362   /// CHECK: <<Int0:i\d+>>        IntConstant 0
363   /// CHECK:                      If
364   /// CHECK:                      begin_block
365   /// CHECK: <<LoadClass:l\d+>>   LoadClass class_name:java.lang.Object[]
366   /// CHECK:                      NewArray [<<LoadClass>>,<<Int1>>]
367   /// CHECK:                      Throw
testSinkingOverInvoke()368   static void testSinkingOverInvoke() {
369     Object[] o = new Object[1];
370     o[0] = o;
371     doStaticNativeCallLiveVreg();
372     if (doThrow) {
373       throw new Error(o.toString());
374     }
375   }
376 
$opt$noinline$toString()377   public String $opt$noinline$toString() {
378     return "" + intField;
379   }
380 
381   volatile int volatileField;
382   int intField;
383   int intField2;
384   Object objectField;
385   static boolean doThrow;
386   static boolean doLoop;
387   static Main mainField = new Main();
388   static Object obj = new Object();
389 }
390