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