1 /*
2  * Copyright (C) 2024 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 {
main(String[] args)18     public static void main(String[] args) {
19         $noinline$testHonorWriteBarrier();
20         $noinline$testDontSkipWriteBarrier();
21     }
22 
$noinline$testHonorWriteBarrier()23     public static void $noinline$testHonorWriteBarrier() {
24         String[] arr = {"Hello", "World"};
25         // We first run the gc to make sure the card is clean.
26         Runtime.getRuntime().gc();
27         // Continually call $noinline$testArraySetsHonorWriteBarrier while allocating over 64 MiB of
28         // memory (with heap size limited to 16 MiB), in order to increase memory pressure and
29         // eventually trigger a concurrent garbage collection, which will start by putting the GC in
30         // marking mode to trigger the bug.
31         for (int i = 0; i != 64 * 1024; ++i) {
32             $noinline$allocateAtLeast1KiB();
33             $noinline$testArraySetsHonorWriteBarrier(arr, "Universe");
34         }
35     }
36 
37     // When the bug was present, $noinline$testArraySetsHonorWriteBarrier would never set the card
38     // as dirty (which is incorrect). arr[1]'s' write barrier depends on arr[0]'s write barrier. The
39     // disappeared BoundType in prepare_for_register_allocation shouldn't skip marking the card
40     // dirty.
41 
42     /// CHECK-START: java.lang.String[] Main.$noinline$testArraySetsHonorWriteBarrier(java.lang.String[], java.lang.String) prepare_for_register_allocation (before)
43     /// CHECK: <<Null:l\d+>>   NullConstant
44     /// CHECK: <<BT:l\d+>>     BoundType [<<Null>>]
45     /// CHECK: ArraySet [<<arr:l\d+>>,<<index:i\d+>>,<<BT>>] value_can_be_null:true needs_type_check:false can_trigger_gc:false write_barrier_kind:DontEmit
46     /// CHECK: ArraySet value_can_be_null:true needs_type_check:false can_trigger_gc:false write_barrier_kind:EmitNotBeingReliedOn
47 
48     /// CHECK-START: java.lang.String[] Main.$noinline$testArraySetsHonorWriteBarrier(java.lang.String[], java.lang.String) prepare_for_register_allocation (after)
49     /// CHECK: <<Null:l\d+>>   NullConstant
50     /// CHECK: ArraySet [<<arr:l\d+>>,<<index:i\d+>>,<<Null>>] value_can_be_null:true needs_type_check:false can_trigger_gc:false write_barrier_kind:DontEmit
51     /// CHECK: ArraySet value_can_be_null:true needs_type_check:false can_trigger_gc:false write_barrier_kind:EmitNotBeingReliedOn
52 
53     /// CHECK-START: java.lang.String[] Main.$noinline$testArraySetsHonorWriteBarrier(java.lang.String[], java.lang.String) prepare_for_register_allocation (after)
54     /// CHECK-NOT: BoundType
55 
56     /// CHECK-START: java.lang.String[] Main.$noinline$testArraySetsHonorWriteBarrier(java.lang.String[], java.lang.String) disassembly (after)
57     /// CHECK: ArraySet value_can_be_null:true needs_type_check:false can_trigger_gc:false write_barrier_kind:DontEmit
58     /// CHECK: ArraySet value_can_be_null:true needs_type_check:false can_trigger_gc:false write_barrier_kind:EmitNotBeingReliedOn
$noinline$testArraySetsHonorWriteBarrier( String[] arr, String o2)59     private static java.lang.String[] $noinline$testArraySetsHonorWriteBarrier(
60             String[] arr, String o2) {
61         Object o = null;
62         arr[0] = (String) o;
63         arr[1] = o2;
64         return arr;
65     }
66 
$noinline$testDontSkipWriteBarrier()67     public static void $noinline$testDontSkipWriteBarrier() {
68         String[] arr = {"Hello", "World"};
69         // We first run the gc to make sure the card is clean.
70         Runtime.getRuntime().gc();
71         // Continually call $noinline$testArraySets while allocating over 64 MiB of memory (with
72         // heap size limited to 16 MiB), in order to increase memory pressure and eventually trigger
73         // a concurrent garbage collection, which will start by putting the GC in marking mode to
74         // trigger the bug.
75         for (int i = 0; i != 64 * 1024; ++i) {
76             $noinline$allocateAtLeast1KiB();
77             $noinline$testArraySetsDontSkipWriteBarrier(arr, null, "Universe");
78         }
79     }
80 
81     // When the bug was present, $noinline$testArraySetsDontSkipWriteBarrier would never set the
82     // card as dirty (which is incorrect). arr[1]'s write barrier depends on arr[0]'s write barrier.
83     // The code path should mark the card dirty without a null check on `o` in `arr[0] = o`.
84 
85     /// CHECK-START: java.lang.String[] Main.$noinline$testArraySetsDontSkipWriteBarrier(java.lang.String[], java.lang.String, java.lang.String) disassembly (after)
86     /// CHECK: ArraySet value_can_be_null:true needs_type_check:false can_trigger_gc:false write_barrier_kind:EmitBeingReliedOn
87     /// CHECK: ArraySet value_can_be_null:true needs_type_check:false can_trigger_gc:false write_barrier_kind:DontEmit
$noinline$testArraySetsDontSkipWriteBarrier( String[] arr, String o, String o2)88     private static java.lang.String[] $noinline$testArraySetsDontSkipWriteBarrier(
89             String[] arr, String o, String o2) {
90         arr[0] = o;
91         arr[1] = o2;
92         return arr;
93     }
94 
95     // Allocate at least 1 KiB of memory on the managed heap.
96     // Retain some allocated memory and release old allocations so that the
97     // garbage collector has something to do.
$noinline$allocateAtLeast1KiB()98     public static void $noinline$allocateAtLeast1KiB() {
99         memory[allocationIndex] = new Object[1024 / 4];
100         ++allocationIndex;
101         if (allocationIndex == memory.length) {
102             allocationIndex = 0;
103         }
104     }
105 
106     public static Object[] memory = new Object[1024];
107     public static int allocationIndex = 0;
108 }
109