diff options
23 files changed, 580 insertions, 316 deletions
diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h index 26ab281741..7f2e1931d0 100644 --- a/compiler/elf_builder.h +++ b/compiler/elf_builder.h @@ -780,9 +780,9 @@ class ElfBuilder FINAL { EF_MIPS_PIC | EF_MIPS_CPIC | EF_MIPS_ABI_O32 | - features->AsMipsInstructionSetFeatures()->IsR6() - ? EF_MIPS_ARCH_32R6 - : EF_MIPS_ARCH_32R2); + (features->AsMipsInstructionSetFeatures()->IsR6() + ? EF_MIPS_ARCH_32R6 + : EF_MIPS_ARCH_32R2)); break; } case kMips64: { diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index b9466ba212..83b4705302 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -430,7 +430,9 @@ class ReadBarrierMarkSlowPathARM : public SlowPathCode { instruction_->IsLoadClass() || instruction_->IsLoadString() || instruction_->IsInstanceOf() || - instruction_->IsCheckCast()) + instruction_->IsCheckCast() || + ((instruction_->IsInvokeStaticOrDirect() || instruction_->IsInvokeVirtual()) && + instruction_->GetLocations()->Intrinsified())) << "Unexpected instruction in read barrier marking slow path: " << instruction_->DebugName(); @@ -493,8 +495,12 @@ class ReadBarrierForHeapReferenceSlowPathARM : public SlowPathCode { Register reg_out = out_.AsRegister<Register>(); DCHECK(locations->CanCall()); DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out)); - DCHECK(!instruction_->IsInvoke() || - (instruction_->IsInvokeStaticOrDirect() && + DCHECK(instruction_->IsInstanceFieldGet() || + instruction_->IsStaticFieldGet() || + instruction_->IsArrayGet() || + instruction_->IsInstanceOf() || + instruction_->IsCheckCast() || + ((instruction_->IsInvokeStaticOrDirect() || instruction_->IsInvokeVirtual()) && instruction_->GetLocations()->Intrinsified())) << "Unexpected instruction in read barrier for heap reference slow path: " << instruction_->DebugName(); @@ -507,7 +513,7 @@ class ReadBarrierForHeapReferenceSlowPathARM : public SlowPathCode { // introduce a copy of it, `index`. Location index = index_; if (index_.IsValid()) { - // Handle `index_` for HArrayGet and intrinsic UnsafeGetObject. + // Handle `index_` for HArrayGet and UnsafeGetObject/UnsafeGetObjectVolatile intrinsics. if (instruction_->IsArrayGet()) { // Compute the actual memory offset and store it in `index`. Register index_reg = index_.AsRegister<Register>(); @@ -555,7 +561,11 @@ class ReadBarrierForHeapReferenceSlowPathARM : public SlowPathCode { "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); __ AddConstant(index_reg, index_reg, offset_); } else { - DCHECK(instruction_->IsInvoke()); + // In the case of the UnsafeGetObject/UnsafeGetObjectVolatile + // intrinsics, `index_` is not shifted by a scale factor of 2 + // (as in the case of ArrayGet), as it is actually an offset + // to an object field within an object. + DCHECK(instruction_->IsInvoke()) << instruction_->DebugName(); DCHECK(instruction_->GetLocations()->Intrinsified()); DCHECK((instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObject) || (instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile)) @@ -6203,8 +6213,9 @@ void CodeGeneratorARM::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instr // /* HeapReference<Object> */ ref = *(obj + offset) Location no_index = Location::NoLocation(); + ScaleFactor no_scale_factor = TIMES_1; GenerateReferenceLoadWithBakerReadBarrier( - instruction, ref, obj, offset, no_index, temp, needs_null_check); + instruction, ref, obj, offset, no_index, no_scale_factor, temp, needs_null_check); } void CodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction, @@ -6217,10 +6228,14 @@ void CodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier(HInstruction* instr DCHECK(kEmitCompilerReadBarrier); DCHECK(kUseBakerReadBarrier); + static_assert( + sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), + "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); // /* HeapReference<Object> */ ref = // *(obj + data_offset + index * sizeof(HeapReference<Object>)) + ScaleFactor scale_factor = TIMES_4; GenerateReferenceLoadWithBakerReadBarrier( - instruction, ref, obj, data_offset, index, temp, needs_null_check); + instruction, ref, obj, data_offset, index, scale_factor, temp, needs_null_check); } void CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction, @@ -6228,6 +6243,7 @@ void CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* i Register obj, uint32_t offset, Location index, + ScaleFactor scale_factor, Location temp, bool needs_null_check) { DCHECK(kEmitCompilerReadBarrier); @@ -6282,17 +6298,22 @@ void CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* i // The actual reference load. if (index.IsValid()) { - static_assert( - sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), - "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); - // /* HeapReference<Object> */ ref = - // *(obj + offset + index * sizeof(HeapReference<Object>)) + // Load types involving an "index": ArrayGet and + // UnsafeGetObject/UnsafeGetObjectVolatile intrinsics. + // /* HeapReference<Object> */ ref = *(obj + offset + (index << scale_factor)) if (index.IsConstant()) { size_t computed_offset = - (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + offset; + (index.GetConstant()->AsIntConstant()->GetValue() << scale_factor) + offset; __ LoadFromOffset(kLoadWord, ref_reg, obj, computed_offset); } else { - __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); + // Handle the special case of the + // UnsafeGetObject/UnsafeGetObjectVolatile intrinsics, which use + // a register pair as index ("long offset"), of which only the low + // part contains data. + Register index_reg = index.IsRegisterPair() + ? index.AsRegisterPairLow<Register>() + : index.AsRegister<Register>(); + __ add(IP, obj, ShifterOperand(index_reg, LSL, scale_factor)); __ LoadFromOffset(kLoadWord, ref_reg, IP, offset); } } else { diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index 4fce5af8e6..477c4f18c1 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -472,6 +472,16 @@ class CodeGeneratorARM : public CodeGenerator { Location index, Location temp, bool needs_null_check); + // Factored implementation used by GenerateFieldLoadWithBakerReadBarrier + // and GenerateArrayLoadWithBakerReadBarrier. + void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction, + Location ref, + Register obj, + uint32_t offset, + Location index, + ScaleFactor scale_factor, + Location temp, + bool needs_null_check); // Generate a read barrier for a heap reference within `instruction` // using a slow path. @@ -527,16 +537,6 @@ class CodeGeneratorARM : public CodeGenerator { void GenerateExplicitNullCheck(HNullCheck* instruction); private: - // Factored implementation of GenerateFieldLoadWithBakerReadBarrier - // and GenerateArrayLoadWithBakerReadBarrier. - void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction, - Location ref, - Register obj, - uint32_t offset, - Location index, - Location temp, - bool needs_null_check); - Register GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke, Register temp); using Uint32ToLiteralMap = ArenaSafeMap<uint32_t, Literal*>; diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 4692a4a876..07d5e50c6b 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -598,7 +598,9 @@ class ReadBarrierMarkSlowPathARM64 : public SlowPathCodeARM64 { instruction_->IsLoadClass() || instruction_->IsLoadString() || instruction_->IsInstanceOf() || - instruction_->IsCheckCast()) + instruction_->IsCheckCast() || + ((instruction_->IsInvokeStaticOrDirect() || instruction_->IsInvokeVirtual()) && + instruction_->GetLocations()->Intrinsified())) << "Unexpected instruction in read barrier marking slow path: " << instruction_->DebugName(); @@ -661,8 +663,12 @@ class ReadBarrierForHeapReferenceSlowPathARM64 : public SlowPathCodeARM64 { Primitive::Type type = Primitive::kPrimNot; DCHECK(locations->CanCall()); DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(out_.reg())); - DCHECK(!instruction_->IsInvoke() || - (instruction_->IsInvokeStaticOrDirect() && + DCHECK(instruction_->IsInstanceFieldGet() || + instruction_->IsStaticFieldGet() || + instruction_->IsArrayGet() || + instruction_->IsInstanceOf() || + instruction_->IsCheckCast() || + ((instruction_->IsInvokeStaticOrDirect() || instruction_->IsInvokeVirtual()) && instruction_->GetLocations()->Intrinsified())) << "Unexpected instruction in read barrier for heap reference slow path: " << instruction_->DebugName(); @@ -680,7 +686,7 @@ class ReadBarrierForHeapReferenceSlowPathARM64 : public SlowPathCodeARM64 { // introduce a copy of it, `index`. Location index = index_; if (index_.IsValid()) { - // Handle `index_` for HArrayGet and intrinsic UnsafeGetObject. + // Handle `index_` for HArrayGet and UnsafeGetObject/UnsafeGetObjectVolatile intrinsics. if (instruction_->IsArrayGet()) { // Compute the actual memory offset and store it in `index`. Register index_reg = RegisterFrom(index_, Primitive::kPrimInt); @@ -728,7 +734,11 @@ class ReadBarrierForHeapReferenceSlowPathARM64 : public SlowPathCodeARM64 { "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); __ Add(index_reg, index_reg, Operand(offset_)); } else { - DCHECK(instruction_->IsInvoke()); + // In the case of the UnsafeGetObject/UnsafeGetObjectVolatile + // intrinsics, `index_` is not shifted by a scale factor of 2 + // (as in the case of ArrayGet), as it is actually an offset + // to an object field within an object. + DCHECK(instruction_->IsInvoke()) << instruction_->DebugName(); DCHECK(instruction_->GetLocations()->Intrinsified()); DCHECK((instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObject) || (instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile)) @@ -5102,8 +5112,16 @@ void CodeGeneratorARM64::GenerateFieldLoadWithBakerReadBarrier(HInstruction* ins // /* HeapReference<Object> */ ref = *(obj + offset) Location no_index = Location::NoLocation(); - GenerateReferenceLoadWithBakerReadBarrier( - instruction, ref, obj, offset, no_index, temp, needs_null_check, use_load_acquire); + size_t no_scale_factor = 0U; + GenerateReferenceLoadWithBakerReadBarrier(instruction, + ref, + obj, + offset, + no_index, + no_scale_factor, + temp, + needs_null_check, + use_load_acquire); } void CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction, @@ -5120,10 +5138,21 @@ void CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier(HInstruction* ins // never use Load-Acquire instructions on ARM64. const bool use_load_acquire = false; + static_assert( + sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), + "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); // /* HeapReference<Object> */ ref = // *(obj + data_offset + index * sizeof(HeapReference<Object>)) - GenerateReferenceLoadWithBakerReadBarrier( - instruction, ref, obj, data_offset, index, temp, needs_null_check, use_load_acquire); + size_t scale_factor = Primitive::ComponentSizeShift(Primitive::kPrimNot); + GenerateReferenceLoadWithBakerReadBarrier(instruction, + ref, + obj, + data_offset, + index, + scale_factor, + temp, + needs_null_check, + use_load_acquire); } void CodeGeneratorARM64::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction, @@ -5131,15 +5160,16 @@ void CodeGeneratorARM64::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* vixl::Register obj, uint32_t offset, Location index, + size_t scale_factor, Register temp, bool needs_null_check, bool use_load_acquire) { DCHECK(kEmitCompilerReadBarrier); DCHECK(kUseBakerReadBarrier); - // If `index` is a valid location, then we are emitting an array - // load, so we shouldn't be using a Load Acquire instruction. - // In other words: `index.IsValid()` => `!use_load_acquire`. - DCHECK(!index.IsValid() || !use_load_acquire); + // If we are emitting an array load, we should not be using a + // Load Acquire instruction. In other words: + // `instruction->IsArrayGet()` => `!use_load_acquire`. + DCHECK(!instruction->IsArrayGet() || !use_load_acquire); MacroAssembler* masm = GetVIXLAssembler(); UseScratchRegisterScope temps(masm); @@ -5196,20 +5226,33 @@ void CodeGeneratorARM64::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* // The actual reference load. if (index.IsValid()) { - static_assert( - sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), - "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); - // /* HeapReference<Object> */ ref = - // *(obj + offset + index * sizeof(HeapReference<Object>)) - const size_t shift_amount = Primitive::ComponentSizeShift(type); - if (index.IsConstant()) { - uint32_t computed_offset = offset + (Int64ConstantFrom(index) << shift_amount); - Load(type, ref_reg, HeapOperand(obj, computed_offset)); + // Load types involving an "index". + if (use_load_acquire) { + // UnsafeGetObjectVolatile intrinsic case. + // Register `index` is not an index in an object array, but an + // offset to an object reference field within object `obj`. + DCHECK(instruction->IsInvoke()) << instruction->DebugName(); + DCHECK(instruction->GetLocations()->Intrinsified()); + DCHECK(instruction->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile) + << instruction->AsInvoke()->GetIntrinsic(); + DCHECK_EQ(offset, 0U); + DCHECK_EQ(scale_factor, 0U); + DCHECK_EQ(needs_null_check, 0U); + // /* HeapReference<Object> */ ref = *(obj + index) + MemOperand field = HeapOperand(obj, XRegisterFrom(index)); + LoadAcquire(instruction, ref_reg, field, /* needs_null_check */ false); } else { - temp2 = temps.AcquireW(); - __ Add(temp2, obj, offset); - Load(type, ref_reg, HeapOperand(temp2, XRegisterFrom(index), LSL, shift_amount)); - temps.Release(temp2); + // ArrayGet and UnsafeGetObject intrinsics cases. + // /* HeapReference<Object> */ ref = *(obj + offset + (index << scale_factor)) + if (index.IsConstant()) { + uint32_t computed_offset = offset + (Int64ConstantFrom(index) << scale_factor); + Load(type, ref_reg, HeapOperand(obj, computed_offset)); + } else { + temp2 = temps.AcquireW(); + __ Add(temp2, obj, offset); + Load(type, ref_reg, HeapOperand(temp2, XRegisterFrom(index), LSL, scale_factor)); + temps.Release(temp2); + } } } else { // /* HeapReference<Object> */ ref = *(obj + offset) diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index e6fd336be7..d4bf695602 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -531,6 +531,17 @@ class CodeGeneratorARM64 : public CodeGenerator { Location index, vixl::Register temp, bool needs_null_check); + // Factored implementation used by GenerateFieldLoadWithBakerReadBarrier + // and GenerateArrayLoadWithBakerReadBarrier. + void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction, + Location ref, + vixl::Register obj, + uint32_t offset, + Location index, + size_t scale_factor, + vixl::Register temp, + bool needs_null_check, + bool use_load_acquire); // Generate a read barrier for a heap reference within `instruction` // using a slow path. @@ -586,17 +597,6 @@ class CodeGeneratorARM64 : public CodeGenerator { void GenerateExplicitNullCheck(HNullCheck* instruction); private: - // Factored implementation of GenerateFieldLoadWithBakerReadBarrier - // and GenerateArrayLoadWithBakerReadBarrier. - void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction, - Location ref, - vixl::Register obj, - uint32_t offset, - Location index, - vixl::Register temp, - bool needs_null_check, - bool use_load_acquire); - using Uint64ToLiteralMap = ArenaSafeMap<uint64_t, vixl::Literal<uint64_t>*>; using Uint32ToLiteralMap = ArenaSafeMap<uint32_t, vixl::Literal<uint32_t>*>; using MethodToLiteralMap = ArenaSafeMap<MethodReference, diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 52868f4b2e..af10bad6cc 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -448,7 +448,9 @@ class ReadBarrierMarkSlowPathX86 : public SlowPathCode { instruction_->IsLoadClass() || instruction_->IsLoadString() || instruction_->IsInstanceOf() || - instruction_->IsCheckCast()) + instruction_->IsCheckCast() || + ((instruction_->IsInvokeStaticOrDirect() || instruction_->IsInvokeVirtual()) && + instruction_->GetLocations()->Intrinsified())) << "Unexpected instruction in read barrier marking slow path: " << instruction_->DebugName(); @@ -511,8 +513,12 @@ class ReadBarrierForHeapReferenceSlowPathX86 : public SlowPathCode { Register reg_out = out_.AsRegister<Register>(); DCHECK(locations->CanCall()); DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out)); - DCHECK(!instruction_->IsInvoke() || - (instruction_->IsInvokeStaticOrDirect() && + DCHECK(instruction_->IsInstanceFieldGet() || + instruction_->IsStaticFieldGet() || + instruction_->IsArrayGet() || + instruction_->IsInstanceOf() || + instruction_->IsCheckCast() || + ((instruction_->IsInvokeStaticOrDirect() || instruction_->IsInvokeVirtual()) && instruction_->GetLocations()->Intrinsified())) << "Unexpected instruction in read barrier for heap reference slow path: " << instruction_->DebugName(); @@ -525,7 +531,7 @@ class ReadBarrierForHeapReferenceSlowPathX86 : public SlowPathCode { // introduce a copy of it, `index`. Location index = index_; if (index_.IsValid()) { - // Handle `index_` for HArrayGet and intrinsic UnsafeGetObject. + // Handle `index_` for HArrayGet and UnsafeGetObject/UnsafeGetObjectVolatile intrinsics. if (instruction_->IsArrayGet()) { // Compute the actual memory offset and store it in `index`. Register index_reg = index_.AsRegister<Register>(); @@ -573,7 +579,11 @@ class ReadBarrierForHeapReferenceSlowPathX86 : public SlowPathCode { "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); __ AddImmediate(index_reg, Immediate(offset_)); } else { - DCHECK(instruction_->IsInvoke()); + // In the case of the UnsafeGetObject/UnsafeGetObjectVolatile + // intrinsics, `index_` is not shifted by a scale factor of 2 + // (as in the case of ArrayGet), as it is actually an offset + // to an object field within an object. + DCHECK(instruction_->IsInvoke()) << instruction_->DebugName(); DCHECK(instruction_->GetLocations()->Intrinsified()); DCHECK((instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObject) || (instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile)) @@ -6977,6 +6987,9 @@ void CodeGeneratorX86::GenerateArrayLoadWithBakerReadBarrier(HInstruction* instr DCHECK(kEmitCompilerReadBarrier); DCHECK(kUseBakerReadBarrier); + static_assert( + sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), + "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); // /* HeapReference<Object> */ ref = // *(obj + data_offset + index * sizeof(HeapReference<Object>)) Address src = index.IsConstant() ? diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index fb402bef00..2a9fb80995 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -491,6 +491,14 @@ class CodeGeneratorX86 : public CodeGenerator { Location index, Location temp, bool needs_null_check); + // Factored implementation used by GenerateFieldLoadWithBakerReadBarrier + // and GenerateArrayLoadWithBakerReadBarrier. + void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction, + Location ref, + Register obj, + const Address& src, + Location temp, + bool needs_null_check); // Generate a read barrier for a heap reference within `instruction` // using a slow path. @@ -561,15 +569,6 @@ class CodeGeneratorX86 : public CodeGenerator { static constexpr int32_t kDummy32BitOffset = 256; private: - // Factored implementation of GenerateFieldLoadWithBakerReadBarrier - // and GenerateArrayLoadWithBakerReadBarrier. - void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction, - Location ref, - Register obj, - const Address& src, - Location temp, - bool needs_null_check); - Register GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke, Register temp); struct PcRelativeDexCacheAccessInfo { diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 9a3e8d266b..2b21454101 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -469,7 +469,9 @@ class ReadBarrierMarkSlowPathX86_64 : public SlowPathCode { instruction_->IsLoadClass() || instruction_->IsLoadString() || instruction_->IsInstanceOf() || - instruction_->IsCheckCast()) + instruction_->IsCheckCast() || + ((instruction_->IsInvokeStaticOrDirect() || instruction_->IsInvokeVirtual()) && + instruction_->GetLocations()->Intrinsified())) << "Unexpected instruction in read barrier marking slow path: " << instruction_->DebugName(); @@ -532,8 +534,12 @@ class ReadBarrierForHeapReferenceSlowPathX86_64 : public SlowPathCode { CpuRegister reg_out = out_.AsRegister<CpuRegister>(); DCHECK(locations->CanCall()); DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out.AsRegister())) << out_; - DCHECK(!instruction_->IsInvoke() || - (instruction_->IsInvokeStaticOrDirect() && + DCHECK(instruction_->IsInstanceFieldGet() || + instruction_->IsStaticFieldGet() || + instruction_->IsArrayGet() || + instruction_->IsInstanceOf() || + instruction_->IsCheckCast() || + ((instruction_->IsInvokeStaticOrDirect() || instruction_->IsInvokeVirtual()) && instruction_->GetLocations()->Intrinsified())) << "Unexpected instruction in read barrier for heap reference slow path: " << instruction_->DebugName(); @@ -546,7 +552,7 @@ class ReadBarrierForHeapReferenceSlowPathX86_64 : public SlowPathCode { // introduce a copy of it, `index`. Location index = index_; if (index_.IsValid()) { - // Handle `index_` for HArrayGet and intrinsic UnsafeGetObject. + // Handle `index_` for HArrayGet and UnsafeGetObject/UnsafeGetObjectVolatile intrinsics. if (instruction_->IsArrayGet()) { // Compute real offset and store it in index_. Register index_reg = index_.AsRegister<CpuRegister>().AsRegister(); @@ -594,7 +600,11 @@ class ReadBarrierForHeapReferenceSlowPathX86_64 : public SlowPathCode { "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); __ AddImmediate(CpuRegister(index_reg), Immediate(offset_)); } else { - DCHECK(instruction_->IsInvoke()); + // In the case of the UnsafeGetObject/UnsafeGetObjectVolatile + // intrinsics, `index_` is not shifted by a scale factor of 2 + // (as in the case of ArrayGet), as it is actually an offset + // to an object field within an object. + DCHECK(instruction_->IsInvoke()) << instruction_->DebugName(); DCHECK(instruction_->GetLocations()->Intrinsified()); DCHECK((instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObject) || (instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile)) @@ -6430,6 +6440,9 @@ void CodeGeneratorX86_64::GenerateArrayLoadWithBakerReadBarrier(HInstruction* in DCHECK(kEmitCompilerReadBarrier); DCHECK(kUseBakerReadBarrier); + static_assert( + sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), + "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); // /* HeapReference<Object> */ ref = // *(obj + data_offset + index * sizeof(HeapReference<Object>)) Address src = index.IsConstant() ? diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index cf4cc4c8d2..d7cfd37c33 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -433,6 +433,14 @@ class CodeGeneratorX86_64 : public CodeGenerator { Location index, Location temp, bool needs_null_check); + // Factored implementation used by GenerateFieldLoadWithBakerReadBarrier + // and GenerateArrayLoadWithBakerReadBarrier. + void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction, + Location ref, + CpuRegister obj, + const Address& src, + Location temp, + bool needs_null_check); // Generate a read barrier for a heap reference within `instruction` // using a slow path. @@ -535,15 +543,6 @@ class CodeGeneratorX86_64 : public CodeGenerator { static constexpr int32_t kDummy32BitOffset = 256; private: - // Factored implementation of GenerateFieldLoadWithBakerReadBarrier - // and GenerateArrayLoadWithBakerReadBarrier. - void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction, - Location ref, - CpuRegister obj, - const Address& src, - Location temp, - bool needs_null_check); - struct PcRelativeDexCacheAccessInfo { PcRelativeDexCacheAccessInfo(const DexFile& dex_file, uint32_t element_off) : target_dex_file(dex_file), element_offset(element_off), label() { } diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index d5e80b4759..be4ea200a9 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -756,7 +756,15 @@ bool HInliner::TryInlineAndReplace(HInvoke* invoke_instruction, ArtMethod* metho invoke_instruction->ReplaceWith(return_replacement); } invoke_instruction->GetBlock()->RemoveInstruction(invoke_instruction); - FixUpReturnReferenceType(invoke_instruction, method, return_replacement, do_rtp); + FixUpReturnReferenceType(method, return_replacement); + if (do_rtp && ReturnTypeMoreSpecific(invoke_instruction, return_replacement)) { + // Actual return value has a more specific type than the method's declared + // return type. Run RTP again on the outer graph to propagate it. + ReferenceTypePropagation(graph_, + outer_compilation_unit_.GetDexCache(), + handles_, + /* is_first_run */ false).Run(); + } return true; } @@ -1159,6 +1167,15 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, } } + // We have replaced formal arguments with actual arguments. If actual types + // are more specific than the declared ones, run RTP again on the inner graph. + if (ArgumentTypesMoreSpecific(invoke_instruction, resolved_method)) { + ReferenceTypePropagation(callee_graph, + dex_compilation_unit.GetDexCache(), + handles_, + /* is_first_run */ false).Run(); + } + size_t number_of_instructions_budget = kMaximumNumberOfHInstructions; size_t number_of_inlined_instructions = RunOptimizations(callee_graph, code_item, dex_compilation_unit); @@ -1332,13 +1349,87 @@ size_t HInliner::RunOptimizations(HGraph* callee_graph, return number_of_inlined_instructions; } -void HInliner::FixUpReturnReferenceType(HInvoke* invoke_instruction, - ArtMethod* resolved_method, - HInstruction* return_replacement, - bool do_rtp) { +static bool IsReferenceTypeRefinement(ReferenceTypeInfo declared_rti, + bool declared_can_be_null, + HInstruction* actual_obj) + SHARED_REQUIRES(Locks::mutator_lock_) { + if (declared_can_be_null && !actual_obj->CanBeNull()) { + return true; + } + + ReferenceTypeInfo actual_rti = actual_obj->GetReferenceTypeInfo(); + return (actual_rti.IsExact() && !declared_rti.IsExact()) || + declared_rti.IsStrictSupertypeOf(actual_rti); +} + +ReferenceTypeInfo HInliner::GetClassRTI(mirror::Class* klass) { + return ReferenceTypePropagation::IsAdmissible(klass) + ? ReferenceTypeInfo::Create(handles_->NewHandle(klass)) + : graph_->GetInexactObjectRti(); +} + +bool HInliner::ArgumentTypesMoreSpecific(HInvoke* invoke_instruction, ArtMethod* resolved_method) { + // If this is an instance call, test whether the type of the `this` argument + // is more specific than the class which declares the method. + if (!resolved_method->IsStatic()) { + if (IsReferenceTypeRefinement(GetClassRTI(resolved_method->GetDeclaringClass()), + /* declared_can_be_null */ false, + invoke_instruction->InputAt(0u))) { + return true; + } + } + + size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); + + // Iterate over the list of parameter types and test whether any of the + // actual inputs has a more specific reference type than the type declared in + // the signature. + const DexFile::TypeList* param_list = resolved_method->GetParameterTypeList(); + for (size_t param_idx = 0, + input_idx = resolved_method->IsStatic() ? 0 : 1, + e = (param_list == nullptr ? 0 : param_list->Size()); + param_idx < e; + ++param_idx, ++input_idx) { + HInstruction* input = invoke_instruction->InputAt(input_idx); + if (input->GetType() == Primitive::kPrimNot) { + mirror::Class* param_cls = resolved_method->GetDexCacheResolvedType( + param_list->GetTypeItem(param_idx).type_idx_, + pointer_size); + if (IsReferenceTypeRefinement(GetClassRTI(param_cls), + /* declared_can_be_null */ true, + input)) { + return true; + } + } + } + + return false; +} + +bool HInliner::ReturnTypeMoreSpecific(HInvoke* invoke_instruction, + HInstruction* return_replacement) { // Check the integrity of reference types and run another type propagation if needed. if (return_replacement != nullptr) { if (return_replacement->GetType() == Primitive::kPrimNot) { + // Test if the return type is a refinement of the declared return type. + if (IsReferenceTypeRefinement(invoke_instruction->GetReferenceTypeInfo(), + /* declared_can_be_null */ true, + return_replacement)) { + return true; + } + } else if (return_replacement->IsInstanceOf()) { + // Inlining InstanceOf into an If may put a tighter bound on reference types. + return true; + } + } + + return false; +} + +void HInliner::FixUpReturnReferenceType(ArtMethod* resolved_method, + HInstruction* return_replacement) { + if (return_replacement != nullptr) { + if (return_replacement->GetType() == Primitive::kPrimNot) { if (!return_replacement->GetReferenceTypeInfo().IsValid()) { // Make sure that we have a valid type for the return. We may get an invalid one when // we inline invokes with multiple branches and create a Phi for the result. @@ -1347,36 +1438,7 @@ void HInliner::FixUpReturnReferenceType(HInvoke* invoke_instruction, DCHECK(return_replacement->IsPhi()); size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); mirror::Class* cls = resolved_method->GetReturnType(false /* resolve */, pointer_size); - if (cls != nullptr && !cls->IsErroneous()) { - ReferenceTypeInfo::TypeHandle return_handle = handles_->NewHandle(cls); - return_replacement->SetReferenceTypeInfo(ReferenceTypeInfo::Create( - return_handle, return_handle->CannotBeAssignedFromOtherTypes() /* is_exact */)); - } else { - // Return inexact object type on failures. - return_replacement->SetReferenceTypeInfo(graph_->GetInexactObjectRti()); - } - } - - if (do_rtp) { - // If the return type is a refinement of the declared type run the type propagation again. - ReferenceTypeInfo return_rti = return_replacement->GetReferenceTypeInfo(); - ReferenceTypeInfo invoke_rti = invoke_instruction->GetReferenceTypeInfo(); - if (invoke_rti.IsStrictSupertypeOf(return_rti) - || (return_rti.IsExact() && !invoke_rti.IsExact()) - || !return_replacement->CanBeNull()) { - ReferenceTypePropagation(graph_, - outer_compilation_unit_.GetDexCache(), - handles_, - /* is_first_run */ false).Run(); - } - } - } else if (return_replacement->IsInstanceOf()) { - if (do_rtp) { - // Inlining InstanceOf into an If may put a tighter bound on reference types. - ReferenceTypePropagation(graph_, - outer_compilation_unit_.GetDexCache(), - handles_, - /* is_first_run */ false).Run(); + return_replacement->SetReferenceTypeInfo(GetClassRTI(cls)); } } } diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h index 7cf1424b6d..02d3a5f499 100644 --- a/compiler/optimizing/inliner.h +++ b/compiler/optimizing/inliner.h @@ -124,10 +124,18 @@ class HInliner : public HOptimization { uint32_t dex_pc) const SHARED_REQUIRES(Locks::mutator_lock_); - void FixUpReturnReferenceType(HInvoke* invoke_instruction, - ArtMethod* resolved_method, - HInstruction* return_replacement, - bool do_rtp) + void FixUpReturnReferenceType(ArtMethod* resolved_method, HInstruction* return_replacement) + SHARED_REQUIRES(Locks::mutator_lock_); + + // Creates an instance of ReferenceTypeInfo from `klass` if `klass` is + // admissible (see ReferenceTypePropagation::IsAdmissible for details). + // Otherwise returns inexact Object RTI. + ReferenceTypeInfo GetClassRTI(mirror::Class* klass) SHARED_REQUIRES(Locks::mutator_lock_); + + bool ArgumentTypesMoreSpecific(HInvoke* invoke_instruction, ArtMethod* resolved_method) + SHARED_REQUIRES(Locks::mutator_lock_); + + bool ReturnTypeMoreSpecific(HInvoke* invoke_instruction, HInstruction* return_replacement) SHARED_REQUIRES(Locks::mutator_lock_); // Add a type guard on the given `receiver`. This will add to the graph: diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc index 93950d58b5..f43f8edf06 100644 --- a/compiler/optimizing/intrinsics_arm.cc +++ b/compiler/optimizing/intrinsics_arm.cc @@ -47,19 +47,6 @@ bool IntrinsicLocationsBuilderARM::TryDispatch(HInvoke* invoke) { if (res == nullptr) { return false; } - if (kEmitCompilerReadBarrier && res->CanCall()) { - // Generating an intrinsic for this HInvoke may produce an - // IntrinsicSlowPathARM slow path. Currently this approach - // does not work when using read barriers, as the emitted - // calling sequence will make use of another slow path - // (ReadBarrierForRootSlowPathARM for HInvokeStaticOrDirect, - // ReadBarrierSlowPathARM for HInvokeVirtual). So we bail - // out in this case. - // - // TODO: Find a way to have intrinsics work with read barriers. - invoke->SetLocations(nullptr); - return false; - } return res->Intrinsified(); } @@ -524,8 +511,8 @@ static void GenUnsafeGet(HInvoke* invoke, if (kEmitCompilerReadBarrier) { if (kUseBakerReadBarrier) { Location temp = locations->GetTemp(0); - codegen->GenerateArrayLoadWithBakerReadBarrier( - invoke, trg_loc, base, 0U, offset_loc, temp, /* needs_null_check */ false); + codegen->GenerateReferenceLoadWithBakerReadBarrier( + invoke, trg_loc, base, 0U, offset_loc, TIMES_1, temp, /* needs_null_check */ false); if (is_volatile) { __ dmb(ISH); } @@ -581,10 +568,11 @@ static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, locations->SetInAt(0, Location::NoLocation()); // Unused receiver. locations->SetInAt(1, Location::RequiresRegister()); locations->SetInAt(2, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + locations->SetOut(Location::RequiresRegister(), + can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap); if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) { // We need a temporary register for the read barrier marking slow - // path in InstructionCodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier. + // path in InstructionCodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier. locations->AddTemp(Location::RequiresRegister()); } } @@ -919,9 +907,10 @@ void IntrinsicLocationsBuilderARM::VisitUnsafeCASObject(HInvoke* invoke) { // The UnsafeCASObject intrinsic is missing a read barrier, and // therefore sometimes does not work as expected (b/25883050). // Turn it off temporarily as a quick fix, until the read barrier is - // implemented (see TODO in GenCAS below). + // implemented (see TODO in GenCAS). // - // TODO(rpl): Fix this issue and re-enable this intrinsic with read barriers. + // TODO(rpl): Implement read barrier support in GenCAS and re-enable + // this intrinsic. if (kEmitCompilerReadBarrier) { return; } @@ -932,6 +921,15 @@ void IntrinsicCodeGeneratorARM::VisitUnsafeCASInt(HInvoke* invoke) { GenCas(invoke->GetLocations(), Primitive::kPrimInt, codegen_); } void IntrinsicCodeGeneratorARM::VisitUnsafeCASObject(HInvoke* invoke) { + // The UnsafeCASObject intrinsic is missing a read barrier, and + // therefore sometimes does not work as expected (b/25883050). + // Turn it off temporarily as a quick fix, until the read barrier is + // implemented (see TODO in GenCAS). + // + // TODO(rpl): Implement read barrier support in GenCAS and re-enable + // this intrinsic. + DCHECK(!kEmitCompilerReadBarrier); + GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_); } @@ -1335,6 +1333,12 @@ void IntrinsicCodeGeneratorARM::VisitStringNewStringFromString(HInvoke* invoke) } void IntrinsicLocationsBuilderARM::VisitSystemArrayCopy(HInvoke* invoke) { + // TODO(rpl): Implement read barriers in the SystemArrayCopy + // intrinsic and re-enable it (b/29516905). + if (kEmitCompilerReadBarrier) { + return; + } + CodeGenerator::CreateSystemArrayCopyLocationSummary(invoke); LocationSummary* locations = invoke->GetLocations(); if (locations == nullptr) { @@ -1419,11 +1423,11 @@ static void CheckPosition(ArmAssembler* assembler, } } -// TODO: Implement read barriers in the SystemArrayCopy intrinsic. -// Note that this code path is not used (yet) because we do not -// intrinsify methods that can go into the IntrinsicSlowPathARM -// slow path. void IntrinsicCodeGeneratorARM::VisitSystemArrayCopy(HInvoke* invoke) { + // TODO(rpl): Implement read barriers in the SystemArrayCopy + // intrinsic and re-enable it (b/29516905). + DCHECK(!kEmitCompilerReadBarrier); + ArmAssembler* assembler = GetAssembler(); LocationSummary* locations = invoke->GetLocations(); diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index 4da0843a76..1685cf9c3c 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -149,19 +149,6 @@ bool IntrinsicLocationsBuilderARM64::TryDispatch(HInvoke* invoke) { if (res == nullptr) { return false; } - if (kEmitCompilerReadBarrier && res->CanCall()) { - // Generating an intrinsic for this HInvoke may produce an - // IntrinsicSlowPathARM64 slow path. Currently this approach - // does not work when using read barriers, as the emitted - // calling sequence will make use of another slow path - // (ReadBarrierForRootSlowPathARM64 for HInvokeStaticOrDirect, - // ReadBarrierSlowPathARM64 for HInvokeVirtual). So we bail - // out in this case. - // - // TODO: Find a way to have intrinsics work with read barriers. - invoke->SetLocations(nullptr); - return false; - } return res->Intrinsified(); } @@ -791,8 +778,15 @@ static void GenUnsafeGet(HInvoke* invoke, // UnsafeGetObject/UnsafeGetObjectVolatile with Baker's read barrier case. UseScratchRegisterScope temps(masm); Register temp = temps.AcquireW(); - codegen->GenerateArrayLoadWithBakerReadBarrier( - invoke, trg_loc, base, 0U, offset_loc, temp, /* needs_null_check */ false); + codegen->GenerateReferenceLoadWithBakerReadBarrier(invoke, + trg_loc, + base, + /* offset */ 0U, + /* index */ offset_loc, + /* scale_factor */ 0U, + temp, + /* needs_null_check */ false, + is_volatile); } else { // Other cases. MemOperand mem_op(base.X(), offset); @@ -821,7 +815,8 @@ static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke locations->SetInAt(0, Location::NoLocation()); // Unused receiver. locations->SetInAt(1, Location::RequiresRegister()); locations->SetInAt(2, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + locations->SetOut(Location::RequiresRegister(), + can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap); } void IntrinsicLocationsBuilderARM64::VisitUnsafeGet(HInvoke* invoke) { @@ -1102,9 +1097,10 @@ void IntrinsicLocationsBuilderARM64::VisitUnsafeCASObject(HInvoke* invoke) { // The UnsafeCASObject intrinsic is missing a read barrier, and // therefore sometimes does not work as expected (b/25883050). // Turn it off temporarily as a quick fix, until the read barrier is - // implemented (see TODO in GenCAS below). + // implemented (see TODO in GenCAS). // - // TODO(rpl): Fix this issue and re-enable this intrinsic with read barriers. + // TODO(rpl): Implement read barrier support in GenCAS and re-enable + // this intrinsic. if (kEmitCompilerReadBarrier) { return; } @@ -1119,6 +1115,15 @@ void IntrinsicCodeGeneratorARM64::VisitUnsafeCASLong(HInvoke* invoke) { GenCas(invoke->GetLocations(), Primitive::kPrimLong, codegen_); } void IntrinsicCodeGeneratorARM64::VisitUnsafeCASObject(HInvoke* invoke) { + // The UnsafeCASObject intrinsic is missing a read barrier, and + // therefore sometimes does not work as expected (b/25883050). + // Turn it off temporarily as a quick fix, until the read barrier is + // implemented (see TODO in GenCAS). + // + // TODO(rpl): Implement read barrier support in GenCAS and re-enable + // this intrinsic. + DCHECK(!kEmitCompilerReadBarrier); + GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_); } @@ -2012,6 +2017,12 @@ static constexpr int32_t kSystemArrayCopyThreshold = 128; // We want to use two temporary registers in order to reduce the register pressure in arm64. // So we don't use the CodeGenerator::CreateSystemArrayCopyLocationSummary. void IntrinsicLocationsBuilderARM64::VisitSystemArrayCopy(HInvoke* invoke) { + // TODO(rpl): Implement read barriers in the SystemArrayCopy + // intrinsic and re-enable it (b/29516905). + if (kEmitCompilerReadBarrier) { + return; + } + // Check to see if we have known failures that will cause us to have to bail out // to the runtime, and just generate the runtime call directly. HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant(); @@ -2064,6 +2075,10 @@ void IntrinsicLocationsBuilderARM64::VisitSystemArrayCopy(HInvoke* invoke) { } void IntrinsicCodeGeneratorARM64::VisitSystemArrayCopy(HInvoke* invoke) { + // TODO(rpl): Implement read barriers in the SystemArrayCopy + // intrinsic and re-enable it (b/29516905). + DCHECK(!kEmitCompilerReadBarrier); + vixl::MacroAssembler* masm = GetVIXLAssembler(); LocationSummary* locations = invoke->GetLocations(); diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index 4988398c92..031cd1313c 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -60,19 +60,6 @@ bool IntrinsicLocationsBuilderX86::TryDispatch(HInvoke* invoke) { if (res == nullptr) { return false; } - if (kEmitCompilerReadBarrier && res->CanCall()) { - // Generating an intrinsic for this HInvoke may produce an - // IntrinsicSlowPathX86 slow path. Currently this approach - // does not work when using read barriers, as the emitted - // calling sequence will make use of another slow path - // (ReadBarrierForRootSlowPathX86 for HInvokeStaticOrDirect, - // ReadBarrierSlowPathX86 for HInvokeVirtual). So we bail - // out in this case. - // - // TODO: Find a way to have intrinsics work with read barriers. - invoke->SetLocations(nullptr); - return false; - } return res->Intrinsified(); } @@ -1822,8 +1809,9 @@ static void GenUnsafeGet(HInvoke* invoke, if (kEmitCompilerReadBarrier) { if (kUseBakerReadBarrier) { Location temp = locations->GetTemp(0); - codegen->GenerateArrayLoadWithBakerReadBarrier( - invoke, output_loc, base, 0U, offset_loc, temp, /* needs_null_check */ false); + Address src(base, offset, ScaleFactor::TIMES_1, 0); + codegen->GenerateReferenceLoadWithBakerReadBarrier( + invoke, output_loc, base, src, temp, /* needs_null_check */ false); } else { __ movl(output, Address(base, offset, ScaleFactor::TIMES_1, 0)); codegen->GenerateReadBarrierSlow( @@ -1878,16 +1866,17 @@ static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, if (is_volatile) { // Need to use XMM to read volatile. locations->AddTemp(Location::RequiresFpuRegister()); - locations->SetOut(Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } else { locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); } } else { - locations->SetOut(Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), + can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap); } if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) { // We need a temporary register for the read barrier marking slow - // path in InstructionCodeGeneratorX86::GenerateArrayLoadWithBakerReadBarrier. + // path in InstructionCodeGeneratorX86::GenerateReferenceLoadWithBakerReadBarrier. locations->AddTemp(Location::RequiresRegister()); } } @@ -2109,9 +2098,9 @@ void IntrinsicLocationsBuilderX86::VisitUnsafeCASObject(HInvoke* invoke) { // The UnsafeCASObject intrinsic is missing a read barrier, and // therefore sometimes does not work as expected (b/25883050). // Turn it off temporarily as a quick fix, until the read barrier is - // implemented. + // implemented (see TODO in GenCAS). // - // TODO(rpl): Implement a read barrier in GenCAS below and re-enable + // TODO(rpl): Implement read barrier support in GenCAS and re-enable // this intrinsic. if (kEmitCompilerReadBarrier) { return; @@ -2236,6 +2225,15 @@ void IntrinsicCodeGeneratorX86::VisitUnsafeCASLong(HInvoke* invoke) { } void IntrinsicCodeGeneratorX86::VisitUnsafeCASObject(HInvoke* invoke) { + // The UnsafeCASObject intrinsic is missing a read barrier, and + // therefore sometimes does not work as expected (b/25883050). + // Turn it off temporarily as a quick fix, until the read barrier is + // implemented (see TODO in GenCAS). + // + // TODO(rpl): Implement read barrier support in GenCAS and re-enable + // this intrinsic. + DCHECK(!kEmitCompilerReadBarrier); + GenCAS(Primitive::kPrimNot, invoke, codegen_); } diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 593c8f319b..1a68dff18e 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -54,19 +54,6 @@ bool IntrinsicLocationsBuilderX86_64::TryDispatch(HInvoke* invoke) { if (res == nullptr) { return false; } - if (kEmitCompilerReadBarrier && res->CanCall()) { - // Generating an intrinsic for this HInvoke may produce an - // IntrinsicSlowPathX86_64 slow path. Currently this approach - // does not work when using read barriers, as the emitted - // calling sequence will make use of another slow path - // (ReadBarrierForRootSlowPathX86_64 for HInvokeStaticOrDirect, - // ReadBarrierSlowPathX86_64 for HInvokeVirtual). So we bail - // out in this case. - // - // TODO: Find a way to have intrinsics work with read barriers. - invoke->SetLocations(nullptr); - return false; - } return res->Intrinsified(); } @@ -1079,14 +1066,20 @@ void IntrinsicCodeGeneratorX86_64::VisitSystemArrayCopyChar(HInvoke* invoke) { void IntrinsicLocationsBuilderX86_64::VisitSystemArrayCopy(HInvoke* invoke) { + // TODO(rpl): Implement read barriers in the SystemArrayCopy + // intrinsic and re-enable it (b/29516905). + if (kEmitCompilerReadBarrier) { + return; + } + CodeGenerator::CreateSystemArrayCopyLocationSummary(invoke); } -// TODO: Implement read barriers in the SystemArrayCopy intrinsic. -// Note that this code path is not used (yet) because we do not -// intrinsify methods that can go into the IntrinsicSlowPathX86_64 -// slow path. void IntrinsicCodeGeneratorX86_64::VisitSystemArrayCopy(HInvoke* invoke) { + // TODO(rpl): Implement read barriers in the SystemArrayCopy + // intrinsic and re-enable it (b/29516905). + DCHECK(!kEmitCompilerReadBarrier); + X86_64Assembler* assembler = GetAssembler(); LocationSummary* locations = invoke->GetLocations(); @@ -1910,8 +1903,9 @@ static void GenUnsafeGet(HInvoke* invoke, if (kEmitCompilerReadBarrier) { if (kUseBakerReadBarrier) { Location temp = locations->GetTemp(0); - codegen->GenerateArrayLoadWithBakerReadBarrier( - invoke, output_loc, base, 0U, offset_loc, temp, /* needs_null_check */ false); + Address src(base, offset, ScaleFactor::TIMES_1, 0); + codegen->GenerateReferenceLoadWithBakerReadBarrier( + invoke, output_loc, base, src, temp, /* needs_null_check */ false); } else { __ movl(output, Address(base, offset, ScaleFactor::TIMES_1, 0)); codegen->GenerateReadBarrierSlow( @@ -1948,10 +1942,11 @@ static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, locations->SetInAt(0, Location::NoLocation()); // Unused receiver. locations->SetInAt(1, Location::RequiresRegister()); locations->SetInAt(2, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), + can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap); if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) { // We need a temporary register for the read barrier marking slow - // path in InstructionCodeGeneratorX86_64::GenerateArrayLoadWithBakerReadBarrier. + // path in InstructionCodeGeneratorX86_64::GenerateReferenceLoadWithBakerReadBarrier. locations->AddTemp(Location::RequiresRegister()); } } @@ -2135,9 +2130,9 @@ void IntrinsicLocationsBuilderX86_64::VisitUnsafeCASObject(HInvoke* invoke) { // The UnsafeCASObject intrinsic is missing a read barrier, and // therefore sometimes does not work as expected (b/25883050). // Turn it off temporarily as a quick fix, until the read barrier is - // implemented. + // implemented (see TODO in GenCAS). // - // TODO(rpl): Implement a read barrier in GenCAS below and re-enable + // TODO(rpl): Implement read barrier support in GenCAS and re-enable // this intrinsic. if (kEmitCompilerReadBarrier) { return; @@ -2253,6 +2248,15 @@ void IntrinsicCodeGeneratorX86_64::VisitUnsafeCASLong(HInvoke* invoke) { } void IntrinsicCodeGeneratorX86_64::VisitUnsafeCASObject(HInvoke* invoke) { + // The UnsafeCASObject intrinsic is missing a read barrier, and + // therefore sometimes does not work as expected (b/25883050). + // Turn it off temporarily as a quick fix, until the read barrier is + // implemented (see TODO in GenCAS). + // + // TODO(rpl): Implement read barrier support in GenCAS and re-enable + // this intrinsic. + DCHECK(!kEmitCompilerReadBarrier); + GenCAS(Primitive::kPrimNot, invoke, codegen_); } diff --git a/compiler/optimizing/locations.h b/compiler/optimizing/locations.h index 63bbc2cd0a..3f27c911be 100644 --- a/compiler/optimizing/locations.h +++ b/compiler/optimizing/locations.h @@ -38,7 +38,13 @@ std::ostream& operator<<(std::ostream& os, const Location& location); class Location : public ValueObject { public: enum OutputOverlap { + // The liveness of the output overlaps the liveness of one or + // several input(s); the register allocator cannot reuse an + // input's location for the output's location. kOutputOverlap, + // The liveness of the output does not overlap the liveness of any + // input; the register allocator is allowed to reuse an input's + // location for the output's location. kNoOutputOverlap }; @@ -494,6 +500,10 @@ class LocationSummary : public ArenaObject<kArenaAllocLocationSummary> { return inputs_.size(); } + // Set the output location. Argument `overlaps` tells whether the + // output overlaps any of the inputs (if so, it cannot share the + // same register as one of the inputs); it is set to + // `Location::kOutputOverlap` by default for safety. void SetOut(Location location, Location::OutputOverlap overlaps = Location::kOutputOverlap) { DCHECK(output_.IsInvalid()); output_overlaps_ = overlaps; diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 455f4e338d..29df7c8ab8 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -172,6 +172,10 @@ class ReferenceTypeInfo : ValueObject { static ReferenceTypeInfo Create(TypeHandle type_handle, bool is_exact); + static ReferenceTypeInfo Create(TypeHandle type_handle) SHARED_REQUIRES(Locks::mutator_lock_) { + return Create(type_handle, type_handle->CannotBeAssignedFromOtherTypes()); + } + static ReferenceTypeInfo CreateUnchecked(TypeHandle type_handle, bool is_exact) { return ReferenceTypeInfo(type_handle, is_exact); } diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index 3e6adcb172..3dfd7282cd 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -46,13 +46,6 @@ static inline ReferenceTypeInfo::TypeHandle GetRootHandle(StackHandleScopeCollec return *cache; } -// Returns true if klass is admissible to the propagation: non-null and resolved. -// For an array type, we also check if the component type is admissible. -static bool IsAdmissible(mirror::Class* klass) SHARED_REQUIRES(Locks::mutator_lock_) { - return klass != nullptr && klass->IsResolved() && - (!klass->IsArrayClass() || IsAdmissible(klass->GetComponentType())); -} - ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetObjectClassHandle() { return GetRootHandle(handles_, ClassLinker::kJavaLangObject, &object_class_handle_); } diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h index 2106be6b53..edd83bf5de 100644 --- a/compiler/optimizing/reference_type_propagation.h +++ b/compiler/optimizing/reference_type_propagation.h @@ -42,6 +42,14 @@ class ReferenceTypePropagation : public HOptimization { void Run() OVERRIDE; + // Returns true if klass is admissible to the propagation: non-null and resolved. + // For an array type, we also check if the component type is admissible. + static bool IsAdmissible(mirror::Class* klass) SHARED_REQUIRES(Locks::mutator_lock_) { + return klass != nullptr && + klass->IsResolved() && + (!klass->IsArrayClass() || IsAdmissible(klass->GetComponentType())); + } + static constexpr const char* kReferenceTypePropagationPassName = "reference_type_propagation"; private: diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h index cf9b8ebfd7..96f2098ab0 100644 --- a/runtime/mirror/string-inl.h +++ b/runtime/mirror/string-inl.h @@ -34,7 +34,7 @@ namespace art { namespace mirror { inline uint32_t String::ClassSize(size_t pointer_size) { - uint32_t vtable_entries = Object::kVTableLength + 56; + uint32_t vtable_entries = Object::kVTableLength + 57; return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0, 1, 2, pointer_size); } diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index 962115ed29..e3cc77f2c0 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -56,12 +56,11 @@ class OatFileAssistantTest : public CommonRuntimeTest { odex_dir_ = odex_oat_dir_ + "/" + std::string(GetInstructionSetString(kRuntimeISA)); ASSERT_EQ(0, mkdir(odex_dir_.c_str(), 0700)); - // Verify the environment is as we expect uint32_t checksum; std::string error_msg; - ASSERT_TRUE(OS::FileExists(GetImageFile().c_str())) - << "Expected pre-compiled boot image to be at: " << GetImageFile(); + ASSERT_TRUE(OS::FileExists(GetSystemImageFile().c_str())) + << "Expected pre-compiled boot image to be at: " << GetSystemImageFile(); ASSERT_TRUE(OS::FileExists(GetDexSrc1().c_str())) << "Expected dex file to be at: " << GetDexSrc1(); ASSERT_TRUE(OS::FileExists(GetStrippedDexSrc1().c_str())) @@ -88,6 +87,27 @@ class OatFileAssistantTest : public CommonRuntimeTest { ASSERT_NE(multi1[1]->GetLocationChecksum(), multi2[1]->GetLocationChecksum()); } + // Pre-Relocate the image to a known non-zero offset so we don't have to + // deal with the runtime randomly relocating the image by 0 and messing up + // the expected results of the tests. + bool PreRelocateImage(std::string* error_msg) { + std::string image; + if (!GetCachedImageFile(&image, error_msg)) { + return false; + } + + std::string patchoat = GetAndroidRoot(); + patchoat += kIsDebugBuild ? "/bin/patchoatd" : "/bin/patchoat"; + + std::vector<std::string> argv; + argv.push_back(patchoat); + argv.push_back("--input-image-location=" + GetImageLocation()); + argv.push_back("--output-image-file=" + image); + argv.push_back("--instruction-set=" + std::string(GetInstructionSetString(kRuntimeISA))); + argv.push_back("--base-offset-delta=0x00008000"); + return Exec(argv, error_msg); + } + virtual void SetUpRuntimeOptions(RuntimeOptions* options) { // options->push_back(std::make_pair("-verbose:oat", nullptr)); @@ -100,6 +120,9 @@ class OatFileAssistantTest : public CommonRuntimeTest { } virtual void PreRuntimeCreate() { + std::string error_msg; + ASSERT_TRUE(PreRelocateImage(&error_msg)) << error_msg; + UnreserveImageSpace(); } @@ -145,11 +168,16 @@ class OatFileAssistantTest : public CommonRuntimeTest { return GetImageDirectory() + "/core.art"; } - std::string GetImageFile() { + std::string GetSystemImageFile() { return GetImageDirectory() + "/" + GetInstructionSetString(kRuntimeISA) + "/core.art"; } + bool GetCachedImageFile(/*out*/std::string* image, std::string* error_msg) { + std::string cache = GetDalvikCache(GetInstructionSetString(kRuntimeISA), true); + return GetDalvikCacheFilename(GetImageLocation().c_str(), cache.c_str(), image, error_msg); + } + std::string GetDexSrc1() { return GetTestDexFileName("Main"); } @@ -190,34 +218,31 @@ class OatFileAssistantTest : public CommonRuntimeTest { // The generated odex file will be un-relocated. void GenerateOdexForTest(const std::string& dex_location, const std::string& odex_location, - CompilerFilter::Filter filter) { - // To generate an un-relocated odex file, we first compile a relocated - // version of the file, then manually call patchoat to make it look as if - // it is unrelocated. - std::string relocated_odex_location = odex_location + ".relocated"; + CompilerFilter::Filter filter, + bool pic = false, + bool with_patch_info = true) { + // Temporarily redirect the dalvik cache so dex2oat doesn't find the + // relocated image file. + std::string android_data_tmp = GetScratchDir() + "AndroidDataTmp"; + setenv("ANDROID_DATA", android_data_tmp.c_str(), 1); std::vector<std::string> args; args.push_back("--dex-file=" + dex_location); - args.push_back("--oat-file=" + relocated_odex_location); + args.push_back("--oat-file=" + odex_location); args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter)); + args.push_back("--runtime-arg"); + args.push_back("-Xnorelocate"); - // We need to use the quick compiler to generate non-PIC code, because - // the optimizing compiler always generates PIC. - args.push_back("--compiler-backend=Quick"); - args.push_back("--include-patch-information"); + if (pic) { + args.push_back("--compile-pic"); + } + + if (with_patch_info) { + args.push_back("--include-patch-information"); + } std::string error_msg; ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg; - - // Use patchoat to unrelocate the relocated odex file. - Runtime* runtime = Runtime::Current(); - std::vector<std::string> argv; - argv.push_back(runtime->GetPatchoatExecutable()); - argv.push_back("--instruction-set=" + std::string(GetInstructionSetString(kRuntimeISA))); - argv.push_back("--input-oat-file=" + relocated_odex_location); - argv.push_back("--output-oat-file=" + odex_location); - argv.push_back("--base-offset-delta=0x00008000"); - std::string command_line(Join(argv, ' ')); - ASSERT_TRUE(Exec(argv, &error_msg)) << error_msg; + setenv("ANDROID_DATA", android_data_.c_str(), 1); // Verify the odex file was generated as expected and really is // unrelocated. @@ -230,13 +255,13 @@ class OatFileAssistantTest : public CommonRuntimeTest { dex_location.c_str(), &error_msg)); ASSERT_TRUE(odex_file.get() != nullptr) << error_msg; - EXPECT_FALSE(odex_file->IsPic()); - EXPECT_TRUE(odex_file->HasPatchInfo()); + EXPECT_EQ(pic, odex_file->IsPic()); + EXPECT_EQ(with_patch_info, odex_file->HasPatchInfo()); EXPECT_EQ(filter, odex_file->GetCompilerFilter()); if (CompilerFilter::IsBytecodeCompilationEnabled(filter)) { const std::vector<gc::space::ImageSpace*> image_spaces = - runtime->GetHeap()->GetBootImageSpaces(); + Runtime::Current()->GetHeap()->GetBootImageSpaces(); ASSERT_TRUE(!image_spaces.empty() && image_spaces[0] != nullptr); const ImageHeader& image_header = image_spaces[0]->GetImageHeader(); const OatHeader& oat_header = odex_file->GetOatHeader(); @@ -251,71 +276,15 @@ class OatFileAssistantTest : public CommonRuntimeTest { void GeneratePicOdexForTest(const std::string& dex_location, const std::string& odex_location, CompilerFilter::Filter filter) { - // Temporarily redirect the dalvik cache so dex2oat doesn't find the - // relocated image file. - std::string android_data_tmp = GetScratchDir() + "AndroidDataTmp"; - setenv("ANDROID_DATA", android_data_tmp.c_str(), 1); - std::vector<std::string> args; - args.push_back("--dex-file=" + dex_location); - args.push_back("--oat-file=" + odex_location); - args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter)); - args.push_back("--compile-pic"); - args.push_back("--runtime-arg"); - args.push_back("-Xnorelocate"); - std::string error_msg; - ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg; - setenv("ANDROID_DATA", android_data_.c_str(), 1); - - // Verify the odex file was generated as expected. - std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(), - odex_location.c_str(), - nullptr, - nullptr, - false, - /*low_4gb*/false, - dex_location.c_str(), - &error_msg)); - ASSERT_TRUE(odex_file.get() != nullptr) << error_msg; - EXPECT_TRUE(odex_file->IsPic()); - EXPECT_EQ(filter, odex_file->GetCompilerFilter()); + GenerateOdexForTest(dex_location, odex_location, filter, true, false); } // Generate a non-PIC odex file without patch information for the purposes // of test. The generated odex file will be un-relocated. - // TODO: This won't work correctly if we depend on the boot image being - // randomly relocated by a non-zero amount. We should have a better solution - // for avoiding that flakiness and duplicating code to generate odex and oat - // files for test. void GenerateNoPatchOdexForTest(const std::string& dex_location, const std::string& odex_location, CompilerFilter::Filter filter) { - // Temporarily redirect the dalvik cache so dex2oat doesn't find the - // relocated image file. - std::string android_data_tmp = GetScratchDir() + "AndroidDataTmp"; - setenv("ANDROID_DATA", android_data_tmp.c_str(), 1); - std::vector<std::string> args; - args.push_back("--dex-file=" + dex_location); - args.push_back("--oat-file=" + odex_location); - args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter)); - args.push_back("--runtime-arg"); - args.push_back("-Xnorelocate"); - std::string error_msg; - ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg; - setenv("ANDROID_DATA", android_data_.c_str(), 1); - - // Verify the odex file was generated as expected. - std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(), - odex_location.c_str(), - nullptr, - nullptr, - false, - /*low_4gb*/false, - dex_location.c_str(), - &error_msg)); - ASSERT_TRUE(odex_file.get() != nullptr) << error_msg; - EXPECT_FALSE(odex_file->IsPic()); - EXPECT_FALSE(odex_file->HasPatchInfo()); - EXPECT_EQ(filter, odex_file->GetCompilerFilter()); + GenerateOdexForTest(dex_location, odex_location, filter, false, false); } private: @@ -327,11 +296,10 @@ class OatFileAssistantTest : public CommonRuntimeTest { MemMap::Init(); // Ensure a chunk of memory is reserved for the image space. - uintptr_t reservation_start = ART_BASE_ADDRESS + ART_BASE_ADDRESS_MIN_DELTA; - uintptr_t reservation_end = ART_BASE_ADDRESS + ART_BASE_ADDRESS_MAX_DELTA - // Include the main space that has to come right after the - // image in case of the GSS collector. - + 384 * MB; + // The reservation_end includes room for the main space that has to come + // right after the image in case of the GSS collector. + uintptr_t reservation_start = ART_BASE_ADDRESS; + uintptr_t reservation_end = ART_BASE_ADDRESS + 384 * MB; std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(getpid(), true)); ASSERT_TRUE(map.get() != nullptr) << "Failed to build process map"; diff --git a/test/004-UnsafeTest/src/Main.java b/test/004-UnsafeTest/src/Main.java index b2f905e0ee..9d4618a07c 100644 --- a/test/004-UnsafeTest/src/Main.java +++ b/test/004-UnsafeTest/src/Main.java @@ -39,16 +39,24 @@ public class Main { } } - private static Unsafe getUnsafe() throws Exception { + private static Unsafe getUnsafe() throws NoSuchFieldException, IllegalAccessException { Class<?> unsafeClass = Unsafe.class; Field f = unsafeClass.getDeclaredField("theUnsafe"); f.setAccessible(true); return (Unsafe) f.get(null); } - public static void main(String[] args) throws Exception { + public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException { System.loadLibrary(args[0]); Unsafe unsafe = getUnsafe(); + + testArrayBaseOffset(unsafe); + testArrayIndexScale(unsafe); + testGetAndPutAndCAS(unsafe); + testGetAndPutVolatile(unsafe); + } + + private static void testArrayBaseOffset(Unsafe unsafe) { check(unsafe.arrayBaseOffset(boolean[].class), vmArrayBaseOffset(boolean[].class), "Unsafe.arrayBaseOffset(boolean[])"); check(unsafe.arrayBaseOffset(byte[].class), vmArrayBaseOffset(byte[].class), @@ -65,7 +73,9 @@ public class Main { "Unsafe.arrayBaseOffset(long[])"); check(unsafe.arrayBaseOffset(Object[].class), vmArrayBaseOffset(Object[].class), "Unsafe.arrayBaseOffset(Object[])"); + } + private static void testArrayIndexScale(Unsafe unsafe) { check(unsafe.arrayIndexScale(boolean[].class), vmArrayIndexScale(boolean[].class), "Unsafe.arrayIndexScale(boolean[])"); check(unsafe.arrayIndexScale(byte[].class), vmArrayIndexScale(byte[].class), @@ -82,7 +92,9 @@ public class Main { "Unsafe.arrayIndexScale(long[])"); check(unsafe.arrayIndexScale(Object[].class), vmArrayIndexScale(Object[].class), "Unsafe.arrayIndexScale(Object[])"); + } + private static void testGetAndPutAndCAS(Unsafe unsafe) throws NoSuchFieldException { TestClass t = new TestClass(); int intValue = 12345678; @@ -185,12 +197,58 @@ public class Main { } } + private static void testGetAndPutVolatile(Unsafe unsafe) throws NoSuchFieldException { + TestVolatileClass tv = new TestVolatileClass(); + + int intValue = 12345678; + Field volatileIntField = TestVolatileClass.class.getDeclaredField("volatileIntVar"); + long volatileIntOffset = unsafe.objectFieldOffset(volatileIntField); + check(unsafe.getIntVolatile(tv, volatileIntOffset), + 0, + "Unsafe.getIntVolatile(Object, long) - initial"); + unsafe.putIntVolatile(tv, volatileIntOffset, intValue); + check(tv.volatileIntVar, intValue, "Unsafe.putIntVolatile(Object, long, int)"); + check(unsafe.getIntVolatile(tv, volatileIntOffset), + intValue, + "Unsafe.getIntVolatile(Object, long)"); + + long longValue = 1234567887654321L; + Field volatileLongField = TestVolatileClass.class.getDeclaredField("volatileLongVar"); + long volatileLongOffset = unsafe.objectFieldOffset(volatileLongField); + check(unsafe.getLongVolatile(tv, volatileLongOffset), + 0, + "Unsafe.getLongVolatile(Object, long) - initial"); + unsafe.putLongVolatile(tv, volatileLongOffset, longValue); + check(tv.volatileLongVar, longValue, "Unsafe.putLongVolatile(Object, long, long)"); + check(unsafe.getLongVolatile(tv, volatileLongOffset), + longValue, + "Unsafe.getLongVolatile(Object, long)"); + + Object objectValue = new Object(); + Field volatileObjectField = TestVolatileClass.class.getDeclaredField("volatileObjectVar"); + long volatileObjectOffset = unsafe.objectFieldOffset(volatileObjectField); + check(unsafe.getObjectVolatile(tv, volatileObjectOffset), + null, + "Unsafe.getObjectVolatile(Object, long) - initial"); + unsafe.putObjectVolatile(tv, volatileObjectOffset, objectValue); + check(tv.volatileObjectVar, objectValue, "Unsafe.putObjectVolatile(Object, long, Object)"); + check(unsafe.getObjectVolatile(tv, volatileObjectOffset), + objectValue, + "Unsafe.getObjectVolatile(Object, long)"); + } + private static class TestClass { public int intVar = 0; public long longVar = 0; public Object objectVar = null; } + private static class TestVolatileClass { + public volatile int volatileIntVar = 0; + public volatile long volatileLongVar = 0; + public volatile Object volatileObjectVar = null; + } + private static native int vmArrayBaseOffset(Class clazz); private static native int vmArrayIndexScale(Class clazz); } diff --git a/test/450-checker-types/src/Main.java b/test/450-checker-types/src/Main.java index 08b6cec35f..98a09386ee 100644 --- a/test/450-checker-types/src/Main.java +++ b/test/450-checker-types/src/Main.java @@ -30,6 +30,11 @@ class Super implements Interface { public void $noinline$f() { throw new RuntimeException(); } + + public int $inline$h(boolean cond) { + Super obj = (cond ? this : null); + return obj.hashCode(); + } } class SubclassA extends Super { @@ -620,6 +625,45 @@ public class Main { o.mainField = 0; } + /// CHECK-START: void Main.testThisArgumentMoreSpecific(boolean) inliner (before) + /// CHECK-DAG: <<Arg:l\d+>> NewInstance + /// CHECK-DAG: InvokeVirtual [<<Arg>>,{{z\d+}}] method_name:Super.$inline$h + + /// CHECK-START: void Main.testThisArgumentMoreSpecific(boolean) inliner (after) + /// CHECK-DAG: <<Arg:l\d+>> NewInstance + /// CHECK-DAG: <<Null:l\d+>> NullConstant + /// CHECK-DAG: <<Phi:l\d+>> Phi [<<Arg>>,<<Null>>] klass:SubclassA + /// CHECK-DAG: <<NCPhi:l\d+>> NullCheck [<<Phi>>] + /// CHECK-DAG: InvokeVirtual [<<NCPhi>>] method_name:Super.hashCode + + public void testThisArgumentMoreSpecific(boolean cond) { + // Inlining method from Super will build it with `this` typed as Super. + // Running RTP will sharpen it to SubclassA. + SubclassA obj = new SubclassA(); + ((Super) obj).$inline$h(cond); + } + + public static int $inline$hashCode(Super obj) { + return obj.hashCode(); + } + + /// CHECK-START: void Main.testExplicitArgumentMoreSpecific(SubclassA) inliner (before) + /// CHECK-DAG: <<Arg:l\d+>> ParameterValue klass:SubclassA + /// CHECK-DAG: InvokeStaticOrDirect [<<Arg>>] method_name:Main.$inline$hashCode + + /// CHECK-START: void Main.testExplicitArgumentMoreSpecific(SubclassA) inliner (after) + /// CHECK-DAG: <<Arg:l\d+>> ParameterValue klass:SubclassA + /// CHECK-DAG: <<NCArg:l\d+>> NullCheck [<<Arg>>] klass:SubclassA + /// CHECK-DAG: InvokeVirtual [<<NCArg>>] method_name:Super.hashCode + + public void testExplicitArgumentMoreSpecific(SubclassA obj) { + // Inlining a method will build it with reference types from its signature, + // here the callee graph is built with Super as the type of its only argument. + // Running RTP after its ParameterValue instructions are replaced with actual + // arguments will type the inner graph more precisely. + $inline$hashCode(obj); + } + /// CHECK-START: void Main.testPhiHasOnlyNullInputs(boolean) inliner (before) /// CHECK: <<Int:i\d+>> IntConstant 0 /// CHECK: <<Phi:l\d+>> Phi klass:Main exact:false |