1 /* 2 * Copyright (C) 2019 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 class Foo { 18 volatile Object bar; 19 } 20 21 public class Main { 22 main(String[] args)23 public static void main(String[] args) { 24 Main main = new Main(); 25 main.test(); 26 System.out.println("passed"); 27 } 28 29 // Check that no explicit null check is emitted for the field load of volatile 30 // field `Foo.bar` before entering the Baker read barrier thunk. 31 // 32 // Note: We cannot check the ARM64 assembly code of the Baker read barrier 33 // thunk code, as it is not emitted in the CFG output. 34 // 35 /// CHECK-START-ARM64: void Main.test() disassembly (after) 36 /// CHECK: <<Foo:l\d+>> InstanceFieldGet [{{l\d+}}] field_name:Main.foo field_type:Reference loop:<<Loop:B\d+>> 37 /// CHECK: NullCheck [<<Foo>>] dex_pc:<<PC:\d+>> loop:<<Loop>> 38 /// CHECK-NEXT: InstanceFieldGet [<<Foo>>] dex_pc:<<PC>> field_name:Foo.bar field_type:Reference loop:<<Loop>> 39 /// CHECK-NEXT: add w<<BaseRegNum:\d+>>, {{w\d+}}, #0x8 (8) 40 /// CHECK-NEXT: adr lr, #+0x{{c|10}} 41 // The following instruction (generated by 42 // `art::arm64::CodeGeneratorARM64::EmitBakerReadBarrierCbnz`) checks the 43 // Marking Register (X20) and goes into the Baker read barrier thunk if MR is 44 // not null. The null offset (#+0x0) in the CBNZ instruction is a placeholder 45 // for the offset to the Baker read barrier thunk (which is not yet set when 46 // the CFG output is emitted). 47 /// CHECK-NEXT: cbnz x20, #+0x0 48 /// CHECK-NEXT: ldar {{w\d+}}, [x<<BaseRegNum>>] 49 test()50 public void test() { 51 // Continually check that reading field `foo.bar` throws a 52 // NullPointerException while allocating over 64 MiB of memory (with heap 53 // size limited to 16 MiB), in order to increase memory pressure and 54 // eventually trigger a concurrent garbage collection, which will start by 55 // putting the GC in marking mode and enable read barriers (when the 56 // Concurrent Copying collector is used). 57 for (int i = 0; i != 64 * 1024; ++i) { 58 allocateAtLeast1KiB(); 59 try { 60 // Read volatile field `bar` of `foo`, which is null, and is expected 61 // to produce a NullPointerException. On ARM64, this is implemented as a 62 // load-acquire (LDAR instruction). 63 // 64 // When the Concurrent Copying GC is marking, read barriers are enabled 65 // and the field load executes code from a Baker read barrier thunk. 66 // On ARM64, there used to be a bug in this thunk for the load-acquire 67 // case, where an explicit null check was missing, triggering an 68 // unhandled SIGSEGV when trying to load the lock word from the volatile 69 // field (b/140507091). 70 Object foo_bar = foo.bar; 71 } catch (NullPointerException e) { 72 continue; 73 } 74 // We should not be here. 75 throw new Error("Expected NullPointerException"); 76 } 77 } 78 79 // Allocate at least 1 KiB of memory on the managed heap. 80 // Retain some allocated memory and release old allocations so that the 81 // garbage collector has something to do. allocateAtLeast1KiB()82 public static void allocateAtLeast1KiB() { 83 memory[allocationIndex] = new Object[1024 / 4]; 84 ++allocationIndex; 85 if (allocationIndex == memory.length) { 86 allocationIndex = 0; 87 } 88 } 89 90 public static Object[] memory = new Object[1024]; 91 public static int allocationIndex = 0; 92 93 private Foo foo; 94 95 } 96