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-IF: readBarrierType('baker')
40   ///   CHECK-NEXT:      add w<<BaseRegNum:\d+>>, {{w\d+}}, #0x8 (8)
41   ///   CHECK-NEXT:      adr lr, #+0x{{c|10}}
42   //    The following instruction (generated by
43   //    `art::arm64::CodeGeneratorARM64::EmitBakerReadBarrierCbnz`) checks the
44   //    Marking Register (X20) and goes into the Baker read barrier thunk if MR is
45   //    not null. The null offset (#+0x0) in the CBNZ instruction is a placeholder
46   //    for the offset to the Baker read barrier thunk (which is not yet set when
47   //    the CFG output is emitted).
48   ///   CHECK-NEXT:      cbnz x20, #+0x0
49   /// CHECK-ELSE:
50   ///   CHECK-NEXT:      add x<<BaseRegNum:\d+>>, {{x\d+}}, #0x8 (8)
51   /// CHECK-FI:
52   /// CHECK-NEXT:      ldar {{w\d+}}, [x<<BaseRegNum>>]
53 
test()54   public void test() {
55     // Continually check that reading field `foo.bar` throws a
56     // NullPointerException while allocating over 64 MiB of memory (with heap
57     // size limited to 16 MiB), in order to increase memory pressure and
58     // eventually trigger a concurrent garbage collection, which will start by
59     // putting the GC in marking mode and enable read barriers (when the
60     // Concurrent Copying collector is used).
61     for (int i = 0; i != 64 * 1024; ++i) {
62       allocateAtLeast1KiB();
63       try {
64         // Read volatile field `bar` of `foo`, which is null, and is expected
65         // to produce a NullPointerException. On ARM64, this is implemented as a
66         // load-acquire (LDAR instruction).
67         //
68         // When the Concurrent Copying GC is marking, read barriers are enabled
69         // and the field load executes code from a Baker read barrier thunk.
70         // On ARM64, there used to be a bug in this thunk for the load-acquire
71         // case, where an explicit null check was missing, triggering an
72         // unhandled SIGSEGV when trying to load the lock word from the volatile
73         // field (b/140507091).
74         Object foo_bar = foo.bar;
75       } catch (NullPointerException e) {
76         continue;
77       }
78       // We should not be here.
79       throw new Error("Expected NullPointerException");
80     }
81   }
82 
83   // Allocate at least 1 KiB of memory on the managed heap.
84   // Retain some allocated memory and release old allocations so that the
85   // garbage collector has something to do.
allocateAtLeast1KiB()86   public static void allocateAtLeast1KiB() {
87     memory[allocationIndex] = new Object[1024 / 4];
88     ++allocationIndex;
89     if (allocationIndex == memory.length) {
90       allocationIndex = 0;
91     }
92   }
93 
94   public static Object[] memory = new Object[1024];
95   public static int allocationIndex = 0;
96 
97   private Foo foo;
98 
99 }
100