diff options
author | 2022-03-22 15:44:57 +0000 | |
---|---|---|
committer | 2022-07-08 10:00:33 +0000 | |
commit | c37e3a0a532fb89b62753d0478c1ba3c9fc87bb3 (patch) | |
tree | 93d519edbe9d5cf5a42e2ca9de15e5dee4c283f5 /compiler/optimizing | |
parent | d88c1499efe2f718f3cc1f45a3dc178471b22ce6 (diff) |
Add clinit checks at entry for some boot image methods.
Look at the list of preloaded classes to know whether the class will be
initialized. If it's not in the list, add explicit clinit checks at
entry. Update FixupStaticTrampolines to only update the entrypoint if it
is the resolution stub.
This adds two pages to current on-device boot classpath oat files.
Test: imgdiag
Bug: 162110941
Change-Id: Ic7b0b01a772444bc615b62cdb9305a1ef555c780
Diffstat (limited to 'compiler/optimizing')
-rw-r--r-- | compiler/optimizing/code_generator.h | 9 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_arm64.cc | 45 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_arm_vixl.cc | 54 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_x86.cc | 45 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_x86_64.cc | 45 |
5 files changed, 175 insertions, 23 deletions
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index b09219a2ed..43cb9862c2 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -37,6 +37,7 @@ #include "optimizing_compiler_stats.h" #include "read_barrier_option.h" #include "stack.h" +#include "subtype_check.h" #include "utils/assembler.h" #include "utils/label.h" @@ -60,6 +61,14 @@ static int64_t constexpr kPrimLongMax = INT64_C(0x7fffffffffffffff); static constexpr ReadBarrierOption kCompilerReadBarrierOption = kEmitCompilerReadBarrier ? kWithReadBarrier : kWithoutReadBarrier; +constexpr size_t status_lsb_position = SubtypeCheckBits::BitStructSizeOf(); +constexpr size_t status_byte_offset = + mirror::Class::StatusOffset().SizeValue() + (status_lsb_position / kBitsPerByte); +constexpr uint32_t shifted_visibly_initialized_value = + enum_cast<uint32_t>(ClassStatus::kVisiblyInitialized) << (status_lsb_position % kBitsPerByte); +constexpr uint32_t shifted_initializing_value = + enum_cast<uint32_t>(ClassStatus::kInitializing) << (status_lsb_position % kBitsPerByte); + class Assembler; class CodeGenerator; class CompilerOptions; diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 2f8c0b22e7..d859ac1a29 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -1233,6 +1233,46 @@ void CodeGeneratorARM64::MaybeIncrementHotness(bool is_frame_entry) { void CodeGeneratorARM64::GenerateFrameEntry() { MacroAssembler* masm = GetVIXLAssembler(); + + // Check if we need to generate the clinit check. We will jump to the + // resolution stub if the class is not initialized and the executing thread is + // not the thread initializing it. + // We do this before constructing the frame to get the correct stack trace if + // an exception is thrown. + if (GetGraph()->GetArtMethod() != nullptr && + GetCompilerOptions().ShouldCompileWithClinitCheck(GetGraph()->GetArtMethod())) { + UseScratchRegisterScope temps(masm); + vixl::aarch64::Label resolution; + + Register temp1 = temps.AcquireW(); + Register temp2 = temps.AcquireW(); + + // Check if we're visibly initialized. + + // We don't emit a read barrier here to save on code size. We rely on the + // resolution trampoline to do a suspend check before re-entering this code. + __ Ldr(temp1, MemOperand(kArtMethodRegister, ArtMethod::DeclaringClassOffset().Int32Value())); + __ Ldrb(temp2, HeapOperand(temp1, status_byte_offset)); + __ Cmp(temp2, shifted_visibly_initialized_value); + __ B(hs, &frame_entry_label_); + + // Check if we're initializing and the thread initializing is the one + // executing the code. + __ Cmp(temp2, shifted_initializing_value); + __ B(lo, &resolution); + + __ Ldr(temp1, HeapOperand(temp1, mirror::Class::ClinitThreadIdOffset().Int32Value())); + __ Ldr(temp2, MemOperand(tr, Thread::TidOffset<kArmPointerSize>().Int32Value())); + __ Cmp(temp1, temp2); + __ B(eq, &frame_entry_label_); + __ Bind(&resolution); + + // Jump to the resolution stub. + ThreadOffset64 entrypoint_offset = + GetThreadOffset<kArm64PointerSize>(kQuickQuickResolutionTrampoline); + __ Ldr(temp1.X(), MemOperand(tr, entrypoint_offset.Int32Value())); + __ Br(temp1.X()); + } __ Bind(&frame_entry_label_); bool do_overflow_check = @@ -1904,11 +1944,6 @@ void InstructionCodeGeneratorARM64::GenerateClassInitializationCheck(SlowPathCod Register class_reg) { UseScratchRegisterScope temps(GetVIXLAssembler()); Register temp = temps.AcquireW(); - constexpr size_t status_lsb_position = SubtypeCheckBits::BitStructSizeOf(); - const size_t status_byte_offset = - mirror::Class::StatusOffset().SizeValue() + (status_lsb_position / kBitsPerByte); - constexpr uint32_t shifted_visibly_initialized_value = - enum_cast<uint32_t>(ClassStatus::kVisiblyInitialized) << (status_lsb_position % kBitsPerByte); // CMP (immediate) is limited to imm12 or imm12<<12, so we would need to materialize // the constant 0xf0000000 for comparison with the full 32-bit field. To reduce the code diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 09fa598203..7e3ad349ad 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -2234,6 +2234,53 @@ void CodeGeneratorARMVIXL::GenerateFrameEntry() { bool skip_overflow_check = IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kArm); DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks()); + + // Check if we need to generate the clinit check. We will jump to the + // resolution stub if the class is not initialized and the executing thread is + // not the thread initializing it. + // We do this before constructing the frame to get the correct stack trace if + // an exception is thrown. + if (GetGraph()->GetArtMethod() != nullptr && + GetCompilerOptions().ShouldCompileWithClinitCheck(GetGraph()->GetArtMethod())) { + UseScratchRegisterScope temps(GetVIXLAssembler()); + vixl32::Label resolution; + + // Check if we're visibly initialized. + + vixl32::Register temp1 = temps.Acquire(); + // Use r4 as other temporary register. + DCHECK(!blocked_core_registers_[R4]); + DCHECK(!kCoreCalleeSaves.Includes(r4)); + vixl32::Register temp2 = r4; + for (vixl32::Register reg : kParameterCoreRegistersVIXL) { + DCHECK(!reg.Is(r4)); + } + + // We don't emit a read barrier here to save on code size. We rely on the + // resolution trampoline to do a suspend check before re-entering this code. + __ Ldr(temp1, MemOperand(kMethodRegister, ArtMethod::DeclaringClassOffset().Int32Value())); + __ Ldrb(temp2, MemOperand(temp1, status_byte_offset)); + __ Cmp(temp2, shifted_visibly_initialized_value); + __ B(cs, &frame_entry_label_); + + // Check if we're initializing and the thread initializing is the one + // executing the code. + __ Cmp(temp2, shifted_initializing_value); + __ B(lo, &resolution); + + __ Ldr(temp1, MemOperand(temp1, mirror::Class::ClinitThreadIdOffset().Int32Value())); + __ Ldr(temp2, MemOperand(tr, Thread::TidOffset<kArmPointerSize>().Int32Value())); + __ Cmp(temp1, temp2); + __ B(eq, &frame_entry_label_); + __ Bind(&resolution); + + // Jump to the resolution stub. + ThreadOffset32 entrypoint_offset = + GetThreadOffset<kArmPointerSize>(kQuickQuickResolutionTrampoline); + __ Ldr(temp1, MemOperand(tr, entrypoint_offset.Int32Value())); + __ Bx(temp1); + } + __ Bind(&frame_entry_label_); if (HasEmptyFrame()) { @@ -7622,12 +7669,7 @@ void InstructionCodeGeneratorARMVIXL::GenerateClassInitializationCheck( LoadClassSlowPathARMVIXL* slow_path, vixl32::Register class_reg) { UseScratchRegisterScope temps(GetVIXLAssembler()); vixl32::Register temp = temps.Acquire(); - constexpr size_t status_lsb_position = SubtypeCheckBits::BitStructSizeOf(); - constexpr uint32_t shifted_visibly_initialized_value = - enum_cast<uint32_t>(ClassStatus::kVisiblyInitialized) << status_lsb_position; - - const size_t status_offset = mirror::Class::StatusOffset().SizeValue(); - GetAssembler()->LoadFromOffset(kLoadWord, temp, class_reg, status_offset); + __ Ldrb(temp, MemOperand(class_reg, status_byte_offset)); __ Cmp(temp, shifted_visibly_initialized_value); __ B(lo, slow_path->GetEntryLabel()); __ Bind(slow_path->GetExitLabel()); diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 8c6b8027cd..57b2cacd19 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -1261,6 +1261,45 @@ void CodeGeneratorX86::MaybeIncrementHotness(bool is_frame_entry) { void CodeGeneratorX86::GenerateFrameEntry() { __ cfi().SetCurrentCFAOffset(kX86WordSize); // return address + + // Check if we need to generate the clinit check. We will jump to the + // resolution stub if the class is not initialized and the executing thread is + // not the thread initializing it. + // We do this before constructing the frame to get the correct stack trace if + // an exception is thrown. + if (GetGraph()->GetArtMethod() != nullptr && + GetCompilerOptions().ShouldCompileWithClinitCheck(GetGraph()->GetArtMethod())) { + NearLabel continue_execution, resolution; + // We'll use EBP as temporary. + __ pushl(EBP); + // Check if we're visibly initialized. + + // We don't emit a read barrier here to save on code size. We rely on the + // resolution trampoline to do a suspend check before re-entering this code. + __ movl(EBP, Address(kMethodRegisterArgument, ArtMethod::DeclaringClassOffset().Int32Value())); + __ cmpb(Address(EBP, status_byte_offset), Immediate(shifted_visibly_initialized_value)); + __ j(kAboveEqual, &continue_execution); + + // Check if we're initializing and the thread initializing is the one + // executing the code. + __ cmpb(Address(EBP, status_byte_offset), Immediate(shifted_initializing_value)); + __ j(kBelow, &resolution); + + __ movl(EBP, Address(EBP, mirror::Class::ClinitThreadIdOffset().Int32Value())); + __ fs()->cmpl(EBP, Address::Absolute(Thread::TidOffset<kX86PointerSize>().Int32Value())); + __ j(kEqual, &continue_execution); + __ Bind(&resolution); + + __ popl(EBP); + // Jump to the resolution stub. + ThreadOffset32 entrypoint_offset = + GetThreadOffset<kX86PointerSize>(kQuickQuickResolutionTrampoline); + __ fs()->jmp(Address::Absolute(entrypoint_offset)); + + __ Bind(&continue_execution); + __ popl(EBP); + } + __ Bind(&frame_entry_label_); bool skip_overflow_check = IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kX86); @@ -7233,12 +7272,6 @@ void InstructionCodeGeneratorX86::VisitClinitCheck(HClinitCheck* check) { void InstructionCodeGeneratorX86::GenerateClassInitializationCheck( SlowPathCode* slow_path, Register class_reg) { - constexpr size_t status_lsb_position = SubtypeCheckBits::BitStructSizeOf(); - const size_t status_byte_offset = - mirror::Class::StatusOffset().SizeValue() + (status_lsb_position / kBitsPerByte); - constexpr uint32_t shifted_visibly_initialized_value = - enum_cast<uint32_t>(ClassStatus::kVisiblyInitialized) << (status_lsb_position % kBitsPerByte); - __ cmpb(Address(class_reg, status_byte_offset), Immediate(shifted_visibly_initialized_value)); __ j(kBelow, slow_path->GetEntryLabel()); __ Bind(slow_path->GetExitLabel()); diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 511917a735..8a19cf2bd5 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -1653,6 +1653,45 @@ void CodeGeneratorX86_64::MaybeIncrementHotness(bool is_frame_entry) { void CodeGeneratorX86_64::GenerateFrameEntry() { __ cfi().SetCurrentCFAOffset(kX86_64WordSize); // return address + + // Check if we need to generate the clinit check. We will jump to the + // resolution stub if the class is not initialized and the executing thread is + // not the thread initializing it. + // We do this before constructing the frame to get the correct stack trace if + // an exception is thrown. + if (GetGraph()->GetArtMethod() != nullptr && + GetCompilerOptions().ShouldCompileWithClinitCheck(GetGraph()->GetArtMethod())) { + NearLabel resolution; + // Check if we're visibly initialized. + + // We don't emit a read barrier here to save on code size. We rely on the + // resolution trampoline to do a suspend check before re-entering this code. + __ movl(CpuRegister(TMP), + Address(CpuRegister(kMethodRegisterArgument), + ArtMethod::DeclaringClassOffset().Int32Value())); + __ cmpb(Address(CpuRegister(TMP), status_byte_offset), + Immediate(shifted_visibly_initialized_value)); + __ j(kAboveEqual, &frame_entry_label_); + + // Check if we're initializing and the thread initializing is the one + // executing the code. + __ cmpb(Address(CpuRegister(TMP), status_byte_offset), Immediate(shifted_initializing_value)); + __ j(kBelow, &resolution); + + __ movl(CpuRegister(TMP), + Address(CpuRegister(TMP), mirror::Class::ClinitThreadIdOffset().Int32Value())); + __ gs()->cmpl( + CpuRegister(TMP), + Address::Absolute(Thread::TidOffset<kX86_64PointerSize>().Int32Value(), /*no_rip=*/ true)); + __ j(kEqual, &frame_entry_label_); + __ Bind(&resolution); + + // Jump to the resolution stub. + ThreadOffset64 entrypoint_offset = + GetThreadOffset<kX86_64PointerSize>(kQuickQuickResolutionTrampoline); + __ gs()->jmp(Address::Absolute(entrypoint_offset, /*no_rip=*/ true)); + } + __ Bind(&frame_entry_label_); bool skip_overflow_check = IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kX86_64); @@ -6282,12 +6321,6 @@ void ParallelMoveResolverX86_64::RestoreScratch(int reg) { void InstructionCodeGeneratorX86_64::GenerateClassInitializationCheck( SlowPathCode* slow_path, CpuRegister class_reg) { - constexpr size_t status_lsb_position = SubtypeCheckBits::BitStructSizeOf(); - const size_t status_byte_offset = - mirror::Class::StatusOffset().SizeValue() + (status_lsb_position / kBitsPerByte); - constexpr uint32_t shifted_visibly_initialized_value = - enum_cast<uint32_t>(ClassStatus::kVisiblyInitialized) << (status_lsb_position % kBitsPerByte); - __ cmpb(Address(class_reg, status_byte_offset), Immediate(shifted_visibly_initialized_value)); __ j(kBelow, slow_path->GetEntryLabel()); __ Bind(slow_path->GetExitLabel()); |