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 import java.lang.reflect.Array;
18 import java.lang.reflect.Field;
19 import java.lang.reflect.Method;
20 import java.lang.reflect.Modifier;
21 
22 // Baseline class. This has no final fields, so there are no additional freezes
23 // in its constructor.
24 //
25 // The new-instance itself always has 1 freeze for the happens-before on the object header
26 // write (i.e. [obj.class = X] happens-before any access to obj).
27 //
28 // Total freezes for "new Base()": 1.
29 class Base {
30   int w0;
31   int w1;
32   int w2;
33   int w3;
34 
Base()35   Base() {
36     // Prevent inliner from matching the code pattern when calling this constructor
37     // to test the normal inlining that builds and inserts the callee graph.
38     // (Pattern matching can merge or eliminate constructor barriers.)
39     $inline$nop();
40   }
41 
42   @Override
toString()43   public String toString() {
44     return getClass().getName() + "(" + baseString() + ")";
45   }
46 
baseString()47   protected String baseString() {
48     return String.format("w0: %d, w1: %d, w2: %d, w3: %d", w0, w1, w2, w3);
49   }
50 
$inline$nop()51   private void $inline$nop() {}
52 }
53 
54 // This has a final field in its constructor, so there must be a field freeze
55 // at the end of <init>.
56 //
57 // Total freezes for "new OneFinal()": 2.
58 class OneFinal extends Base {
59   final int x;
OneFinal(int x)60   OneFinal(int x) {
61     this.x = x;
62   }
63 
64   @Override
baseString()65   protected String baseString() {
66     return String.format("%s, x: %d", super.baseString(), x);
67   }
68 }
69 
70 class Assert {
stringEquals(String expected, Object actual)71   public static void stringEquals(String expected, Object actual) {
72     stringEquals$noinline$(expected, actual);
73   }
74 
75   // Forbid compiler from inlining this to avoid overly clever optimizations.
stringEquals$noinline$(String expected, Object actual)76   private static void stringEquals$noinline$(String expected, Object actual) {
77     String actualStr = Main.valueToString(actual);
78     if (!expected.equals(actualStr)) {
79       throw new AssertionError("Expected: " + expected + ", actual: " + actualStr);
80     }
81   }
82 }
83 
84 interface Test {
exercise()85   public void exercise();
check()86   public void check();
87 }
88 
89 class TestOneFinal implements Test {
90   // Initialize at least once before actual test.
91   public static Object external;
92 
93   /// CHECK-START: void TestOneFinal.exercise() constructor_fence_redundancy_elimination (before)
94   /// CHECK: <<NewInstance:l\d+>>     NewInstance
95   /// CHECK-DAG:                      ConstructorFence [<<NewInstance>>]
96   /// CHECK-DAG:                      ConstructorFence [<<NewInstance>>]
97   /// CHECK-NOT:                      ConstructorFence
98   /// CHECK-DAG:                      StaticFieldSet [<<External:l\d+>>,<<NewInstance>>]
99 
100   /// CHECK-START: void TestOneFinal.exercise() constructor_fence_redundancy_elimination (after)
101   /// CHECK: <<NewInstance:l\d+>>     NewInstance
102   /// CHECK-DAG:                      ConstructorFence [<<NewInstance>>]
103   /// CHECK-NOT:                      ConstructorFence
104   /// CHECK-DAG:                      StaticFieldSet [<<External:l\d+>>,<<NewInstance>>]
105   @Override
exercise()106   public void exercise() {
107       Base b = new OneFinal(1);
108       // 1 store, 2 freezes.
109 
110       // Stores to 'b' do not escape b.
111       b.w0 = 1;
112       b.w1 = 2;
113       b.w2 = 3;
114 
115       // Publish the result to a global so that it is not LSE-eliminated.
116       external = b;
117   }
118 
119   @Override
check()120   public void check() {
121     Assert.stringEquals("OneFinal(w0: 1, w1: 2, w2: 3, w3: 0, x: 1)", external);
122   }
123 }
124 
125 // This has a final field in its constructor, so there must be a field freeze
126 // at the end of <init>. The previous base class's freezes accumulate on top
127 // of this one.
128 //
129 // Total freezes for "new TwoFinal()": 3.
130 class TwoFinal extends OneFinal {
131   final int y;
TwoFinal(int x, int y)132   TwoFinal(int x, int y) {
133     super(x);
134     this.y = y;
135   }
136 
137   @Override
baseString()138   protected String baseString() {
139     return String.format("%s, y: %d", super.baseString(), y);
140   }
141 }
142 
143 // This has a final field in its constructor, so there must be a field freeze
144 // at the end of <init>. The previous base class's freezes accumulate on top
145 // of this one.
146 //
147 // Total freezes for "new ThreeFinal()": 4.
148 class ThreeFinal extends TwoFinal {
149   final int z;
ThreeFinal(int x, int y, int z)150   ThreeFinal(int x, int y, int z) {
151     super(x, y);
152     this.z = z;
153   }
154 
155   @Override
baseString()156   protected String baseString() {
157     return String.format("%s, z: %d", super.baseString(), z);
158   }
159 }
160 
161 class TestThreeFinal implements Test {
162   // Initialize at least once before actual test.
163   public static Object external;
164 
165   /// CHECK-START: void TestThreeFinal.exercise() constructor_fence_redundancy_elimination (before)
166   /// CHECK: <<NewInstance:l\d+>>     NewInstance
167   /// CHECK-DAG:                      ConstructorFence [<<NewInstance>>]
168   /// CHECK-DAG:                      ConstructorFence [<<NewInstance>>]
169   /// CHECK-DAG:                      ConstructorFence [<<NewInstance>>]
170   /// CHECK-DAG:                      ConstructorFence [<<NewInstance>>]
171   /// CHECK-NOT:                      ConstructorFence
172   /// CHECK-DAG:                      StaticFieldSet [<<External:l\d+>>,<<NewInstance>>]
173 
174   /// CHECK-START: void TestThreeFinal.exercise() constructor_fence_redundancy_elimination (after)
175   /// CHECK: <<NewInstance:l\d+>>     NewInstance
176   /// CHECK-DAG:                      ConstructorFence [<<NewInstance>>]
177   /// CHECK-NOT:                      ConstructorFence
178   /// CHECK-DAG:                      StaticFieldSet [<<External:l\d+>>,<<NewInstance>>]
179   @Override
exercise()180   public void exercise() {
181     Base b = new ThreeFinal(1, 1, 2);
182     // 3 store, 4 freezes.
183 
184     // Stores to 'b' do not escape b.
185     b.w0 = 3;
186 
187     // Publish the result to a global so that it is not LSE-eliminated.
188     external = b;
189   }
190 
191   @Override
check()192   public void check() {
193     Assert.stringEquals("ThreeFinal(w0: 3, w1: 0, w2: 0, w3: 0, x: 1, y: 1, z: 2)", external);
194   }
195 }
196 
197 // Ensure "freezes" between multiple new-instances are optimized out.
198 class TestMultiAlloc implements Test {
199   public static Object external;
200   public static Object external2;
201 
202   /// CHECK-START: void TestMultiAlloc.exercise() constructor_fence_redundancy_elimination (before)
203   /// CHECK: <<NewInstance:l\d+>>     NewInstance
204   /// CHECK-DAG:                      ConstructorFence [<<NewInstance>>]
205   /// CHECK: <<NewInstance2:l\d+>>    NewInstance
206   /// CHECK-DAG:                      ConstructorFence [<<NewInstance2>>]
207   /// CHECK-NOT:                      ConstructorFence
208   /// CHECK-DAG:                      StaticFieldSet [<<External:l\d+>>,<<NewInstance>>]
209   /// CHECK-DAG:                      StaticFieldSet [<<External2:l\d+>>,<<NewInstance2>>]
210 
211   /// CHECK-START: void TestMultiAlloc.exercise() constructor_fence_redundancy_elimination (after)
212   /// CHECK: <<NewInstance:l\d+>>     NewInstance
213   /// CHECK: <<NewInstance2:l\d+>>    NewInstance
214   /// CHECK-DAG:                      ConstructorFence [<<NewInstance2>>,<<NewInstance>>]
215   /// CHECK-NOT:                      ConstructorFence
216   /// CHECK-DAG:                      StaticFieldSet [<<External:l\d+>>,<<NewInstance>>]
217   /// CHECK-DAG:                      StaticFieldSet [<<External2:l\d+>>,<<NewInstance2>>]
218   @Override
exercise()219   public void exercise() {
220     // 1 freeze
221     Base b = new Base();
222     // 1 freeze
223     Base b2 = new Base();
224 
225     // Merge 2 freezes above into 1 constructor fence.
226     external = b;
227     external2 = b2;
228   }
229 
230   @Override
check()231   public void check() {
232     Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external);
233     Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external2);
234   }
235 }
236 
237 // Ensure "freezes" between multiple new-instances are optimized out.
238 class TestThreeFinalTwice implements Test {
239   // Initialize at least once before actual test.
240   public static Object external;
241   public static Object external2;
242 
243   /// CHECK-START: void TestThreeFinalTwice.exercise() constructor_fence_redundancy_elimination (before)
244   /// CHECK: <<NewInstance:l\d+>>     NewInstance
245   /// CHECK-DAG:                      ConstructorFence [<<NewInstance>>]
246   /// CHECK-DAG:                      ConstructorFence [<<NewInstance>>]
247   /// CHECK-DAG:                      ConstructorFence [<<NewInstance>>]
248   /// CHECK-DAG:                      ConstructorFence [<<NewInstance>>]
249   /// CHECK: <<NewInstance2:l\d+>>    NewInstance
250   /// CHECK-DAG:                      ConstructorFence [<<NewInstance2>>]
251   /// CHECK-DAG:                      ConstructorFence [<<NewInstance2>>]
252   /// CHECK-DAG:                      ConstructorFence [<<NewInstance2>>]
253   /// CHECK-DAG:                      ConstructorFence [<<NewInstance2>>]
254   /// CHECK-NOT:                      ConstructorFence
255   /// CHECK-DAG:                      StaticFieldSet [<<External:l\d+>>,<<NewInstance>>]
256   /// CHECK-DAG:                      StaticFieldSet [<<External2:l\d+>>,<<NewInstance2>>]
257 
258   /// CHECK-START: void TestThreeFinalTwice.exercise() constructor_fence_redundancy_elimination (after)
259   /// CHECK: <<NewInstance:l\d+>>     NewInstance
260   /// CHECK: <<NewInstance2:l\d+>>    NewInstance
261   /// CHECK-DAG:                      ConstructorFence [<<NewInstance2>>,<<NewInstance>>]
262   /// CHECK-NOT:                      ConstructorFence
263   /// CHECK-DAG:                      StaticFieldSet [<<External:l\d+>>,<<NewInstance>>]
264   /// CHECK-DAG:                      StaticFieldSet [<<External2:l\d+>>,<<NewInstance2>>]
265   @Override
exercise()266   public void exercise() {
267     Base b = new ThreeFinal(1, 1, 2);
268     // 3 store, 4 freezes.
269 
270     // Stores to 'b' do not escape b.
271     b.w0 = 3;
272 
273     Base b2 = new ThreeFinal(4, 5, 6);
274     // 3 store, 4 freezes.
275 
276     // Stores to 'b2' do not escape b2.
277     b2.w0 = 7;
278 
279     // Publish the result to a global so that it is not LSE-eliminated.
280     // Publishing is done at the end to give freezes above a chance to merge.
281     external = b;
282     external2 = b2;
283   }
284 
285   @Override
check()286   public void check() {
287     Assert.stringEquals("ThreeFinal(w0: 3, w1: 0, w2: 0, w3: 0, x: 1, y: 1, z: 2)", external);
288     Assert.stringEquals("ThreeFinal(w0: 7, w1: 0, w2: 0, w3: 0, x: 4, y: 5, z: 6)", external2);
289   }
290 }
291 
292 class TestNonEscaping {
293   // Prevent constant folding.
294   static boolean test;
295 
296   static Object external;
297   static Object external2;
298   static Object external3;
299   static Object external4;
300 
301   static class Invoke implements Test {
302     /// CHECK-START: void TestNonEscaping$Invoke.exercise() constructor_fence_redundancy_elimination (before)
303     /// CHECK: <<NewInstance:l\d+>>     NewInstance
304     /// CHECK:                          ConstructorFence [<<NewInstance>>]
305     /// CHECK:                          InvokeStaticOrDirect
306     /// CHECK: <<NewInstance2:l\d+>>    NewInstance
307     /// CHECK-DAG:                      ConstructorFence [<<NewInstance2>>]
308     /// CHECK-NOT:                      ConstructorFence
309 
310     /// CHECK-START: void TestNonEscaping$Invoke.exercise() constructor_fence_redundancy_elimination (after)
311     /// CHECK: <<NewInstance:l\d+>>     NewInstance
312     /// CHECK:                          InvokeStaticOrDirect
313     /// CHECK: <<NewInstance2:l\d+>>    NewInstance
314     /// CHECK-DAG:                      ConstructorFence [<<NewInstance2>>,<<NewInstance>>]
315     /// CHECK-NOT:                      ConstructorFence
316     @Override
exercise()317     public void exercise() {
318       Base b = new Base();
319 
320       // b cannot possibly escape into this invoke because it hasn't escaped onto the heap earlier,
321       // and the invoke doesn't take it as a parameter.
322       noEscape$noinline$();
323 
324       // Remove the Constructor Fence for b, merging into the fence for b2.
325       Base b2 = new Base();
326 
327       // Do not LSE-eliminate b,b2
328       external = b;
329       external2 = b2;
330     }
331 
332     @Override
check()333     public void check() {
334       Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external);
335       Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external2);
336     }
337   }
338 
339   public static int[] array = new int[1];
340   static Base base = new Base();
341 
342   static class Store implements Test {
343     /// CHECK-START: void TestNonEscaping$Store.exercise() constructor_fence_redundancy_elimination (before)
344     /// CHECK: <<NewInstance:l\d+>>     NewInstance
345     /// CHECK:                          ConstructorFence [<<NewInstance>>]
346     /// CHECK-DAG:                      ArraySet
347     /// CHECK-DAG:                      StaticFieldSet
348     /// CHECK-DAG:                      InstanceFieldSet
349     /// CHECK: <<NewInstance2:l\d+>>    NewInstance
350     /// CHECK-DAG:                      ConstructorFence [<<NewInstance2>>]
351     /// CHECK-NOT:                      ConstructorFence
352 
353     /// CHECK-START: void TestNonEscaping$Store.exercise() constructor_fence_redundancy_elimination (after)
354     /// CHECK-DAG: <<NewInstance:l\d+>>   NewInstance
355     /// CHECK-DAG: <<NewInstance2:l\d+>>  NewInstance
356     /// CHECK-DAG:                        ConstructorFence [<<NewInstance2>>,<<NewInstance>>]
357     /// CHECK-NOT:                        ConstructorFence
358     @Override
exercise()359     public void exercise() {
360       Base b = new Base();
361 
362       // Stores of inputs other than the fence target do not publish 'b'.
363       array[0] = b.w0;  // aput
364       external = array; // sput
365       base.w0 = b.w0;   // iput
366 
367       // Remove the Constructor Fence for b, merging into the fence for b2.
368       Base b2 = new Base();
369 
370       // Do not LSE-eliminate b,b2
371       external3 = b;
372       external4 = b2;
373     }
374 
375     @Override
check()376     public void check() {
377       Assert.stringEquals("[0]", array);
378       Assert.stringEquals("[0]", external);
379       Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", base);
380       Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external3);
381       Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external4);
382     }
383   }
384 
noEscape$noinline$()385   private static void noEscape$noinline$() {
386   }
387 }
388 
389 class TestDontOptimizeAcrossBlocks implements Test {
390   // Prevent constant folding.
391   static boolean test;
392 
393   static Object external;
394   static Object external3;
395 
396   /// CHECK-START: void TestDontOptimizeAcrossBlocks.exercise() constructor_fence_redundancy_elimination (before)
397   /// CHECK: <<NewInstance:l\d+>>     NewInstance
398   /// CHECK:                          ConstructorFence [<<NewInstance>>]
399   /// CHECK: <<NewInstance2:l\d+>>    NewInstance
400   /// CHECK-DAG:                      ConstructorFence [<<NewInstance2>>]
401   /// CHECK-NOT:                      ConstructorFence
402   /// CHECK-DAG:                      StaticFieldSet [<<External:l\d+>>,<<NewInstance>>]
403   /// CHECK-DAG:                      StaticFieldSet [<<External2:l\d+>>,<<NewInstance2>>]
404 
405   /// CHECK-START: void TestDontOptimizeAcrossBlocks.exercise() constructor_fence_redundancy_elimination (after)
406   /// CHECK: <<NewInstance:l\d+>>     NewInstance
407   /// CHECK:                          ConstructorFence [<<NewInstance>>]
408   /// CHECK: <<NewInstance2:l\d+>>    NewInstance
409   /// CHECK-DAG:                      ConstructorFence [<<NewInstance2>>]
410   /// CHECK-NOT:                      ConstructorFence
411   /// CHECK-DAG:                      StaticFieldSet [<<External:l\d+>>,<<NewInstance>>]
412   /// CHECK-DAG:                      StaticFieldSet [<<External2:l\d+>>,<<NewInstance2>>]
413   @Override
exercise()414   public void exercise() {
415     Base b = new Base();
416 
417     // Do not move constructor fence across this block, even though 'b' is not published yet.
418     if (test) {
419       external = null;
420     }
421 
422     Base b2 = new Base();
423     external = b2;
424     external3 = b;
425   }
426 
427   @Override
check()428   public void check() {
429     Assert.stringEquals("false", test);
430     Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external);
431     Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external3);
432   }
433 }
434 
435 class TestDontOptimizeAcrossEscape {
436   // Prevent constant folding.
437   static boolean test;
438 
439   static Object external;
440   static Object external2;
441   static Object external3;
442   static Object external4;
443 
444   static class Invoke implements Test {
445     /// CHECK-START: void TestDontOptimizeAcrossEscape$Invoke.exercise() constructor_fence_redundancy_elimination (before)
446     /// CHECK: <<NewInstance:l\d+>>     NewInstance
447     /// CHECK:                          ConstructorFence [<<NewInstance>>]
448     /// CHECK:                          InvokeStaticOrDirect
449     /// CHECK: <<NewInstance2:l\d+>>    NewInstance
450     /// CHECK-DAG:                      ConstructorFence [<<NewInstance2>>]
451     /// CHECK-NOT:                      ConstructorFence
452 
453     /// CHECK-START: void TestDontOptimizeAcrossEscape$Invoke.exercise() constructor_fence_redundancy_elimination (after)
454     /// CHECK: <<NewInstance:l\d+>>     NewInstance
455     /// CHECK:                          ConstructorFence [<<NewInstance>>]
456     /// CHECK:                          InvokeStaticOrDirect
457     /// CHECK: <<NewInstance2:l\d+>>    NewInstance
458     /// CHECK-DAG:                      ConstructorFence [<<NewInstance2>>]
459     /// CHECK-NOT:                      ConstructorFence
460     @Override
exercise()461     public void exercise() {
462       Base b = new Base();
463       // Do not optimize across invokes into which the fence target escapes.
464       invoke$noinline$(b);
465 
466       Base b2 = new Base();
467 
468       // Do not LSE-eliminate b,b2
469       external = b;
470       external2 = b2;
471     }
472 
invoke$noinline$(Object b)473     private static void invoke$noinline$(Object b) {
474       // Even though 'b' does not escape this method, we conservatively assume all parameters
475       // of an invoke escape.
476     }
477 
478     @Override
check()479     public void check() {
480       Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external);
481       Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external2);
482     }
483   }
484 
485   public static Object[] array = new Object[3];
486   static Base base = new Base();
487 
488   static class InstanceEscaper {
489     public Object holder;
490 
491     @Override
toString()492     public String toString() {
493       return getClass().getName() + "(" + baseString() + ")";
494     }
495 
baseString()496     protected String baseString() {
497       return String.format("holder: %s", Main.valueToString(holder));
498     }
499   }
500 
501   static InstanceEscaper instanceEscaper = new InstanceEscaper();
502 
503   static class StoreIput implements Test {
504     /// CHECK-START: void TestDontOptimizeAcrossEscape$StoreIput.exercise() constructor_fence_redundancy_elimination (before)
505     /// CHECK: <<NewInstance:l\d+>>     NewInstance
506     /// CHECK:                          ConstructorFence [<<NewInstance>>]
507     /// CHECK-DAG:                      InstanceFieldSet
508     /// CHECK: <<NewInstance2:l\d+>>    NewInstance
509     /// CHECK-DAG:                      ConstructorFence [<<NewInstance2>>]
510     /// CHECK-NOT:                      ConstructorFence
511 
512     /// CHECK-START: void TestDontOptimizeAcrossEscape$StoreIput.exercise() constructor_fence_redundancy_elimination (after)
513     /// CHECK-DAG: <<NewInstance:l\d+>>   NewInstance
514     /// CHECK:                            ConstructorFence [<<NewInstance>>]
515     /// CHECK-DAG: <<NewInstance2:l\d+>>  NewInstance
516     /// CHECK-DAG:                        ConstructorFence [<<NewInstance2>>]
517     /// CHECK-NOT:                        ConstructorFence
518     @Override
exercise()519     public void exercise() {
520       Base b = new Base();
521 
522       // A store of 'b' into another instance will publish 'b'.
523       instanceEscaper.holder = b;
524 
525       // Do not remove any constructor fences above.
526       Base b2 = new Base();
527 
528       // Do not LSE-eliminate b,b2
529       external3 = b;
530       external4 = b2;
531     }
532 
533     @Override
check()534     public void check() {
535       Assert.stringEquals(
536           "TestDontOptimizeAcrossEscape$InstanceEscaper(holder: Base(w0: 0, w1: 0, w2: 0, w3: 0))",
537           instanceEscaper);
538       Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external3);
539       Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external4);
540     }
541   }
542 
543   static class StoreAput implements Test {
544     /// CHECK-START: void TestDontOptimizeAcrossEscape$StoreAput.exercise() constructor_fence_redundancy_elimination (before)
545     /// CHECK: <<NewInstance:l\d+>>     NewInstance
546     /// CHECK:                          ConstructorFence [<<NewInstance>>]
547     /// CHECK-DAG:                      ArraySet
548     /// CHECK: <<NewInstance2:l\d+>>    NewInstance
549     /// CHECK-DAG:                      ConstructorFence [<<NewInstance2>>]
550     /// CHECK-NOT:                      ConstructorFence
551 
552     /// CHECK-START: void TestDontOptimizeAcrossEscape$StoreAput.exercise() constructor_fence_redundancy_elimination (after)
553     /// CHECK-DAG: <<NewInstance:l\d+>>   NewInstance
554     /// CHECK:                            ConstructorFence [<<NewInstance>>]
555     /// CHECK-DAG: <<NewInstance2:l\d+>>  NewInstance
556     /// CHECK-DAG:                        ConstructorFence [<<NewInstance2>>]
557     /// CHECK-NOT:                        ConstructorFence
558     @Override
exercise()559     public void exercise() {
560       Base b = new Base();
561 
562       // A store of 'b' into another array will publish 'b'.
563       array[0] = b;  // aput
564 
565       // Do not remove any constructor fences above.
566       Base b2 = new Base();
567 
568       // Do not LSE-eliminate b,b2
569       external3 = b;
570       external4 = b2;
571     }
572 
573     @Override
check()574     public void check() {
575       Assert.stringEquals("[Base(w0: 0, w1: 0, w2: 0, w3: 0),<null>,<null>]", array);
576       Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external3);
577       Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external4);
578     }
579   }
580 
581   static class StoreSput implements Test {
582     /// CHECK-START: void TestDontOptimizeAcrossEscape$StoreSput.exercise() constructor_fence_redundancy_elimination (before)
583     /// CHECK: <<NewInstance:l\d+>>     NewInstance
584     /// CHECK:                          ConstructorFence [<<NewInstance>>]
585     /// CHECK-DAG:                      StaticFieldSet
586     /// CHECK: <<NewInstance2:l\d+>>    NewInstance
587     /// CHECK-DAG:                      ConstructorFence [<<NewInstance2>>]
588     /// CHECK-NOT:                      ConstructorFence
589 
590     /// CHECK-START: void TestDontOptimizeAcrossEscape$StoreSput.exercise() constructor_fence_redundancy_elimination (after)
591     /// CHECK-DAG: <<NewInstance:l\d+>>   NewInstance
592     /// CHECK:                            ConstructorFence [<<NewInstance>>]
593     /// CHECK-DAG: <<NewInstance2:l\d+>>  NewInstance
594     /// CHECK-DAG:                        ConstructorFence [<<NewInstance2>>]
595     /// CHECK-NOT:                        ConstructorFence
596     @Override
exercise()597     public void exercise() {
598       Base b = new Base();
599 
600       // A store of 'b' into a static will publish 'b'.
601       external = b;
602 
603       // Do not remove any constructor fences above.
604       Base b2 = new Base();
605 
606       // Do not LSE-eliminate b,b2
607       external3 = b;
608       external4 = b2;
609     }
610 
611     @Override
check()612     public void check() {
613       Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external);
614       Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external3);
615       Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external4);
616     }
617   }
618 
619   static class Deopt implements Test {
620     /// CHECK-START: void TestDontOptimizeAcrossEscape$Deopt.exercise() constructor_fence_redundancy_elimination (before)
621     /// CHECK: <<NewInstance:l\d+>>     NewInstance
622     /// CHECK:                          ConstructorFence [<<NewInstance>>]
623     /// CHECK-DAG:                      Deoptimize
624     /// CHECK: <<NewInstance2:l\d+>>    NewInstance
625     /// CHECK-DAG:                      ConstructorFence [<<NewInstance2>>]
626     /// CHECK-NOT:                      ConstructorFence
627 
628     /// CHECK-START: void TestDontOptimizeAcrossEscape$Deopt.exercise() constructor_fence_redundancy_elimination (after)
629     /// CHECK-DAG: <<NewInstance:l\d+>>   NewInstance
630     /// CHECK:                            ConstructorFence [<<NewInstance>>]
631     /// CHECK-DAG: <<NewInstance2:l\d+>>  NewInstance
632     /// CHECK-DAG:                        ConstructorFence [<<NewInstance2>>]
633     /// CHECK-NOT:                        ConstructorFence
634     @Override
exercise()635     public void exercise() {
636       Base b = new Base();
637 
638       // An array access generates a Deopt to avoid doing bounds check.
639       array[0] = external;  // aput
640       array[1] = external;  // aput
641       array[2] = external;  // aput
642 
643       // Do not remove any constructor fences above.
644       Base b2 = new Base();
645 
646       // Do not LSE-eliminate b,b2
647       external3 = b;
648       external4 = b2;
649     }
650 
651     @Override
check()652     public void check() {
653       Assert.stringEquals("[Base(w0: 0, w1: 0, w2: 0, w3: 0),"
654               + "Base(w0: 0, w1: 0, w2: 0, w3: 0),"
655               + "Base(w0: 0, w1: 0, w2: 0, w3: 0)]",
656           array);
657       Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external3);
658       Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external4);
659     }
660   }
661 
662   static class Select implements Test {
663     /// CHECK-START: void TestDontOptimizeAcrossEscape$Select.exercise() constructor_fence_redundancy_elimination (before)
664     /// CHECK: <<NewInstance:l\d+>>     NewInstance
665     /// CHECK:                          ConstructorFence [<<NewInstance>>]
666     /// CHECK-DAG:                      Select
667     /// CHECK: <<NewInstance2:l\d+>>    NewInstance
668     /// CHECK-DAG:                      ConstructorFence [<<NewInstance2>>]
669     /// CHECK-NOT:                      ConstructorFence
670 
671     /// CHECK-START: void TestDontOptimizeAcrossEscape$Select.exercise() constructor_fence_redundancy_elimination (after)
672     /// CHECK-DAG: <<NewInstance:l\d+>>   NewInstance
673     /// CHECK:                            ConstructorFence [<<NewInstance>>]
674     /// CHECK-DAG: <<NewInstance2:l\d+>>  NewInstance
675     /// CHECK-DAG:                        ConstructorFence [<<NewInstance2>>]
676     /// CHECK-NOT:                        ConstructorFence
677     @Override
exercise()678     public void exercise() {
679       Base b = new Base();
680 
681       boolean localTest = test;
682       Object localExternal = external3;
683 
684       // Selecting 'b' creates an alias, which we conservatively assume escapes immediately.
685       external = localTest ? b : localExternal;
686 
687       // Do not remove any constructor fences above.
688       Base b2 = new Base();
689 
690       // Do not LSE-eliminate b,b2
691       external3 = b;
692       external4 = b2;
693     }
694 
695     @Override
check()696     public void check() {
697       Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external);
698       Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external3);
699       Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external4);
700     }
701   }
702 
703   static class MakeBoundTypeTest implements Test {
704     public static Object makeBoundType;
705     public static Object makeBoundTypeSub;
706 
707     @Override
exercise()708     public void exercise() {
709       // Note: MakeBoundType is special and we have to call the constructor directly
710       // to prevent inlining it.
711       try {
712         makeBoundType = exerciseNewInstance(MakeBoundType.class, 123);
713         makeBoundTypeSub = exerciseNewInstance(MakeBoundTypeSub.class, 123);
714       } catch (Exception e) {
715         throw new RuntimeException(e);
716       }
717     }
718 
719     @Override
check()720     public void check() {
721       Assert.stringEquals(
722           "TestDontOptimizeAcrossEscape$MakeBoundTypeTest$MakeBoundType(abcdefgh: 123, x: 2)",
723           makeBoundType);
724       Assert.stringEquals(
725           "TestDontOptimizeAcrossEscape$MakeBoundTypeTest$MakeBoundTypeSub(abcdefgh: 123, x: 1)",
726           makeBoundTypeSub);
727     }
728 
729     // Make a new instance of 'klass'.
exerciseNewInstance(Class<T> klass, int params)730     private static <T> T exerciseNewInstance(Class<T> klass, int params) throws Exception {
731       return klass.cast(klass.getDeclaredConstructor(int.class).newInstance(params));
732     }
733 
734     /// CHECK-START: void TestDontOptimizeAcrossEscape$MakeBoundTypeTest$MakeBoundType.<init>(int) constructor_fence_redundancy_elimination (before)
735     /// CHECK-DAG: <<This:l\d+>>         ParameterValue
736     /// CHECK-DAG: <<NewInstance:l\d+>>  NewInstance
737     /// CHECK:                           ConstructorFence [<<NewInstance>>]
738     /// CHECK-DAG:                       BoundType
739     /// CHECK-DAG:                       ConstructorFence [<<This>>]
740     /// CHECK-NOT:                       ConstructorFence
741 
742     /// CHECK-START: void TestDontOptimizeAcrossEscape$MakeBoundTypeTest$MakeBoundType.<init>(int) constructor_fence_redundancy_elimination (after)
743     /// CHECK-DAG: <<This:l\d+>>         ParameterValue
744     /// CHECK-DAG: <<NewInstance:l\d+>>  NewInstance
745     /// CHECK:                           ConstructorFence [<<NewInstance>>]
746     /// CHECK-DAG:                       BoundType
747     /// CHECK-DAG:                       ConstructorFence [<<This>>]
748     /// CHECK-NOT:                       ConstructorFence
749     static class MakeBoundType {
750       final int abcdefgh;
751       int x;
752 
MakeBoundType(int param)753       MakeBoundType(int param) {
754         abcdefgh = param;
755 
756         Base b = new Base();
757         // constructor-fence(b)
758 
759         if (this instanceof MakeBoundTypeSub) {
760           // Create a "BoundType(this)" which prevents
761           // a merged constructor-fence(this, b)
762           x = 1;
763         } else {
764           x = 2;
765         }
766 
767         // publish(b).
768         external = b;
769 
770         // constructor-fence(this)
771       }
772 
773       @Override
toString()774       public String toString() {
775         return getClass().getName() + "(" + baseString() + ")";
776       }
777 
baseString()778       protected String baseString() {
779         return String.format("abcdefgh: %d, x: %d", abcdefgh, x);
780       }
781     }
782 
783     static class MakeBoundTypeSub extends MakeBoundType {
MakeBoundTypeSub(int xyz)784       MakeBoundTypeSub(int xyz) {
785         super(xyz);
786       }
787     }
788   }
789 }
790 
791 public class Main {
main(String[] args)792   public static void main(String[] args) throws Exception {
793     // Ensure that all of this code does not get optimized out into a no-op
794     // by actually running the code with reflection, then validating
795     // the result by asserting it against a string.
796     Class<? extends Test>[] testClasses = new Class[] {
797       TestOneFinal.class,
798       TestThreeFinal.class,
799       TestMultiAlloc.class,
800       TestThreeFinalTwice.class,
801       TestNonEscaping.Invoke.class,
802       TestNonEscaping.Store.class,
803       TestDontOptimizeAcrossBlocks.class,
804       TestDontOptimizeAcrossEscape.Invoke.class,
805       TestDontOptimizeAcrossEscape.StoreIput.class,
806       TestDontOptimizeAcrossEscape.StoreAput.class,
807       TestDontOptimizeAcrossEscape.StoreSput.class,
808       TestDontOptimizeAcrossEscape.Deopt.class,
809       TestDontOptimizeAcrossEscape.Select.class,
810       TestDontOptimizeAcrossEscape.MakeBoundTypeTest.class,
811     };
812 
813     for (Class<? extends Test> klass : testClasses) {
814       exerciseTestClass(klass);
815     }
816   }
817 
818   /**
819    * Invoke Test#exercise(), then Test#check().
820    * @throws AssertionError if test fails.
821    */
exerciseTestClass(Class<? extends Test> klass)822   private static void exerciseTestClass(Class<? extends Test> klass) throws Exception {
823     Test instance = klass.cast(klass.getDeclaredConstructor().newInstance());
824 
825     // Use reflection as a best-effort to avoid compiler optimizations (e.g. inlining).
826     instance.getClass().getDeclaredMethod("exercise").invoke(instance);
827     instance.getClass().getDeclaredMethod("check").invoke(instance);
828   }
829 
830   // Print an object, with special handling for array and null.
valueToString(Object val)831   public static String valueToString(Object val) {
832     if (val == null) {
833       return "<null>";
834     }
835     if (val.getClass().isArray()) {
836       String fmt = "[";
837       int length = Array.getLength(val);
838       for (int i = 0; i < length; ++i) {
839         Object arrayElement = Array.get(val, i);
840         fmt += valueToString(arrayElement);
841 
842         if (i != length - 1) {
843           fmt += ",";
844         }
845       }
846       fmt += "]";
847 
848       return fmt;
849     }
850 
851     return val.toString();
852   }
853 }
854