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   // NB It might seem that we'd move the allocation and ifield-set but those are
104   // already moved into the throw block by a combo of partial-LSE and DCE.
105   // Instead all that is actually moved is the LoadClass. Also note the
106   // LoadClass can only be moved since it refers to the 'Main' class itself,
107   // meaning there's no need for any clinit/actual loading.
108   //
109   /// CHECK-START: void Main.testFieldStores(boolean) code_sinking (before)
110   /// CHECK: <<Int42:i\d+>>       IntConstant 42
111   /// CHECK:                      begin_block
112   /// CHECK: <<LoadClass:l\d+>>   LoadClass class_name:Main
113   /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>]
114   /// CHECK:                      InstanceFieldSet [<<NewInstance>>,<<Int42>>]
115   /// CHECK:                      Throw
116 
117   /// CHECK-START: void Main.testFieldStores(boolean) code_sinking (after)
118   /// CHECK: <<Int42:i\d+>>       IntConstant 42
119   /// CHECK-NOT:                  NewInstance
120   /// CHECK:                      If
121   /// CHECK:                      begin_block
122   /// CHECK: <<Error:l\d+>>       LoadClass class_name:java.lang.Error
123   /// CHECK-NOT:                  begin_block
124   /// CHECK: <<LoadClass:l\d+>>   LoadClass class_name:Main
125   /// CHECK-NOT:                  begin_block
126   /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>]
127   /// CHECK-NOT:                  begin_block
128   /// CHECK:                      InstanceFieldSet [<<NewInstance>>,<<Int42>>]
129   /// CHECK-NOT:                  begin_block
130   /// CHECK: <<Throw:l\d+>>       NewInstance [<<Error>>]
131   /// CHECK-NOT:                  begin_block
132   /// CHECK:                      Throw [<<Throw>>]
testFieldStores(boolean doThrow)133   public static void testFieldStores(boolean doThrow) {
134     Main m = new Main();
135     m.intField = 42;
136     if (doThrow) {
137       throw new Error(m.toString());
138     }
139   }
140 
141   /// CHECK-START: void Main.testFieldStoreCycle() code_sinking (before)
142   /// CHECK: <<LoadClass:l\d+>>    LoadClass class_name:Main
143   /// CHECK: <<NewInstance1:l\d+>> NewInstance [<<LoadClass>>]
144   /// CHECK: <<NewInstance2:l\d+>> NewInstance [<<LoadClass>>]
145   /// CHECK:                       InstanceFieldSet [<<NewInstance1>>,<<NewInstance2>>]
146   /// CHECK:                       InstanceFieldSet [<<NewInstance2>>,<<NewInstance1>>]
147   /// CHECK:                       If
148   /// CHECK:                       begin_block
149   /// CHECK:                       Throw
150 
151   // TODO(ngeoffray): Handle allocation/store cycles.
152   /// CHECK-START: void Main.testFieldStoreCycle() code_sinking (after)
153   /// CHECK: begin_block
154   /// CHECK: <<LoadClass:l\d+>>    LoadClass class_name:Main
155   /// CHECK: <<NewInstance1:l\d+>> NewInstance [<<LoadClass>>]
156   /// CHECK: <<NewInstance2:l\d+>> NewInstance [<<LoadClass>>]
157   /// CHECK:                       InstanceFieldSet [<<NewInstance1>>,<<NewInstance2>>]
158   /// CHECK:                       InstanceFieldSet [<<NewInstance2>>,<<NewInstance1>>]
159   /// CHECK:                       If
160   /// CHECK:                       begin_block
161   /// CHECK:                       Throw
testFieldStoreCycle()162   public static void testFieldStoreCycle() {
163     Main m1 = new Main();
164     Main m2 = new Main();
165     m1.objectField = m2;
166     m2.objectField = m1;
167     if (doThrow) {
168       throw new Error(m1.toString() + m2.toString());
169     }
170   }
171 
172   /// CHECK-START: void Main.testArrayStores() code_sinking (before)
173   /// CHECK: <<Int1:i\d+>>        IntConstant 1
174   /// CHECK: <<Int0:i\d+>>        IntConstant 0
175   /// CHECK: <<LoadClass:l\d+>>   LoadClass class_name:java.lang.Object[]
176   /// CHECK: <<NewArray:l\d+>>    NewArray [<<LoadClass>>,<<Int1>>]
177   /// CHECK:                      ArraySet [<<NewArray>>,<<Int0>>,<<NewArray>>]
178   /// CHECK:                      If
179   /// CHECK:                      begin_block
180   /// CHECK:                      Throw
181 
182   /// CHECK-START: void Main.testArrayStores() code_sinking (after)
183   /// CHECK: <<Int1:i\d+>>        IntConstant 1
184   /// CHECK: <<Int0:i\d+>>        IntConstant 0
185   /// CHECK-NOT:                  NewArray
186   /// CHECK:                      If
187   /// CHECK:                      begin_block
188   /// CHECK: <<Error:l\d+>>       LoadClass class_name:java.lang.Error
189   /// CHECK: <<LoadClass:l\d+>>   LoadClass class_name:java.lang.Object[]
190   /// CHECK-NOT:                  begin_block
191   /// CHECK: <<NewArray:l\d+>>    NewArray [<<LoadClass>>,<<Int1>>]
192   /// CHECK-NOT:                  begin_block
193   /// CHECK:                      ArraySet [<<NewArray>>,<<Int0>>,<<NewArray>>]
194   /// CHECK-NOT:                  begin_block
195   /// CHECK:                      NewInstance [<<Error>>]
196   /// CHECK:                      Throw
testArrayStores()197   public static void testArrayStores() {
198     Object[] o = new Object[1];
199     o[0] = o;
200     if (doThrow) {
201       throw new Error(o.toString());
202     }
203   }
204 
205   // Make sure code sinking does not crash on dead allocations.
testOnlyStoreUses()206   public static void testOnlyStoreUses() {
207     Main m = new Main();
208     Object[] o = new Object[1];  // dead allocation, should eventually be removed b/35634932.
209     o[0] = m;
210     o = null;  // Avoid environment uses for the array allocation.
211     if (doThrow) {
212       throw new Error(m.toString());
213     }
214   }
215 
216   // Make sure code sinking does not crash on dead code.
testNoUse()217   public static void testNoUse() {
218     Main m = new Main();
219     boolean load = Main.doLoop;  // dead code, not removed because of environment use.
220     // Ensure one environment use for the static field
221     $opt$noinline$foo();
222     load = false;
223     if (doThrow) {
224       throw new Error(m.toString());
225     }
226   }
227 
228   // Make sure we can move code only used by a phi.
229   /// CHECK-START: void Main.testPhiInput() code_sinking (before)
230   /// CHECK: <<Null:l\d+>>        NullConstant
231   /// CHECK: <<LoadClass:l\d+>>   LoadClass class_name:java.lang.Object
232   /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>]
233   /// CHECK:                      If
234   /// CHECK:                      begin_block
235   /// CHECK:                      Phi [<<Null>>,<<NewInstance>>]
236   /// CHECK:                      Throw
237 
238   /// CHECK-START: void Main.testPhiInput() code_sinking (after)
239   /// CHECK: <<Null:l\d+>>        NullConstant
240   /// CHECK-NOT:                  NewInstance
241   /// CHECK:                      If
242   /// CHECK:                      begin_block
243   /// CHECK: <<LoadClass:l\d+>>   LoadClass class_name:java.lang.Object
244   /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>]
245   /// CHECK:                      begin_block
246   /// CHECK:                      Phi [<<Null>>,<<NewInstance>>]
247   /// CHECK: <<Error:l\d+>>       LoadClass class_name:java.lang.Error
248   /// CHECK:                      NewInstance [<<Error>>]
249   /// CHECK:                      Throw
testPhiInput()250   public static void testPhiInput() {
251     Object f = new Object();
252     if (doThrow) {
253       Object o = null;
254       int i = 2;
255       if (doLoop) {
256         o = f;
257         i = 42;
258       }
259       throw new Error(o.toString() + i);
260     }
261   }
262 
$opt$noinline$foo()263   static void $opt$noinline$foo() {}
264 
265   // Check that we do not move volatile stores.
266   /// CHECK-START: void Main.testVolatileStore() code_sinking (before)
267   /// CHECK: <<Int42:i\d+>>        IntConstant 42
268   /// CHECK: <<LoadClass:l\d+>>    LoadClass class_name:Main
269   /// CHECK: <<NewInstance:l\d+>>  NewInstance [<<LoadClass>>]
270   /// CHECK:                       InstanceFieldSet [<<NewInstance>>,<<Int42>>]
271   /// CHECK:                       If
272   /// CHECK:                       begin_block
273   /// CHECK:                       Throw
274 
275   /// CHECK-START: void Main.testVolatileStore() code_sinking (after)
276   /// CHECK: <<Int42:i\d+>>        IntConstant 42
277   /// CHECK: <<LoadClass:l\d+>>    LoadClass class_name:Main
278   /// CHECK: <<NewInstance:l\d+>>  NewInstance [<<LoadClass>>]
279   /// CHECK:                       InstanceFieldSet [<<NewInstance>>,<<Int42>>]
280   /// CHECK:                       If
281   /// CHECK:                       begin_block
282   /// CHECK:                       Throw
testVolatileStore()283   public static void testVolatileStore() {
284     Main m = new Main();
285     m.volatileField = 42;
286     if (doThrow) {
287       throw new Error(m.toString());
288     }
289   }
290 
testInstanceSideEffects()291   public static void testInstanceSideEffects() {
292     int a = mainField.intField;
293     $noinline$changeIntField();
294     if (doThrow) {
295       throw new Error("" + a);
296     }
297   }
298 
$noinline$changeIntField()299   static void $noinline$changeIntField() {
300     mainField.intField = 42;
301   }
302 
testStaticSideEffects()303   public static void testStaticSideEffects() {
304     Object o = obj;
305     $noinline$changeStaticObjectField();
306     if (doThrow) {
307       throw new Error(o.getClass().toString());
308     }
309   }
310 
$noinline$changeStaticObjectField()311   static void $noinline$changeStaticObjectField() {
312     obj = new Main();
313   }
314 
315   // Test that we preserve the order of stores.
316   // NB It might seem that we'd move the allocation and ifield-set but those are
317   // already moved into the throw block by a combo of partial-LSE and DCE.
318   // Instead all that is actually moved is the LoadClass. Also note the
319   // LoadClass can only be moved since it refers to the 'Main' class itself,
320   // meaning there's no need for any clinit/actual loading.
321   //
322   /// CHECK-START: void Main.testStoreStore(boolean) code_sinking (before)
323   /// CHECK: <<Int42:i\d+>>       IntConstant 42
324   /// CHECK: <<Int43:i\d+>>       IntConstant 43
325   /// CHECK: <<LoadClass:l\d+>>   LoadClass class_name:Main
326   /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>]
327   /// CHECK-DAG:                  InstanceFieldSet [<<NewInstance>>,<<Int42>>]
328   /// CHECK-DAG:                  InstanceFieldSet [<<NewInstance>>,<<Int43>>]
329   /// CHECK:                      Throw
330   /// CHECK-NOT:                  InstanceFieldSet
331 
332   /// CHECK-START: void Main.testStoreStore(boolean) code_sinking (after)
333   /// CHECK: <<Int42:i\d+>>       IntConstant 42
334   /// CHECK: <<Int43:i\d+>>       IntConstant 43
335   /// CHECK-NOT:                  NewInstance
336   /// CHECK:                      If
337   /// CHECK:                      begin_block
338   /// CHECK: <<Error:l\d+>>       LoadClass class_name:java.lang.Error
339   /// CHECK-NOT:                  begin_block
340   /// CHECK: <<LoadClass:l\d+>>   LoadClass class_name:Main
341   /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>]
342   /// CHECK-NOT:                  begin_block
343   /// CHECK-DAG:                  InstanceFieldSet [<<NewInstance>>,<<Int42>>]
344   /// CHECK-DAG:                  InstanceFieldSet [<<NewInstance>>,<<Int43>>]
345   /// CHECK-NOT:                  begin_block
346   /// CHECK:                      NewInstance [<<Error>>]
347   /// CHECK:                      Throw
348   /// CHECK-NOT:                  InstanceFieldSet
testStoreStore(boolean doThrow)349   public static void testStoreStore(boolean doThrow) {
350     Main m = new Main();
351     m.intField = 42;
352     m.intField2 = 43;
353     if (doThrow) {
354       throw new Error(m.$opt$noinline$toString());
355     }
356   }
357 
doStaticNativeCallLiveVreg()358   static native void doStaticNativeCallLiveVreg();
359 
360   //  Test ensures that 'o' has been moved into the if despite the InvokeStaticOrDirect.
361   //
362   /// CHECK-START: void Main.testSinkingOverInvoke() code_sinking (before)
363   /// CHECK: <<Int1:i\d+>>        IntConstant 1
364   /// CHECK: <<Int0:i\d+>>        IntConstant 0
365   /// CHECK: <<LoadClass:l\d+>>   LoadClass class_name:java.lang.Object[]
366   /// CHECK-NOT:                  begin_block
367   /// CHECK:                      NewArray [<<LoadClass>>,<<Int1>>]
368   /// CHECK:                      If
369   /// CHECK:                      begin_block
370   /// CHECK:                      Throw
371 
372   /// CHECK-START: void Main.testSinkingOverInvoke() code_sinking (after)
373   /// CHECK: <<Int1:i\d+>>        IntConstant 1
374   /// CHECK: <<Int0:i\d+>>        IntConstant 0
375   /// CHECK:                      If
376   /// CHECK:                      begin_block
377   /// CHECK: <<LoadClass:l\d+>>   LoadClass class_name:java.lang.Object[]
378   /// CHECK:                      NewArray [<<LoadClass>>,<<Int1>>]
379   /// CHECK:                      Throw
testSinkingOverInvoke()380   static void testSinkingOverInvoke() {
381     Object[] o = new Object[1];
382     o[0] = o;
383     doStaticNativeCallLiveVreg();
384     if (doThrow) {
385       throw new Error(o.toString());
386     }
387   }
388 
$opt$noinline$toString()389   public String $opt$noinline$toString() {
390     return "" + intField;
391   }
392 
393   volatile int volatileField;
394   int intField;
395   int intField2;
396   Object objectField;
397   static boolean doThrow;
398   static boolean doLoop;
399   static Main mainField = new Main();
400   static Object obj = new Object();
401 }
402