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