blob: c3d55ce535662fffe4f9b5933e919b0c3f176f7c [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class Foo {
volatile Object bar;
}
public class Main {
public static void main(String[] args) {
Main main = new Main();
main.test();
System.out.println("passed");
}
// Check that no explicit null check is emitted for the field load of volatile
// field `Foo.bar` before entering the Baker read barrier thunk.
//
// Note: We cannot check the ARM64 assembly code of the Baker read barrier
// thunk code, as it is not emitted in the CFG output.
//
/// CHECK-START-ARM64: void Main.test() disassembly (after)
/// CHECK: <<Foo:l\d+>> InstanceFieldGet [{{l\d+}}] field_name:Main.foo field_type:Reference loop:<<Loop:B\d+>>
/// CHECK: NullCheck [<<Foo>>] dex_pc:<<PC:\d+>> loop:<<Loop>>
/// CHECK-NEXT: InstanceFieldGet [<<Foo>>] dex_pc:<<PC>> field_name:Foo.bar field_type:Reference loop:<<Loop>>
/// CHECK-IF: readBarrierType('baker')
/// CHECK-NEXT: add w<<BaseRegNum:\d+>>, {{w\d+}}, #0x8 (8)
/// CHECK-NEXT: adr lr, #+0x{{c|10}}
// The following instruction (generated by
// `art::arm64::CodeGeneratorARM64::EmitBakerReadBarrierCbnz`) checks the
// Marking Register (X20) and goes into the Baker read barrier thunk if MR is
// not null. The null offset (#+0x0) in the CBNZ instruction is a placeholder
// for the offset to the Baker read barrier thunk (which is not yet set when
// the CFG output is emitted).
/// CHECK-NEXT: cbnz x20, #+0x0
/// CHECK-ELSE:
/// CHECK-NEXT: add x<<BaseRegNum:\d+>>, {{x\d+}}, #0x8 (8)
/// CHECK-FI:
/// CHECK-NEXT: ldar {{w\d+}}, [x<<BaseRegNum>>]
public void test() {
// Continually check that reading field `foo.bar` throws a
// NullPointerException while allocating over 64 MiB of memory (with heap
// size limited to 16 MiB), in order to increase memory pressure and
// eventually trigger a concurrent garbage collection, which will start by
// putting the GC in marking mode and enable read barriers (when the
// Concurrent Copying collector is used).
for (int i = 0; i != 64 * 1024; ++i) {
allocateAtLeast1KiB();
try {
// Read volatile field `bar` of `foo`, which is null, and is expected
// to produce a NullPointerException. On ARM64, this is implemented as a
// load-acquire (LDAR instruction).
//
// When the Concurrent Copying GC is marking, read barriers are enabled
// and the field load executes code from a Baker read barrier thunk.
// On ARM64, there used to be a bug in this thunk for the load-acquire
// case, where an explicit null check was missing, triggering an
// unhandled SIGSEGV when trying to load the lock word from the volatile
// field (b/140507091).
Object foo_bar = foo.bar;
} catch (NullPointerException e) {
continue;
}
// We should not be here.
throw new Error("Expected NullPointerException");
}
}
// Allocate at least 1 KiB of memory on the managed heap.
// Retain some allocated memory and release old allocations so that the
// garbage collector has something to do.
public static void allocateAtLeast1KiB() {
memory[allocationIndex] = new Object[1024 / 4];
++allocationIndex;
if (allocationIndex == memory.length) {
allocationIndex = 0;
}
}
public static Object[] memory = new Object[1024];
public static int allocationIndex = 0;
private Foo foo;
}