diff options
29 files changed, 1785 insertions, 459 deletions
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index a2bab80b85..e62bdb5530 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -1188,13 +1188,12 @@ void CompilerDriver::LoadImageClasses(TimingLogger* timings) { CHECK_NE(image_classes_->size(), 0U); } -static void MaybeAddToImageClasses(Handle<mirror::Class> c, +static void MaybeAddToImageClasses(Thread* self, + ObjPtr<mirror::Class> klass, std::unordered_set<std::string>* image_classes) REQUIRES_SHARED(Locks::mutator_lock_) { - Thread* self = Thread::Current(); + DCHECK_EQ(self, Thread::Current()); StackHandleScope<1> hs(self); - // Make a copy of the handle so that we don't clobber it doing Assign. - MutableHandle<mirror::Class> klass(hs.NewHandle(c.Get())); std::string temp; const PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); while (!klass->IsObjectClass()) { @@ -1205,19 +1204,16 @@ static void MaybeAddToImageClasses(Handle<mirror::Class> c, break; } VLOG(compiler) << "Adding " << descriptor << " to image classes"; - for (size_t i = 0; i < klass->NumDirectInterfaces(); ++i) { - StackHandleScope<1> hs2(self); - // May cause thread suspension. - MaybeAddToImageClasses(hs2.NewHandle(mirror::Class::GetDirectInterface(self, klass, i)), - image_classes); + for (size_t i = 0, num_interfaces = klass->NumDirectInterfaces(); i != num_interfaces; ++i) { + ObjPtr<mirror::Class> interface = mirror::Class::GetDirectInterface(self, klass, i); + DCHECK(interface != nullptr); + MaybeAddToImageClasses(self, interface, image_classes); } - for (auto& m : c->GetVirtualMethods(pointer_size)) { - StackHandleScope<1> hs2(self); - MaybeAddToImageClasses(hs2.NewHandle(m.GetDeclaringClass()), image_classes); + for (auto& m : klass->GetVirtualMethods(pointer_size)) { + MaybeAddToImageClasses(self, m.GetDeclaringClass(), image_classes); } if (klass->IsArrayClass()) { - StackHandleScope<1> hs2(self); - MaybeAddToImageClasses(hs2.NewHandle(klass->GetComponentType()), image_classes); + MaybeAddToImageClasses(self, klass->GetComponentType(), image_classes); } klass.Assign(klass->GetSuperClass()); } @@ -1268,8 +1264,10 @@ class ClinitImageUpdate { for (Handle<mirror::Class> klass_root : image_classes_) { VisitClinitClassesObject(klass_root.Get()); } + Thread* self = Thread::Current(); + ScopedAssertNoThreadSuspension ants(__FUNCTION__); for (Handle<mirror::Class> h_klass : to_insert_) { - MaybeAddToImageClasses(h_klass, image_class_descriptors_); + MaybeAddToImageClasses(self, h_klass.Get(), image_class_descriptors_); } } diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 4b24ac3459..c8bdd0163d 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -613,6 +613,509 @@ class ArraySetSlowPathARMVIXL : public SlowPathCodeARMVIXL { DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathARMVIXL); }; +// Slow path marking an object reference `ref` during a read +// barrier. The field `obj.field` in the object `obj` holding this +// reference does not get updated by this slow path after marking (see +// ReadBarrierMarkAndUpdateFieldSlowPathARM below for that). +// +// This means that after the execution of this slow path, `ref` will +// always be up-to-date, but `obj.field` may not; i.e., after the +// flip, `ref` will be a to-space reference, but `obj.field` will +// probably still be a from-space reference (unless it gets updated by +// another thread, or if another thread installed another object +// reference (different from `ref`) in `obj.field`). +class ReadBarrierMarkSlowPathARMVIXL : public SlowPathCodeARMVIXL { + public: + ReadBarrierMarkSlowPathARMVIXL(HInstruction* instruction, + Location ref, + Location entrypoint = Location::NoLocation()) + : SlowPathCodeARMVIXL(instruction), ref_(ref), entrypoint_(entrypoint) { + DCHECK(kEmitCompilerReadBarrier); + } + + const char* GetDescription() const OVERRIDE { return "ReadBarrierMarkSlowPathARMVIXL"; } + + void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + LocationSummary* locations = instruction_->GetLocations(); + vixl32::Register ref_reg = RegisterFrom(ref_); + DCHECK(locations->CanCall()); + DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_reg.GetCode())) << ref_reg; + DCHECK(instruction_->IsInstanceFieldGet() || + instruction_->IsStaticFieldGet() || + instruction_->IsArrayGet() || + instruction_->IsArraySet() || + instruction_->IsLoadClass() || + instruction_->IsLoadString() || + instruction_->IsInstanceOf() || + instruction_->IsCheckCast() || + (instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()) || + (instruction_->IsInvokeStaticOrDirect() && instruction_->GetLocations()->Intrinsified())) + << "Unexpected instruction in read barrier marking slow path: " + << instruction_->DebugName(); + // The read barrier instrumentation of object ArrayGet + // instructions does not support the HIntermediateAddress + // instruction. + DCHECK(!(instruction_->IsArrayGet() && + instruction_->AsArrayGet()->GetArray()->IsIntermediateAddress())); + + __ Bind(GetEntryLabel()); + // No need to save live registers; it's taken care of by the + // entrypoint. Also, there is no need to update the stack mask, + // as this runtime call will not trigger a garbage collection. + CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen); + DCHECK(!ref_reg.Is(sp)); + DCHECK(!ref_reg.Is(lr)); + DCHECK(!ref_reg.Is(pc)); + // IP is used internally by the ReadBarrierMarkRegX entry point + // as a temporary, it cannot be the entry point's input/output. + DCHECK(!ref_reg.Is(ip)); + DCHECK(ref_reg.IsRegister()) << ref_reg; + // "Compact" slow path, saving two moves. + // + // Instead of using the standard runtime calling convention (input + // and output in R0): + // + // R0 <- ref + // R0 <- ReadBarrierMark(R0) + // ref <- R0 + // + // we just use rX (the register containing `ref`) as input and output + // of a dedicated entrypoint: + // + // rX <- ReadBarrierMarkRegX(rX) + // + if (entrypoint_.IsValid()) { + arm_codegen->ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction_, this); + __ Blx(RegisterFrom(entrypoint_)); + } else { + int32_t entry_point_offset = + CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArmPointerSize>(ref_reg.GetCode()); + // This runtime call does not require a stack map. + arm_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this); + } + __ B(GetExitLabel()); + } + + private: + // The location (register) of the marked object reference. + const Location ref_; + + // The location of the entrypoint if already loaded. + const Location entrypoint_; + + DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkSlowPathARMVIXL); +}; + +// Slow path marking an object reference `ref` during a read barrier, +// and if needed, atomically updating the field `obj.field` in the +// object `obj` holding this reference after marking (contrary to +// ReadBarrierMarkSlowPathARM above, which never tries to update +// `obj.field`). +// +// This means that after the execution of this slow path, both `ref` +// and `obj.field` will be up-to-date; i.e., after the flip, both will +// hold the same to-space reference (unless another thread installed +// another object reference (different from `ref`) in `obj.field`). +class ReadBarrierMarkAndUpdateFieldSlowPathARMVIXL : public SlowPathCodeARMVIXL { + public: + ReadBarrierMarkAndUpdateFieldSlowPathARMVIXL(HInstruction* instruction, + Location ref, + vixl32::Register obj, + Location field_offset, + vixl32::Register temp1, + vixl32::Register temp2) + : SlowPathCodeARMVIXL(instruction), + ref_(ref), + obj_(obj), + field_offset_(field_offset), + temp1_(temp1), + temp2_(temp2) { + DCHECK(kEmitCompilerReadBarrier); + } + + const char* GetDescription() const OVERRIDE { + return "ReadBarrierMarkAndUpdateFieldSlowPathARMVIXL"; + } + + void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + LocationSummary* locations = instruction_->GetLocations(); + vixl32::Register ref_reg = RegisterFrom(ref_); + DCHECK(locations->CanCall()); + DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_reg.GetCode())) << ref_reg; + // This slow path is only used by the UnsafeCASObject intrinsic. + DCHECK((instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified())) + << "Unexpected instruction in read barrier marking and field updating slow path: " + << instruction_->DebugName(); + DCHECK(instruction_->GetLocations()->Intrinsified()); + DCHECK_EQ(instruction_->AsInvoke()->GetIntrinsic(), Intrinsics::kUnsafeCASObject); + DCHECK(field_offset_.IsRegisterPair()) << field_offset_; + + __ Bind(GetEntryLabel()); + + // Save the old reference. + // Note that we cannot use IP to save the old reference, as IP is + // used internally by the ReadBarrierMarkRegX entry point, and we + // need the old reference after the call to that entry point. + DCHECK(!temp1_.Is(ip)); + __ Mov(temp1_, ref_reg); + + // No need to save live registers; it's taken care of by the + // entrypoint. Also, there is no need to update the stack mask, + // as this runtime call will not trigger a garbage collection. + CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen); + DCHECK(!ref_reg.Is(sp)); + DCHECK(!ref_reg.Is(lr)); + DCHECK(!ref_reg.Is(pc)); + // IP is used internally by the ReadBarrierMarkRegX entry point + // as a temporary, it cannot be the entry point's input/output. + DCHECK(!ref_reg.Is(ip)); + DCHECK(ref_reg.IsRegister()) << ref_reg; + // "Compact" slow path, saving two moves. + // + // Instead of using the standard runtime calling convention (input + // and output in R0): + // + // R0 <- ref + // R0 <- ReadBarrierMark(R0) + // ref <- R0 + // + // we just use rX (the register containing `ref`) as input and output + // of a dedicated entrypoint: + // + // rX <- ReadBarrierMarkRegX(rX) + // + int32_t entry_point_offset = + CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArmPointerSize>(ref_reg.GetCode()); + // This runtime call does not require a stack map. + arm_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this); + + // If the new reference is different from the old reference, + // update the field in the holder (`*(obj_ + field_offset_)`). + // + // Note that this field could also hold a different object, if + // another thread had concurrently changed it. In that case, the + // LDREX/SUBS/ITNE sequence of instructions in the compare-and-set + // (CAS) operation below would abort the CAS, leaving the field + // as-is. + vixl32::Label done; + __ Cmp(temp1_, ref_reg); + __ B(eq, &done); + + // Update the the holder's field atomically. This may fail if + // mutator updates before us, but it's OK. This is achieved + // using a strong compare-and-set (CAS) operation with relaxed + // memory synchronization ordering, where the expected value is + // the old reference and the desired value is the new reference. + + UseScratchRegisterScope temps(arm_codegen->GetVIXLAssembler()); + // Convenience aliases. + vixl32::Register base = obj_; + // The UnsafeCASObject intrinsic uses a register pair as field + // offset ("long offset"), of which only the low part contains + // data. + vixl32::Register offset = LowRegisterFrom(field_offset_); + vixl32::Register expected = temp1_; + vixl32::Register value = ref_reg; + vixl32::Register tmp_ptr = temps.Acquire(); // Pointer to actual memory. + vixl32::Register tmp = temp2_; // Value in memory. + + __ Add(tmp_ptr, base, offset); + + if (kPoisonHeapReferences) { + arm_codegen->GetAssembler()->PoisonHeapReference(expected); + if (value.Is(expected)) { + // Do not poison `value`, as it is the same register as + // `expected`, which has just been poisoned. + } else { + arm_codegen->GetAssembler()->PoisonHeapReference(value); + } + } + + // do { + // tmp = [r_ptr] - expected; + // } while (tmp == 0 && failure([r_ptr] <- r_new_value)); + + vixl32::Label loop_head, exit_loop; + __ Bind(&loop_head); + + __ Ldrex(tmp, MemOperand(tmp_ptr)); + + __ Subs(tmp, tmp, expected); + + { + AssemblerAccurateScope aas(arm_codegen->GetVIXLAssembler(), + 2 * kMaxInstructionSizeInBytes, + CodeBufferCheckScope::kMaximumSize); + + __ it(ne); + __ clrex(ne); + } + + __ B(ne, &exit_loop); + + __ Strex(tmp, value, MemOperand(tmp_ptr)); + __ Cmp(tmp, 1); + __ B(eq, &loop_head); + + __ Bind(&exit_loop); + + if (kPoisonHeapReferences) { + arm_codegen->GetAssembler()->UnpoisonHeapReference(expected); + if (value.Is(expected)) { + // Do not unpoison `value`, as it is the same register as + // `expected`, which has just been unpoisoned. + } else { + arm_codegen->GetAssembler()->UnpoisonHeapReference(value); + } + } + + __ Bind(&done); + __ B(GetExitLabel()); + } + + private: + // The location (register) of the marked object reference. + const Location ref_; + // The register containing the object holding the marked object reference field. + const vixl32::Register obj_; + // The location of the offset of the marked reference field within `obj_`. + Location field_offset_; + + const vixl32::Register temp1_; + const vixl32::Register temp2_; + + DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkAndUpdateFieldSlowPathARMVIXL); +}; + +// Slow path generating a read barrier for a heap reference. +class ReadBarrierForHeapReferenceSlowPathARMVIXL : public SlowPathCodeARMVIXL { + public: + ReadBarrierForHeapReferenceSlowPathARMVIXL(HInstruction* instruction, + Location out, + Location ref, + Location obj, + uint32_t offset, + Location index) + : SlowPathCodeARMVIXL(instruction), + out_(out), + ref_(ref), + obj_(obj), + offset_(offset), + index_(index) { + DCHECK(kEmitCompilerReadBarrier); + // If `obj` is equal to `out` or `ref`, it means the initial object + // has been overwritten by (or after) the heap object reference load + // to be instrumented, e.g.: + // + // __ LoadFromOffset(kLoadWord, out, out, offset); + // codegen_->GenerateReadBarrierSlow(instruction, out_loc, out_loc, out_loc, offset); + // + // In that case, we have lost the information about the original + // object, and the emitted read barrier cannot work properly. + DCHECK(!obj.Equals(out)) << "obj=" << obj << " out=" << out; + DCHECK(!obj.Equals(ref)) << "obj=" << obj << " ref=" << ref; + } + + void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen); + LocationSummary* locations = instruction_->GetLocations(); + vixl32::Register reg_out = RegisterFrom(out_); + DCHECK(locations->CanCall()); + DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out.GetCode())); + DCHECK(instruction_->IsInstanceFieldGet() || + instruction_->IsStaticFieldGet() || + instruction_->IsArrayGet() || + instruction_->IsInstanceOf() || + instruction_->IsCheckCast() || + (instruction_->IsInvokeVirtual()) && instruction_->GetLocations()->Intrinsified()) + << "Unexpected instruction in read barrier for heap reference slow path: " + << instruction_->DebugName(); + // The read barrier instrumentation of object ArrayGet + // instructions does not support the HIntermediateAddress + // instruction. + DCHECK(!(instruction_->IsArrayGet() && + instruction_->AsArrayGet()->GetArray()->IsIntermediateAddress())); + + __ Bind(GetEntryLabel()); + SaveLiveRegisters(codegen, locations); + + // We may have to change the index's value, but as `index_` is a + // constant member (like other "inputs" of this slow path), + // introduce a copy of it, `index`. + Location index = index_; + if (index_.IsValid()) { + // Handle `index_` for HArrayGet and UnsafeGetObject/UnsafeGetObjectVolatile intrinsics. + if (instruction_->IsArrayGet()) { + // Compute the actual memory offset and store it in `index`. + vixl32::Register index_reg = RegisterFrom(index_); + DCHECK(locations->GetLiveRegisters()->ContainsCoreRegister(index_reg.GetCode())); + if (codegen->IsCoreCalleeSaveRegister(index_reg.GetCode())) { + // We are about to change the value of `index_reg` (see the + // calls to art::arm::Thumb2Assembler::Lsl and + // art::arm::Thumb2Assembler::AddConstant below), but it has + // not been saved by the previous call to + // art::SlowPathCode::SaveLiveRegisters, as it is a + // callee-save register -- + // art::SlowPathCode::SaveLiveRegisters does not consider + // callee-save registers, as it has been designed with the + // assumption that callee-save registers are supposed to be + // handled by the called function. So, as a callee-save + // register, `index_reg` _would_ eventually be saved onto + // the stack, but it would be too late: we would have + // changed its value earlier. Therefore, we manually save + // it here into another freely available register, + // `free_reg`, chosen of course among the caller-save + // registers (as a callee-save `free_reg` register would + // exhibit the same problem). + // + // Note we could have requested a temporary register from + // the register allocator instead; but we prefer not to, as + // this is a slow path, and we know we can find a + // caller-save register that is available. + vixl32::Register free_reg = FindAvailableCallerSaveRegister(codegen); + __ Mov(free_reg, index_reg); + index_reg = free_reg; + index = LocationFrom(index_reg); + } else { + // The initial register stored in `index_` has already been + // saved in the call to art::SlowPathCode::SaveLiveRegisters + // (as it is not a callee-save register), so we can freely + // use it. + } + // Shifting the index value contained in `index_reg` by the scale + // factor (2) cannot overflow in practice, as the runtime is + // unable to allocate object arrays with a size larger than + // 2^26 - 1 (that is, 2^28 - 4 bytes). + __ Lsl(index_reg, index_reg, TIMES_4); + static_assert( + sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), + "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); + __ Add(index_reg, index_reg, offset_); + } else { + // 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)) + << instruction_->AsInvoke()->GetIntrinsic(); + DCHECK_EQ(offset_, 0U); + DCHECK(index_.IsRegisterPair()); + // UnsafeGet's offset location is a register pair, the low + // part contains the correct offset. + index = index_.ToLow(); + } + } + + // We're moving two or three locations to locations that could + // overlap, so we need a parallel move resolver. + InvokeRuntimeCallingConventionARMVIXL calling_convention; + HParallelMove parallel_move(codegen->GetGraph()->GetArena()); + parallel_move.AddMove(ref_, + LocationFrom(calling_convention.GetRegisterAt(0)), + Primitive::kPrimNot, + nullptr); + parallel_move.AddMove(obj_, + LocationFrom(calling_convention.GetRegisterAt(1)), + Primitive::kPrimNot, + nullptr); + if (index.IsValid()) { + parallel_move.AddMove(index, + LocationFrom(calling_convention.GetRegisterAt(2)), + Primitive::kPrimInt, + nullptr); + codegen->GetMoveResolver()->EmitNativeCode(¶llel_move); + } else { + codegen->GetMoveResolver()->EmitNativeCode(¶llel_move); + __ Mov(calling_convention.GetRegisterAt(2), offset_); + } + arm_codegen->InvokeRuntime(kQuickReadBarrierSlow, instruction_, instruction_->GetDexPc(), this); + CheckEntrypointTypes< + kQuickReadBarrierSlow, mirror::Object*, mirror::Object*, mirror::Object*, uint32_t>(); + arm_codegen->Move32(out_, LocationFrom(r0)); + + RestoreLiveRegisters(codegen, locations); + __ B(GetExitLabel()); + } + + const char* GetDescription() const OVERRIDE { + return "ReadBarrierForHeapReferenceSlowPathARMVIXL"; + } + + private: + vixl32::Register FindAvailableCallerSaveRegister(CodeGenerator* codegen) { + uint32_t ref = RegisterFrom(ref_).GetCode(); + uint32_t obj = RegisterFrom(obj_).GetCode(); + for (uint32_t i = 0, e = codegen->GetNumberOfCoreRegisters(); i < e; ++i) { + if (i != ref && i != obj && !codegen->IsCoreCalleeSaveRegister(i)) { + return vixl32::Register(i); + } + } + // We shall never fail to find a free caller-save register, as + // there are more than two core caller-save registers on ARM + // (meaning it is possible to find one which is different from + // `ref` and `obj`). + DCHECK_GT(codegen->GetNumberOfCoreCallerSaveRegisters(), 2u); + LOG(FATAL) << "Could not find a free caller-save register"; + UNREACHABLE(); + } + + const Location out_; + const Location ref_; + const Location obj_; + const uint32_t offset_; + // An additional location containing an index to an array. + // Only used for HArrayGet and the UnsafeGetObject & + // UnsafeGetObjectVolatile intrinsics. + const Location index_; + + DISALLOW_COPY_AND_ASSIGN(ReadBarrierForHeapReferenceSlowPathARMVIXL); +}; + +// Slow path generating a read barrier for a GC root. +class ReadBarrierForRootSlowPathARMVIXL : public SlowPathCodeARMVIXL { + public: + ReadBarrierForRootSlowPathARMVIXL(HInstruction* instruction, Location out, Location root) + : SlowPathCodeARMVIXL(instruction), out_(out), root_(root) { + DCHECK(kEmitCompilerReadBarrier); + } + + void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + LocationSummary* locations = instruction_->GetLocations(); + vixl32::Register reg_out = RegisterFrom(out_); + DCHECK(locations->CanCall()); + DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out.GetCode())); + DCHECK(instruction_->IsLoadClass() || instruction_->IsLoadString()) + << "Unexpected instruction in read barrier for GC root slow path: " + << instruction_->DebugName(); + + __ Bind(GetEntryLabel()); + SaveLiveRegisters(codegen, locations); + + InvokeRuntimeCallingConventionARMVIXL calling_convention; + CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen); + arm_codegen->Move32(LocationFrom(calling_convention.GetRegisterAt(0)), root_); + arm_codegen->InvokeRuntime(kQuickReadBarrierForRootSlow, + instruction_, + instruction_->GetDexPc(), + this); + CheckEntrypointTypes<kQuickReadBarrierForRootSlow, mirror::Object*, GcRoot<mirror::Object>*>(); + arm_codegen->Move32(out_, LocationFrom(r0)); + + RestoreLiveRegisters(codegen, locations); + __ B(GetExitLabel()); + } + + const char* GetDescription() const OVERRIDE { return "ReadBarrierForRootSlowPathARMVIXL"; } + + private: + const Location out_; + const Location root_; + + DISALLOW_COPY_AND_ASSIGN(ReadBarrierForRootSlowPathARMVIXL); +}; inline vixl32::Condition ARMCondition(IfCondition cond) { switch (cond) { @@ -4007,7 +4510,14 @@ void InstructionCodeGeneratorARMVIXL::HandleFieldGet(HInstruction* instruction, case Primitive::kPrimNot: { // /* HeapReference<Object> */ out = *(base + offset) if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) { - TODO_VIXL32(FATAL); + Location temp_loc = locations->GetTemp(0); + // Note that a potential implicit null check is handled in this + // CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier call. + codegen_->GenerateFieldLoadWithBakerReadBarrier( + instruction, out, base, offset, temp_loc, /* needs_null_check */ true); + if (is_volatile) { + codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny); + } } else { GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(out), base, offset); codegen_->MaybeRecordImplicitNullCheck(instruction); @@ -4334,7 +4844,7 @@ void LocationsBuilderARMVIXL::VisitArrayGet(HArrayGet* instruction) { LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall); if (object_array_get_with_read_barrier && kUseBakerReadBarrier) { - TODO_VIXL32(FATAL); + locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); @@ -4358,7 +4868,6 @@ void LocationsBuilderARMVIXL::VisitArrayGet(HArrayGet* instruction) { } void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) { - UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler()); LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); vixl32::Register obj = InputRegisterAt(instruction, 0); @@ -4370,8 +4879,6 @@ void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) { instruction->IsStringCharAt(); HInstruction* array_instr = instruction->GetArray(); bool has_intermediate_address = array_instr->IsIntermediateAddress(); - // The read barrier instrumentation does not support the HIntermediateAddress instruction yet. - DCHECK(!(has_intermediate_address && kEmitCompilerReadBarrier)); switch (type) { case Primitive::kPrimBoolean: @@ -4412,6 +4919,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) { GetAssembler()->LoadFromOffset(load_type, RegisterFrom(out_loc), obj, full_offset); } } else { + UseScratchRegisterScope temps(GetVIXLAssembler()); vixl32::Register temp = temps.Acquire(); if (has_intermediate_address) { @@ -4440,19 +4948,27 @@ void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) { } else { codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, RegisterFrom(index)); } - temps.Release(temp); } break; } case Primitive::kPrimNot: { + // The read barrier instrumentation of object ArrayGet + // instructions does not support the HIntermediateAddress + // instruction. + DCHECK(!(has_intermediate_address && kEmitCompilerReadBarrier)); + static_assert( sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); // /* HeapReference<Object> */ out = // *(obj + data_offset + index * sizeof(HeapReference<Object>)) if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) { - TODO_VIXL32(FATAL); + Location temp = locations->GetTemp(0); + // Note that a potential implicit null check is handled in this + // CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier call. + codegen_->GenerateArrayLoadWithBakerReadBarrier( + instruction, out_loc, obj, data_offset, index, temp, /* needs_null_check */ true); } else { vixl32::Register out = OutputRegister(instruction); if (index.IsConstant()) { @@ -4470,6 +4986,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) { // reference, if heap poisoning is enabled). codegen_->MaybeGenerateReadBarrierSlow(instruction, out_loc, out_loc, obj_loc, offset); } else { + UseScratchRegisterScope temps(GetVIXLAssembler()); vixl32::Register temp = temps.Acquire(); if (has_intermediate_address) { @@ -4485,7 +5002,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) { __ Add(temp, obj, data_offset); } codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, RegisterFrom(index)); - temps.Release(temp); + temps.Close(); // TODO(VIXL): Use a scope to ensure that we record the pc position immediately after the // load instruction. Practically, everything is fine because the helper and VIXL, at the // time of writing, do generate the store instruction last. @@ -4506,10 +5023,10 @@ void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) { (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out_loc), obj, offset); } else { + UseScratchRegisterScope temps(GetVIXLAssembler()); vixl32::Register temp = temps.Acquire(); __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8)); GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out_loc), temp, data_offset); - temps.Release(temp); } break; } @@ -4520,10 +5037,10 @@ void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) { size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; GetAssembler()->LoadSFromOffset(out, obj, offset); } else { + UseScratchRegisterScope temps(GetVIXLAssembler()); vixl32::Register temp = temps.Acquire(); __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_4)); GetAssembler()->LoadSFromOffset(out, temp, data_offset); - temps.Release(temp); } break; } @@ -4533,10 +5050,10 @@ void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) { size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; GetAssembler()->LoadDFromOffset(DRegisterFrom(out_loc), obj, offset); } else { + UseScratchRegisterScope temps(GetVIXLAssembler()); vixl32::Register temp = temps.Acquire(); __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8)); GetAssembler()->LoadDFromOffset(DRegisterFrom(out_loc), temp, data_offset); - temps.Release(temp); } break; } @@ -4584,7 +5101,6 @@ void LocationsBuilderARMVIXL::VisitArraySet(HArraySet* instruction) { } void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) { - UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler()); LocationSummary* locations = instruction->GetLocations(); vixl32::Register array = InputRegisterAt(instruction, 0); Location index = locations->InAt(1); @@ -4597,8 +5113,6 @@ void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) { Location value_loc = locations->InAt(2); HInstruction* array_instr = instruction->GetArray(); bool has_intermediate_address = array_instr->IsIntermediateAddress(); - // The read barrier instrumentation does not support the HIntermediateAddress instruction yet. - DCHECK(!(has_intermediate_address && kEmitCompilerReadBarrier)); switch (value_type) { case Primitive::kPrimBoolean: @@ -4613,6 +5127,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) { StoreOperandType store_type = GetStoreOperandType(value_type); GetAssembler()->StoreToOffset(store_type, RegisterFrom(value_loc), array, full_offset); } else { + UseScratchRegisterScope temps(GetVIXLAssembler()); vixl32::Register temp = temps.Acquire(); if (has_intermediate_address) { @@ -4628,7 +5143,6 @@ void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) { __ Add(temp, array, data_offset); } codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index)); - temps.Release(temp); } break; } @@ -4647,10 +5161,10 @@ void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) { GetAssembler()->StoreToOffset(kStoreWord, value, array, offset); } else { DCHECK(index.IsRegister()) << index; + UseScratchRegisterScope temps(GetVIXLAssembler()); vixl32::Register temp = temps.Acquire(); __ Add(temp, array, data_offset); codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index)); - temps.Release(temp); } // TODO(VIXL): Use a scope to ensure we record the pc info immediately after the preceding // store instruction. @@ -4683,10 +5197,10 @@ void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) { GetAssembler()->StoreToOffset(kStoreWord, value, array, offset); } else { DCHECK(index.IsRegister()) << index; + UseScratchRegisterScope temps(GetVIXLAssembler()); vixl32::Register temp = temps.Acquire(); __ Add(temp, array, data_offset); codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index)); - temps.Release(temp); } // TODO(VIXL): Use a scope to ensure we record the pc info immediately after the preceding // store instruction. @@ -4758,13 +5272,13 @@ void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) { } else { DCHECK(index.IsRegister()) << index; + UseScratchRegisterScope temps(GetVIXLAssembler()); vixl32::Register temp = temps.Acquire(); __ Add(temp, array, data_offset); codegen_->StoreToShiftedRegOffset(value_type, LocationFrom(source), temp, RegisterFrom(index)); - temps.Release(temp); } if (!may_need_runtime_call_for_type_check) { @@ -4793,10 +5307,10 @@ void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) { (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), array, offset); } else { + UseScratchRegisterScope temps(GetVIXLAssembler()); vixl32::Register temp = temps.Acquire(); __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8)); GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), temp, data_offset); - temps.Release(temp); } break; } @@ -4808,10 +5322,10 @@ void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) { size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; GetAssembler()->StoreSToOffset(SRegisterFrom(value), array, offset); } else { + UseScratchRegisterScope temps(GetVIXLAssembler()); vixl32::Register temp = temps.Acquire(); __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_4)); GetAssembler()->StoreSToOffset(SRegisterFrom(value), temp, data_offset); - temps.Release(temp); } break; } @@ -4823,10 +5337,10 @@ void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) { size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; GetAssembler()->StoreDToOffset(DRegisterFrom(value), array, offset); } else { + UseScratchRegisterScope temps(GetVIXLAssembler()); vixl32::Register temp = temps.Acquire(); __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8)); GetAssembler()->StoreDToOffset(DRegisterFrom(value), temp, data_offset); - temps.Release(temp); } break; } @@ -4869,8 +5383,6 @@ void InstructionCodeGeneratorARMVIXL::VisitArrayLength(HArrayLength* instruction } void LocationsBuilderARMVIXL::VisitIntermediateAddress(HIntermediateAddress* instruction) { - // The read barrier instrumentation does not support the HIntermediateAddress instruction yet. - DCHECK(!kEmitCompilerReadBarrier); LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); @@ -4884,9 +5396,6 @@ void InstructionCodeGeneratorARMVIXL::VisitIntermediateAddress(HIntermediateAddr vixl32::Register first = InputRegisterAt(instruction, 0); Location second = instruction->GetLocations()->InAt(1); - // The read barrier instrumentation does not support the HIntermediateAddress instruction yet. - DCHECK(!kEmitCompilerReadBarrier); - if (second.IsRegister()) { __ Add(out, first, RegisterFrom(second)); } else { @@ -4978,7 +5487,7 @@ void InstructionCodeGeneratorARMVIXL::GenerateSuspendCheck(HSuspendCheck* instru DCHECK_EQ(slow_path->GetSuccessor(), successor); } - UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler()); + UseScratchRegisterScope temps(GetVIXLAssembler()); vixl32::Register temp = temps.Acquire(); GetAssembler()->LoadFromOffset( kLoadUnsignedHalfword, temp, tr, Thread::ThreadFlagsOffset<kArmPointerSize>().Int32Value()); @@ -5289,7 +5798,7 @@ void LocationsBuilderARMVIXL::VisitLoadClass(HLoadClass* cls) { : LocationSummary::kNoCall; LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind); if (kUseBakerReadBarrier && requires_read_barrier && !cls->NeedsEnvironment()) { - TODO_VIXL32(FATAL); + locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } HLoadClass::LoadKind load_kind = cls->GetLoadKind(); @@ -6336,14 +6845,30 @@ void InstructionCodeGeneratorARMVIXL::HandleBitwiseOperation(HBinaryOperation* i } void InstructionCodeGeneratorARMVIXL::GenerateReferenceLoadOneRegister( - HInstruction* instruction ATTRIBUTE_UNUSED, + HInstruction* instruction, Location out, uint32_t offset, - Location maybe_temp ATTRIBUTE_UNUSED, - ReadBarrierOption read_barrier_option ATTRIBUTE_UNUSED) { + Location maybe_temp, + ReadBarrierOption read_barrier_option) { vixl32::Register out_reg = RegisterFrom(out); - if (kEmitCompilerReadBarrier) { - TODO_VIXL32(FATAL); + if (read_barrier_option == kWithReadBarrier) { + CHECK(kEmitCompilerReadBarrier); + DCHECK(maybe_temp.IsRegister()) << maybe_temp; + if (kUseBakerReadBarrier) { + // Load with fast path based Baker's read barrier. + // /* HeapReference<Object> */ out = *(out + offset) + codegen_->GenerateFieldLoadWithBakerReadBarrier( + instruction, out, out_reg, offset, maybe_temp, /* needs_null_check */ false); + } else { + // Load with slow path based read barrier. + // Save the value of `out` into `maybe_temp` before overwriting it + // in the following move operation, as we will need it for the + // read barrier below. + __ Mov(RegisterFrom(maybe_temp), out_reg); + // /* HeapReference<Object> */ out = *(out + offset) + GetAssembler()->LoadFromOffset(kLoadWord, out_reg, out_reg, offset); + codegen_->GenerateReadBarrierSlow(instruction, out, out, maybe_temp, offset); + } } else { // Plain load with no read barrier. // /* HeapReference<Object> */ out = *(out + offset) @@ -6353,16 +6878,28 @@ void InstructionCodeGeneratorARMVIXL::GenerateReferenceLoadOneRegister( } void InstructionCodeGeneratorARMVIXL::GenerateReferenceLoadTwoRegisters( - HInstruction* instruction ATTRIBUTE_UNUSED, + HInstruction* instruction, Location out, Location obj, uint32_t offset, - Location maybe_temp ATTRIBUTE_UNUSED, - ReadBarrierOption read_barrier_option ATTRIBUTE_UNUSED) { + Location maybe_temp, + ReadBarrierOption read_barrier_option) { vixl32::Register out_reg = RegisterFrom(out); vixl32::Register obj_reg = RegisterFrom(obj); - if (kEmitCompilerReadBarrier) { - TODO_VIXL32(FATAL); + if (read_barrier_option == kWithReadBarrier) { + CHECK(kEmitCompilerReadBarrier); + if (kUseBakerReadBarrier) { + DCHECK(maybe_temp.IsRegister()) << maybe_temp; + // Load with fast path based Baker's read barrier. + // /* HeapReference<Object> */ out = *(obj + offset) + codegen_->GenerateFieldLoadWithBakerReadBarrier( + instruction, out, obj_reg, offset, maybe_temp, /* needs_null_check */ false); + } else { + // Load with slow path based read barrier. + // /* HeapReference<Object> */ out = *(obj + offset) + GetAssembler()->LoadFromOffset(kLoadWord, out_reg, obj_reg, offset); + codegen_->GenerateReadBarrierSlow(instruction, out, out, obj, offset); + } } else { // Plain load with no read barrier. // /* HeapReference<Object> */ out = *(obj + offset) @@ -6372,14 +6909,61 @@ void InstructionCodeGeneratorARMVIXL::GenerateReferenceLoadTwoRegisters( } void InstructionCodeGeneratorARMVIXL::GenerateGcRootFieldLoad( - HInstruction* instruction ATTRIBUTE_UNUSED, + HInstruction* instruction, Location root, vixl32::Register obj, uint32_t offset, ReadBarrierOption read_barrier_option) { vixl32::Register root_reg = RegisterFrom(root); if (read_barrier_option == kWithReadBarrier) { - TODO_VIXL32(FATAL); + DCHECK(kEmitCompilerReadBarrier); + if (kUseBakerReadBarrier) { + // Fast path implementation of art::ReadBarrier::BarrierForRoot when + // Baker's read barrier are used: + // + // root = obj.field; + // temp = Thread::Current()->pReadBarrierMarkReg ## root.reg() + // if (temp != null) { + // root = temp(root) + // } + + // /* GcRoot<mirror::Object> */ root = *(obj + offset) + GetAssembler()->LoadFromOffset(kLoadWord, root_reg, obj, offset); + static_assert( + sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(GcRoot<mirror::Object>), + "art::mirror::CompressedReference<mirror::Object> and art::GcRoot<mirror::Object> " + "have different sizes."); + static_assert(sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(int32_t), + "art::mirror::CompressedReference<mirror::Object> and int32_t " + "have different sizes."); + + // Slow path marking the GC root `root`. + Location temp = LocationFrom(lr); + SlowPathCodeARMVIXL* slow_path = + new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathARMVIXL( + instruction, + root, + /*entrypoint*/ temp); + codegen_->AddSlowPath(slow_path); + + // temp = Thread::Current()->pReadBarrierMarkReg ## root.reg() + const int32_t entry_point_offset = + CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kArmPointerSize>(root.reg()); + // Loading the entrypoint does not require a load acquire since it is only changed when + // threads are suspended or running a checkpoint. + GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(temp), tr, entry_point_offset); + // The entrypoint is null when the GC is not marking, this prevents one load compared to + // checking GetIsGcMarking. + __ CompareAndBranchIfNonZero(RegisterFrom(temp), slow_path->GetEntryLabel()); + __ Bind(slow_path->GetExitLabel()); + } else { + // GC root loaded through a slow path for read barriers other + // than Baker's. + // /* GcRoot<mirror::Object>* */ root = obj + offset + __ Add(root_reg, obj, offset); + // /* mirror::Object* */ root = root->Read() + codegen_->GenerateReadBarrierForRootSlow(instruction, root, root); + } } else { // Plain GC root load with no read barrier. // /* GcRoot<mirror::Object> */ root = *(obj + offset) @@ -6389,53 +6973,217 @@ void InstructionCodeGeneratorARMVIXL::GenerateGcRootFieldLoad( } } -void CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier( - HInstruction* instruction ATTRIBUTE_UNUSED, - Location ref ATTRIBUTE_UNUSED, - vixl::aarch32::Register obj ATTRIBUTE_UNUSED, - uint32_t offset ATTRIBUTE_UNUSED, - Location temp ATTRIBUTE_UNUSED, - bool needs_null_check ATTRIBUTE_UNUSED) { - TODO_VIXL32(FATAL); -} +void CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction, + Location ref, + vixl32::Register obj, + uint32_t offset, + Location temp, + bool needs_null_check) { + DCHECK(kEmitCompilerReadBarrier); + DCHECK(kUseBakerReadBarrier); + + // /* HeapReference<Object> */ ref = *(obj + offset) + Location no_index = Location::NoLocation(); + ScaleFactor no_scale_factor = TIMES_1; + GenerateReferenceLoadWithBakerReadBarrier( + instruction, ref, obj, offset, no_index, no_scale_factor, temp, needs_null_check); +} + +void CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction, + Location ref, + vixl32::Register obj, + uint32_t data_offset, + Location index, + Location temp, + bool needs_null_check) { + 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, scale_factor, temp, needs_null_check); +} + +void CodeGeneratorARMVIXL::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction, + Location ref, + vixl32::Register obj, + uint32_t offset, + Location index, + ScaleFactor scale_factor, + Location temp, + bool needs_null_check, + bool always_update_field, + vixl32::Register* temp2) { + DCHECK(kEmitCompilerReadBarrier); + DCHECK(kUseBakerReadBarrier); + + // In slow path based read barriers, the read barrier call is + // inserted after the original load. However, in fast path based + // Baker's read barriers, we need to perform the load of + // mirror::Object::monitor_ *before* the original reference load. + // This load-load ordering is required by the read barrier. + // The fast path/slow path (for Baker's algorithm) should look like: + // + // uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState(); + // lfence; // Load fence or artificial data dependency to prevent load-load reordering + // HeapReference<Object> ref = *src; // Original reference load. + // bool is_gray = (rb_state == ReadBarrier::GrayState()); + // if (is_gray) { + // ref = ReadBarrier::Mark(ref); // Performed by runtime entrypoint slow path. + // } + // + // Note: the original implementation in ReadBarrier::Barrier is + // slightly more complex as it performs additional checks that we do + // not do here for performance reasons. + + vixl32::Register ref_reg = RegisterFrom(ref); + vixl32::Register temp_reg = RegisterFrom(temp); + uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value(); + + // /* int32_t */ monitor = obj->monitor_ + GetAssembler()->LoadFromOffset(kLoadWord, temp_reg, obj, monitor_offset); + if (needs_null_check) { + MaybeRecordImplicitNullCheck(instruction); + } + // /* LockWord */ lock_word = LockWord(monitor) + static_assert(sizeof(LockWord) == sizeof(int32_t), + "art::LockWord and int32_t have different sizes."); + + // Introduce a dependency on the lock_word including the rb_state, + // which shall prevent load-load reordering without using + // a memory barrier (which would be more expensive). + // `obj` is unchanged by this operation, but its value now depends + // on `temp_reg`. + __ Add(obj, obj, Operand(temp_reg, ShiftType::LSR, 32)); + + // The actual reference load. + if (index.IsValid()) { + // Load types involving an "index": ArrayGet, + // UnsafeGetObject/UnsafeGetObjectVolatile and UnsafeCASObject + // intrinsics. + // /* HeapReference<Object> */ ref = *(obj + offset + (index << scale_factor)) + if (index.IsConstant()) { + size_t computed_offset = + (Int32ConstantFrom(index) << scale_factor) + offset; + GetAssembler()->LoadFromOffset(kLoadWord, ref_reg, obj, computed_offset); + } else { + // Handle the special case of the + // UnsafeGetObject/UnsafeGetObjectVolatile and UnsafeCASObject + // intrinsics, which use a register pair as index ("long + // offset"), of which only the low part contains data. + vixl32::Register index_reg = index.IsRegisterPair() + ? LowRegisterFrom(index) + : RegisterFrom(index); + UseScratchRegisterScope temps(GetVIXLAssembler()); + const vixl32::Register temp3 = temps.Acquire(); + __ Add(temp3, obj, Operand(index_reg, ShiftType::LSL, scale_factor)); + GetAssembler()->LoadFromOffset(kLoadWord, ref_reg, temp3, offset); + } + } else { + // /* HeapReference<Object> */ ref = *(obj + offset) + GetAssembler()->LoadFromOffset(kLoadWord, ref_reg, obj, offset); + } + + // Object* ref = ref_addr->AsMirrorPtr() + GetAssembler()->MaybeUnpoisonHeapReference(ref_reg); + + // Slow path marking the object `ref` when it is gray. + SlowPathCodeARMVIXL* slow_path; + if (always_update_field) { + DCHECK(temp2 != nullptr); + // ReadBarrierMarkAndUpdateFieldSlowPathARMVIXL only supports address + // of the form `obj + field_offset`, where `obj` is a register and + // `field_offset` is a register pair (of which only the lower half + // is used). Thus `offset` and `scale_factor` above are expected + // to be null in this code path. + DCHECK_EQ(offset, 0u); + DCHECK_EQ(scale_factor, ScaleFactor::TIMES_1); + slow_path = new (GetGraph()->GetArena()) ReadBarrierMarkAndUpdateFieldSlowPathARMVIXL( + instruction, ref, obj, /* field_offset */ index, temp_reg, *temp2); + } else { + slow_path = new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathARMVIXL(instruction, ref); + } + AddSlowPath(slow_path); -void CodeGeneratorARMVIXL::GenerateReferenceLoadWithBakerReadBarrier( - HInstruction* instruction ATTRIBUTE_UNUSED, - Location ref ATTRIBUTE_UNUSED, - vixl::aarch32::Register obj ATTRIBUTE_UNUSED, - uint32_t offset ATTRIBUTE_UNUSED, - Location index ATTRIBUTE_UNUSED, - ScaleFactor scale_factor ATTRIBUTE_UNUSED, - Location temp ATTRIBUTE_UNUSED, - bool needs_null_check ATTRIBUTE_UNUSED, - bool always_update_field ATTRIBUTE_UNUSED, - vixl::aarch32::Register* temp2 ATTRIBUTE_UNUSED) { - TODO_VIXL32(FATAL); + // if (rb_state == ReadBarrier::GrayState()) + // ref = ReadBarrier::Mark(ref); + // Given the numeric representation, it's enough to check the low bit of the + // rb_state. We do that by shifting the bit out of the lock word with LSRS + // which can be a 16-bit instruction unlike the TST immediate. + static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0"); + static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1"); + __ Lsrs(temp_reg, temp_reg, LockWord::kReadBarrierStateShift + 1); + __ B(cs, slow_path->GetEntryLabel()); // Carry flag is the last bit shifted out by LSRS. + __ Bind(slow_path->GetExitLabel()); } -void CodeGeneratorARMVIXL::GenerateReadBarrierSlow(HInstruction* instruction ATTRIBUTE_UNUSED, - Location out ATTRIBUTE_UNUSED, - Location ref ATTRIBUTE_UNUSED, - Location obj ATTRIBUTE_UNUSED, - uint32_t offset ATTRIBUTE_UNUSED, - Location index ATTRIBUTE_UNUSED) { - TODO_VIXL32(FATAL); +void CodeGeneratorARMVIXL::GenerateReadBarrierSlow(HInstruction* instruction, + Location out, + Location ref, + Location obj, + uint32_t offset, + Location index) { + DCHECK(kEmitCompilerReadBarrier); + + // Insert a slow path based read barrier *after* the reference load. + // + // If heap poisoning is enabled, the unpoisoning of the loaded + // reference will be carried out by the runtime within the slow + // path. + // + // Note that `ref` currently does not get unpoisoned (when heap + // poisoning is enabled), which is alright as the `ref` argument is + // not used by the artReadBarrierSlow entry point. + // + // TODO: Unpoison `ref` when it is used by artReadBarrierSlow. + SlowPathCodeARMVIXL* slow_path = new (GetGraph()->GetArena()) + ReadBarrierForHeapReferenceSlowPathARMVIXL(instruction, out, ref, obj, offset, index); + AddSlowPath(slow_path); + + __ B(slow_path->GetEntryLabel()); + __ Bind(slow_path->GetExitLabel()); } -void CodeGeneratorARMVIXL::MaybeGenerateReadBarrierSlow(HInstruction* instruction ATTRIBUTE_UNUSED, +void CodeGeneratorARMVIXL::MaybeGenerateReadBarrierSlow(HInstruction* instruction, Location out, - Location ref ATTRIBUTE_UNUSED, - Location obj ATTRIBUTE_UNUSED, - uint32_t offset ATTRIBUTE_UNUSED, - Location index ATTRIBUTE_UNUSED) { + Location ref, + Location obj, + uint32_t offset, + Location index) { if (kEmitCompilerReadBarrier) { + // Baker's read barriers shall be handled by the fast path + // (CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier). DCHECK(!kUseBakerReadBarrier); - TODO_VIXL32(FATAL); + // If heap poisoning is enabled, unpoisoning will be taken care of + // by the runtime within the slow path. + GenerateReadBarrierSlow(instruction, out, ref, obj, offset, index); } else if (kPoisonHeapReferences) { GetAssembler()->UnpoisonHeapReference(RegisterFrom(out)); } } +void CodeGeneratorARMVIXL::GenerateReadBarrierForRootSlow(HInstruction* instruction, + Location out, + Location root) { + DCHECK(kEmitCompilerReadBarrier); + + // Insert a slow path based read barrier *after* the GC root load. + // + // Note that GC roots are not affected by heap poisoning, so we do + // not need to do anything special for this here. + SlowPathCodeARMVIXL* slow_path = + new (GetGraph()->GetArena()) ReadBarrierForRootSlowPathARMVIXL(instruction, out, root); + AddSlowPath(slow_path); + + __ B(slow_path->GetEntryLabel()); + __ Bind(slow_path->GetExitLabel()); +} + // Check if the desired_dispatch_info is supported. If it is, return it, // otherwise return a fall-back info that should be used instead. HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARMVIXL::GetSupportedInvokeStaticOrDirectDispatch( @@ -6805,7 +7553,7 @@ void InstructionCodeGeneratorARMVIXL::VisitPackedSwitch(HPackedSwitch* switch_in if (num_entries <= kPackedSwitchCompareJumpThreshold || !codegen_->GetAssembler()->GetVIXLAssembler()->IsUsingT32()) { // Create a series of compare/jumps. - UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler()); + UseScratchRegisterScope temps(GetVIXLAssembler()); vixl32::Register temp_reg = temps.Acquire(); // Note: It is fine for the below AddConstantSetFlags() using IP register to temporarily store // the immediate, because IP is used as the destination register. For the other @@ -6853,7 +7601,7 @@ void InstructionCodeGeneratorARMVIXL::VisitPackedSwitch(HPackedSwitch* switch_in __ Cmp(key_reg, num_entries - 1); __ B(hi, codegen_->GetLabelOf(default_block)); - UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler()); + UseScratchRegisterScope temps(GetVIXLAssembler()); vixl32::Register jump_offset = temps.Acquire(); // Load jump offset from the table. diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h index b7ba8ddf0d..5ec3da4652 100644 --- a/compiler/optimizing/code_generator_arm_vixl.h +++ b/compiler/optimizing/code_generator_arm_vixl.h @@ -576,7 +576,15 @@ class CodeGeneratorARMVIXL : public CodeGenerator { uint32_t offset, Location temp, bool needs_null_check); - + // Fast path implementation of ReadBarrier::Barrier for a heap + // reference array load when Baker's read barriers are used. + void GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction, + Location ref, + vixl::aarch32::Register obj, + uint32_t data_offset, + Location index, + Location temp, + bool needs_null_check); // Factored implementation, used by GenerateFieldLoadWithBakerReadBarrier, // GenerateArrayLoadWithBakerReadBarrier and some intrinsics. // @@ -634,6 +642,19 @@ class CodeGeneratorARMVIXL : public CodeGenerator { uint32_t offset, Location index = Location::NoLocation()); + // Generate a read barrier for a GC root within `instruction` using + // a slow path. + // + // A read barrier for an object reference GC root is implemented as + // a call to the artReadBarrierForRootSlow runtime entry point, + // which is passed the value in location `root`: + // + // mirror::Object* artReadBarrierForRootSlow(GcRoot<mirror::Object>* root); + // + // The `out` location contains the value returned by + // artReadBarrierForRootSlow. + void GenerateReadBarrierForRootSlow(HInstruction* instruction, Location out, Location root); + void GenerateNop() OVERRIDE; void GenerateImplicitNullCheck(HNullCheck* instruction) OVERRIDE; diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc index 15615022e3..c240c67e79 100644 --- a/compiler/optimizing/induction_var_analysis.cc +++ b/compiler/optimizing/induction_var_analysis.cc @@ -296,21 +296,22 @@ void HInductionVarAnalysis::ClassifyNonTrivial(HLoopInformation* loop) { update = SolveAddSub( loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kSub, true); } else if (instruction->IsMul()) { - update = SolveGeo( + update = SolveOp( loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kMul); } else if (instruction->IsDiv()) { - update = SolveGeo( + update = SolveOp( loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kDiv); } else if (instruction->IsRem()) { - update = SolveGeo( - loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kNop); + update = SolveOp( + loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kRem); } else if (instruction->IsShl()) { HInstruction* mulc = GetMultConstantForShift(loop, instruction); if (mulc != nullptr) { - update = SolveGeo(loop, phi, instruction, instruction->InputAt(0), mulc, kMul); + update = SolveOp(loop, phi, instruction, instruction->InputAt(0), mulc, kMul); } } else if (instruction->IsXor()) { - update = SolveXor(loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1)); + update = SolveOp( + loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kXor); } else if (instruction->IsEqual()) { update = SolveTest(loop, phi, instruction, 0); } else if (instruction->IsNotEqual()) { @@ -334,6 +335,7 @@ void HInductionVarAnalysis::ClassifyNonTrivial(HLoopInformation* loop) { FALLTHROUGH_INTENDED; case kPolynomial: case kGeometric: + case kWrapAround: // Classify first phi and then the rest of the cycle "on-demand". // Statements are scanned in order. AssignInfo(loop, phi, induction); @@ -402,14 +404,15 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferAddSub(Indu InductionOp op) { // Transfer over an addition or subtraction: any invariant, linear, polynomial, geometric, // wrap-around, or periodic can be combined with an invariant to yield a similar result. - // Even two linear inputs can be combined. Other combinations fail. + // Two linear or two polynomial inputs can be combined too. Other combinations fail. if (a != nullptr && b != nullptr) { type_ = Narrowest(type_, Narrowest(a->type, b->type)); if (a->induction_class == kInvariant && b->induction_class == kInvariant) { return CreateInvariantOp(op, a, b); - } else if (a->induction_class == kLinear && b->induction_class == kLinear) { - return CreateInduction(kLinear, - kNop, + } else if ((a->induction_class == kLinear && b->induction_class == kLinear) || + (a->induction_class == kPolynomial && b->induction_class == kPolynomial)) { + return CreateInduction(a->induction_class, + a->operation, TransferAddSub(a->op_a, b->op_a, op), TransferAddSub(a->op_b, b->op_b, op), /*fetch*/ nullptr, @@ -555,18 +558,33 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveAddSub(HLoopIn HInstruction* y, InductionOp op, bool is_first_call) { - // Solve within a cycle over an addition or subtraction: adding or subtracting an - // invariant value, seeded from phi, keeps adding to the stride of the linear induction. + // Solve within a cycle over an addition or subtraction. InductionInfo* b = LookupInfo(loop, y); - if (b != nullptr && b->induction_class == kInvariant) { - if (x == entry_phi) { - return (op == kAdd) ? b : CreateInvariantOp(kNeg, nullptr, b); - } - auto it = cycle_.find(x); - if (it != cycle_.end()) { - InductionInfo* a = it->second; - if (a->induction_class == kInvariant) { - return CreateInvariantOp(op, a, b); + if (b != nullptr) { + if (b->induction_class == kInvariant) { + // Adding or subtracting an invariant value, seeded from phi, + // keeps adding to the stride of the linear induction. + if (x == entry_phi) { + return (op == kAdd) ? b : CreateInvariantOp(kNeg, nullptr, b); + } + auto it = cycle_.find(x); + if (it != cycle_.end()) { + InductionInfo* a = it->second; + if (a->induction_class == kInvariant) { + return CreateInvariantOp(op, a, b); + } + } + } else if (op == kAdd && b->induction_class == kLinear) { + // Solve within a tight cycle that adds a term that is already classified as a linear + // induction for a polynomial induction k = k + i (represented as sum over linear terms). + if (x == entry_phi && entry_phi->InputCount() == 2 && instruction == entry_phi->InputAt(1)) { + InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0)); + return CreateInduction(kPolynomial, + kNop, + b, + initial, + /*fetch*/ nullptr, + type_); } } } @@ -593,58 +611,63 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveAddSub(HLoopIn } } } - return nullptr; } -HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveGeo(HLoopInformation* loop, +HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveOp(HLoopInformation* loop, HInstruction* entry_phi, HInstruction* instruction, HInstruction* x, HInstruction* y, InductionOp op) { - // Solve within a tight cycle that is formed by exactly two instructions, one phi and - // one update, for a geometric induction of the form k = k * c. - if (x == entry_phi && entry_phi->InputCount() == 2 && instruction == entry_phi->InputAt(1)) { - InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0)); - InductionInfo* b = LookupInfo(loop, y); - if (b != nullptr && b->induction_class == kInvariant && b->operation == kFetch) { - return CreateInduction(kGeometric, - op, - initial, - CreateConstant(0, type_), - b->fetch, - type_); - } - } - return nullptr; -} - -HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveXor(HLoopInformation* loop, - HInstruction* entry_phi, - HInstruction* instruction, - HInstruction* x, - HInstruction* y) { - // Solve within a tight cycle on periodic idiom of the form x = c ^ x or x = x ^ c. + // Solve within a tight cycle for a binary operation k = k op c or, for some op, k = c op k. if (entry_phi->InputCount() == 2 && instruction == entry_phi->InputAt(1)) { - InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0)); - InductionInfo* a = LookupInfo(loop, x); - if (a != nullptr && a->induction_class == kInvariant && entry_phi == y) { - return CreateInduction(kPeriodic, - kNop, - CreateInvariantOp(kXor, a, initial), - initial, - /*fetch*/ nullptr, - type_); - } + InductionInfo* c = nullptr; InductionInfo* b = LookupInfo(loop, y); if (b != nullptr && b->induction_class == kInvariant && entry_phi == x) { - return CreateInduction(kPeriodic, - kNop, - CreateInvariantOp(kXor, initial, b), - initial, - /*fetch*/ nullptr, - type_); + c = b; + } else if (op != kDiv && op != kRem) { + InductionInfo* a = LookupInfo(loop, x); + if (a != nullptr && a->induction_class == kInvariant && entry_phi == y) { + c = a; + } + } + // Found suitable operand left or right? + if (c != nullptr) { + InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0)); + switch (op) { + case kMul: + case kDiv: + // Restrict base of geometric induction to direct fetch. + if (c->operation == kFetch) { + return CreateInduction(kGeometric, + op, + initial, + CreateConstant(0, type_), + c->fetch, + type_); + }; + break; + case kRem: + // Idiomatic MOD wrap-around induction. + return CreateInduction(kWrapAround, + kNop, + initial, + CreateInvariantOp(kRem, initial, c), + /*fetch*/ nullptr, + type_); + case kXor: + // Idiomatic XOR periodic induction. + return CreateInduction(kPeriodic, + kNop, + CreateInvariantOp(kXor, initial, c), + initial, + /*fetch*/ nullptr, + type_); + default: + CHECK(false) << op; + break; + } } } return nullptr; @@ -659,9 +682,9 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveTest(HLoopInfo HInstruction* x = instruction->InputAt(0); HInstruction* y = instruction->InputAt(1); if (IsExact(LookupInfo(loop, x), &value) && value == opposite_value) { - return SolveXor(loop, entry_phi, instruction, graph_->GetIntConstant(1), y); + return SolveOp(loop, entry_phi, instruction, graph_->GetIntConstant(1), y, kXor); } else if (IsExact(LookupInfo(loop, y), &value) && value == opposite_value) { - return SolveXor(loop, entry_phi, instruction, x, graph_->GetIntConstant(1)); + return SolveOp(loop, entry_phi, instruction, x, graph_->GetIntConstant(1), kXor); } return nullptr; } @@ -1018,7 +1041,7 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::CreateSimplifiedInv HInstruction* HInductionVarAnalysis::GetMultConstantForShift(HLoopInformation* loop, HInstruction* instruction) { // Obtain the constant needed to treat shift as equivalent multiplication. This yields an - // existing instruction if the constants is already there. Otherwise, this has a side effect + // existing instruction if the constant is already there. Otherwise, this has a side effect // on the HIR. The restriction on the shift factor avoids generating a negative constant // (viz. 1 << 31 and 1L << 63 set the sign bit). The code assumes that generalization for // shift factors outside [0,32) and [0,64) ranges is done by earlier simplification. @@ -1102,6 +1125,7 @@ std::string HInductionVarAnalysis::InductionToString(InductionInfo* info) { case kNeg: inv += " - "; break; case kMul: inv += " * "; break; case kDiv: inv += " / "; break; + case kRem: inv += " % "; break; case kXor: inv += " ^ "; break; case kLT: inv += " < "; break; case kLE: inv += " <= "; break; @@ -1118,32 +1142,30 @@ std::string HInductionVarAnalysis::InductionToString(InductionInfo* info) { return inv; } else { if (info->induction_class == kLinear) { + DCHECK(info->operation == kNop); return "(" + InductionToString(info->op_a) + " * i + " + InductionToString(info->op_b) + "):" + Primitive::PrettyDescriptor(info->type); } else if (info->induction_class == kPolynomial) { - return "poly( sum_i(" + InductionToString(info->op_a) + ") + " + + DCHECK(info->operation == kNop); + return "poly(sum_lt(" + InductionToString(info->op_a) + ") + " + InductionToString(info->op_b) + "):" + Primitive::PrettyDescriptor(info->type); } else if (info->induction_class == kGeometric) { + DCHECK(info->operation == kMul || info->operation == kDiv); DCHECK(info->fetch != nullptr); - if (info->operation == kNop) { - return "geo(" + InductionToString(info->op_a) + " mod_i " + - FetchToString(info->fetch) + " + " + - InductionToString(info->op_b) + "):" + - Primitive::PrettyDescriptor(info->type); - } else { - return "geo(" + InductionToString(info->op_a) + " * " + - FetchToString(info->fetch) + - (info->operation == kMul ? " ^ i + " : " ^ -i + ") + - InductionToString(info->op_b) + "):" + - Primitive::PrettyDescriptor(info->type); - } + return "geo(" + InductionToString(info->op_a) + " * " + + FetchToString(info->fetch) + + (info->operation == kMul ? " ^ i + " : " ^ -i + ") + + InductionToString(info->op_b) + "):" + + Primitive::PrettyDescriptor(info->type); } else if (info->induction_class == kWrapAround) { + DCHECK(info->operation == kNop); return "wrap(" + InductionToString(info->op_a) + ", " + InductionToString(info->op_b) + "):" + Primitive::PrettyDescriptor(info->type); } else if (info->induction_class == kPeriodic) { + DCHECK(info->operation == kNop); return "periodic(" + InductionToString(info->op_a) + ", " + InductionToString(info->op_b) + "):" + Primitive::PrettyDescriptor(info->type); diff --git a/compiler/optimizing/induction_var_analysis.h b/compiler/optimizing/induction_var_analysis.h index 94afc71c3d..4720f2d61c 100644 --- a/compiler/optimizing/induction_var_analysis.h +++ b/compiler/optimizing/induction_var_analysis.h @@ -65,6 +65,7 @@ class HInductionVarAnalysis : public HOptimization { kNeg, kMul, kDiv, + kRem, kXor, kFetch, // Trip-counts. @@ -85,10 +86,10 @@ class HInductionVarAnalysis : public HOptimization { * op: a + b, a - b, -b, a * b, a / b, a % b, a ^ b, fetch * (2) linear: * nop: a * i + b - * (3) polynomial: // TODO: coming soon - * nop: sum_i(a) + b, for linear a + * (3) polynomial: + * nop: sum_lt(a) + b, for linear a * (4) geometric: - * op: a * fetch^i + b, a * fetch^-i + b, a mod_i fetch + b + * op: a * fetch^i + b, a * fetch^-i + b * (5) wrap-around * nop: a, then defined by b * (6) periodic @@ -177,17 +178,12 @@ class HInductionVarAnalysis : public HOptimization { HInstruction* y, InductionOp op, bool is_first_call); // possibly swaps x and y to try again - InductionInfo* SolveGeo(HLoopInformation* loop, - HInstruction* entry_phi, - HInstruction* instruction, - HInstruction* x, - HInstruction* y, - InductionOp op); - InductionInfo* SolveXor(HLoopInformation* loop, - HInstruction* entry_phi, - HInstruction* instruction, - HInstruction* x, - HInstruction* y); + InductionInfo* SolveOp(HLoopInformation* loop, + HInstruction* entry_phi, + HInstruction* instruction, + HInstruction* x, + HInstruction* y, + InductionOp op); InductionInfo* SolveTest(HLoopInformation* loop, HInstruction* entry_phi, HInstruction* instruction, diff --git a/compiler/optimizing/induction_var_analysis_test.cc b/compiler/optimizing/induction_var_analysis_test.cc index 2199c8e105..2d182f6483 100644 --- a/compiler/optimizing/induction_var_analysis_test.cc +++ b/compiler/optimizing/induction_var_analysis_test.cc @@ -85,6 +85,7 @@ class InductionVarAnalysisTest : public CommonCompilerTest { constant0_ = graph_->GetIntConstant(0); constant1_ = graph_->GetIntConstant(1); constant2_ = graph_->GetIntConstant(2); + constant7_ = graph_->GetIntConstant(7); constant100_ = graph_->GetIntConstant(100); float_constant0_ = graph_->GetFloatConstant(0.0f); return_->AddInstruction(new (&allocator_) HReturnVoid()); @@ -193,6 +194,7 @@ class InductionVarAnalysisTest : public CommonCompilerTest { HInstruction* constant0_; HInstruction* constant1_; HInstruction* constant2_; + HInstruction* constant7_; HInstruction* constant100_; HInstruction* float_constant0_; @@ -378,6 +380,135 @@ TEST_F(InductionVarAnalysisTest, FindTwoWayDerivedInduction) { EXPECT_TRUE(HaveSameInduction(store->InputAt(1), inc2)); } +TEST_F(InductionVarAnalysisTest, AddLinear) { + // Setup: + // for (int i = 0; i < 100; i++) { + // t1 = i + i; + // t2 = 7 + i; + // t3 = t1 + t2; + // } + BuildLoopNest(1); + + HInstruction* add1 = InsertInstruction( + new (&allocator_) HAdd(Primitive::kPrimInt, basic_[0], basic_[0]), 0); + HInstruction* add2 = InsertInstruction( + new (&allocator_) HAdd(Primitive::kPrimInt, constant7_, basic_[0]), 0); + HInstruction* add3 = InsertInstruction( + new (&allocator_) HAdd(Primitive::kPrimInt, add1, add2), 0); + PerformInductionVarAnalysis(); + + EXPECT_STREQ("((1) * i + (0)):PrimInt", GetInductionInfo(basic_[0], 0).c_str()); + EXPECT_STREQ("(((1) + (1)) * i + (0)):PrimInt", GetInductionInfo(add1, 0).c_str()); + EXPECT_STREQ("((1) * i + (7)):PrimInt", GetInductionInfo(add2, 0).c_str()); + EXPECT_STREQ("((((1) + (1)) + (1)) * i + (7)):PrimInt", GetInductionInfo(add3, 0).c_str()); +} + +TEST_F(InductionVarAnalysisTest, FindPolynomialInduction) { + // Setup: + // k = 1; + // for (int i = 0; i < 100; i++) { + // t = i * 2; + // t = 100 + t + // k = t + k; // polynomial + // } + BuildLoopNest(1); + HPhi* k_header = InsertLoopPhi(0, 0); + k_header->AddInput(constant1_); + + HInstruction* mul = InsertInstruction( + new (&allocator_) HMul(Primitive::kPrimInt, basic_[0], constant2_), 0); + HInstruction* add = InsertInstruction( + new (&allocator_) HAdd(Primitive::kPrimInt, constant100_, mul), 0); + HInstruction* pol = InsertInstruction( + new (&allocator_) HAdd(Primitive::kPrimInt, add, k_header), 0); + k_header->AddInput(pol); + PerformInductionVarAnalysis(); + + // Note, only the phi in the cycle and the base linear induction are classified. + EXPECT_STREQ("poly(sum_lt(((2) * i + (100)):PrimInt) + (1)):PrimInt", + GetInductionInfo(k_header, 0).c_str()); + EXPECT_STREQ("((2) * i + (100)):PrimInt", GetInductionInfo(add, 0).c_str()); + EXPECT_STREQ("", GetInductionInfo(pol, 0).c_str()); +} + +TEST_F(InductionVarAnalysisTest, FindPolynomialInductionAndDerived) { + // Setup: + // k = 1; + // for (int i = 0; i < 100; i++) { + // t = k + 100; + // t = k - 1; + // t = - t + // t = k * 2; + // t = k << 2; + // k = k + i; // polynomial + // } + BuildLoopNest(1); + HPhi* k_header = InsertLoopPhi(0, 0); + k_header->AddInput(constant1_); + + HInstruction* add = InsertInstruction( + new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant100_), 0); + HInstruction* sub = InsertInstruction( + new (&allocator_) HSub(Primitive::kPrimInt, k_header, constant1_), 0); + HInstruction* neg = InsertInstruction( + new (&allocator_) HNeg(Primitive::kPrimInt, sub), 0); + HInstruction* mul = InsertInstruction( + new (&allocator_) HMul(Primitive::kPrimInt, k_header, constant2_), 0); + HInstruction* shl = InsertInstruction( + new (&allocator_) HShl(Primitive::kPrimInt, k_header, constant2_), 0); + HInstruction* pol = InsertInstruction( + new (&allocator_) HAdd(Primitive::kPrimInt, k_header, basic_[0]), 0); + k_header->AddInput(pol); + PerformInductionVarAnalysis(); + + // Note, only the phi in the cycle and derived are classified. + EXPECT_STREQ("poly(sum_lt(((1) * i + (0)):PrimInt) + (1)):PrimInt", + GetInductionInfo(k_header, 0).c_str()); + EXPECT_STREQ("poly(sum_lt(((1) * i + (0)):PrimInt) + ((1) + (100))):PrimInt", + GetInductionInfo(add, 0).c_str()); + EXPECT_STREQ("poly(sum_lt(((1) * i + (0)):PrimInt) + ((1) - (1))):PrimInt", + GetInductionInfo(sub, 0).c_str()); + EXPECT_STREQ("poly(sum_lt((( - (1)) * i + (0)):PrimInt) + ((1) - (1))):PrimInt", + GetInductionInfo(neg, 0).c_str()); + EXPECT_STREQ("poly(sum_lt(((2) * i + (0)):PrimInt) + (2)):PrimInt", + GetInductionInfo(mul, 0).c_str()); + EXPECT_STREQ("poly(sum_lt(((4) * i + (0)):PrimInt) + (4)):PrimInt", + GetInductionInfo(shl, 0).c_str()); + EXPECT_STREQ("", GetInductionInfo(pol, 0).c_str()); +} + +TEST_F(InductionVarAnalysisTest, AddPolynomial) { + // Setup: + // k = 7; + // for (int i = 0; i < 100; i++) { + // t = k + k; + // t = t + k; + // k = k + i + // } + BuildLoopNest(1); + HPhi* k_header = InsertLoopPhi(0, 0); + k_header->AddInput(constant7_); + + HInstruction* add1 = InsertInstruction( + new (&allocator_) HAdd(Primitive::kPrimInt, k_header, k_header), 0); + HInstruction* add2 = InsertInstruction( + new (&allocator_) HAdd(Primitive::kPrimInt, add1, k_header), 0); + HInstruction* add3 = InsertInstruction( + new (&allocator_) HAdd(Primitive::kPrimInt, k_header, basic_[0]), 0); + k_header->AddInput(add3); + PerformInductionVarAnalysis(); + + // Note, only the phi in the cycle and added-derived are classified. + EXPECT_STREQ("poly(sum_lt(((1) * i + (0)):PrimInt) + (7)):PrimInt", + GetInductionInfo(k_header, 0).c_str()); + EXPECT_STREQ("poly(sum_lt((((1) + (1)) * i + (0)):PrimInt) + ((7) + (7))):PrimInt", + GetInductionInfo(add1, 0).c_str()); + EXPECT_STREQ( + "poly(sum_lt(((((1) + (1)) + (1)) * i + (0)):PrimInt) + (((7) + (7)) + (7))):PrimInt", + GetInductionInfo(add2, 0).c_str()); + EXPECT_STREQ("", GetInductionInfo(add3, 0).c_str()); +} + TEST_F(InductionVarAnalysisTest, FindGeometricMulInduction) { // Setup: // k = 1; @@ -481,20 +612,20 @@ TEST_F(InductionVarAnalysisTest, FindGeometricDivInductionAndDerived) { EXPECT_STREQ("", GetInductionInfo(div, 0).c_str()); } -TEST_F(InductionVarAnalysisTest, FindGeometricRemInductionAndDerived) { +TEST_F(InductionVarAnalysisTest, FindRemWrapAroundInductionAndDerived) { // Setup: - // k = 1; + // k = 100; // for (int i = 0; i < 100; i++) { // t = k + 100; // t = k - 1; // t = -t // t = k * 2; // t = k << 2; - // k = k % 100; // geometric (% 100) + // k = k % 7; // } BuildLoopNest(1); HPhi* k_header = InsertLoopPhi(0, 0); - k_header->AddInput(constant1_); + k_header->AddInput(constant100_); HInstruction* add = InsertInstruction( new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant100_), 0); @@ -507,17 +638,17 @@ TEST_F(InductionVarAnalysisTest, FindGeometricRemInductionAndDerived) { HInstruction* shl = InsertInstruction( new (&allocator_) HShl(Primitive::kPrimInt, k_header, constant2_), 0); HInstruction* rem = InsertInstruction( - new (&allocator_) HRem(Primitive::kPrimInt, k_header, constant100_, kNoDexPc), 0); + new (&allocator_) HRem(Primitive::kPrimInt, k_header, constant7_, kNoDexPc), 0); k_header->AddInput(rem); PerformInductionVarAnalysis(); - // Note, only the phi in the cycle and direct additive derived are classified. - EXPECT_STREQ("geo((1) mod_i 100 + (0)):PrimInt", GetInductionInfo(k_header, 0).c_str()); - EXPECT_STREQ("geo((1) mod_i 100 + (100)):PrimInt", GetInductionInfo(add, 0).c_str()); - EXPECT_STREQ("geo((1) mod_i 100 + ((0) - (1))):PrimInt", GetInductionInfo(sub, 0).c_str()); - EXPECT_STREQ("", GetInductionInfo(neg, 0).c_str()); - EXPECT_STREQ("", GetInductionInfo(mul, 0).c_str()); - EXPECT_STREQ("", GetInductionInfo(shl, 0).c_str()); + // Note, only the phi in the cycle and derived are classified. + EXPECT_STREQ("wrap((100), ((100) % (7))):PrimInt", GetInductionInfo(k_header, 0).c_str()); + EXPECT_STREQ("wrap(((100) + (100)), (((100) % (7)) + (100))):PrimInt", GetInductionInfo(add, 0).c_str()); + EXPECT_STREQ("wrap(((100) - (1)), (((100) % (7)) - (1))):PrimInt", GetInductionInfo(sub, 0).c_str()); + EXPECT_STREQ("wrap(( - ((100) - (1))), ( - (((100) % (7)) - (1)))):PrimInt", GetInductionInfo(neg, 0).c_str()); + EXPECT_STREQ("wrap(((100) * (2)), (((100) % (7)) * (2))):PrimInt", GetInductionInfo(mul, 0).c_str()); + EXPECT_STREQ("wrap(((100) * (4)), (((100) % (7)) * (4))):PrimInt", GetInductionInfo(shl, 0).c_str()); EXPECT_STREQ("", GetInductionInfo(rem, 0).c_str()); } diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc index 75619a3c01..e665551012 100644 --- a/compiler/optimizing/induction_var_range.cc +++ b/compiler/optimizing/induction_var_range.cc @@ -460,6 +460,8 @@ bool InductionVarRange::NeedsTripCount(HInductionVarAnalysis::InductionInfo* inf if (info != nullptr) { if (info->induction_class == HInductionVarAnalysis::kLinear) { return IsConstant(info->op_a, kExact, stride_value); + } else if (info->induction_class == HInductionVarAnalysis::kPolynomial) { + return NeedsTripCount(info->op_a, stride_value); } else if (info->induction_class == HInductionVarAnalysis::kWrapAround) { return NeedsTripCount(info->op_b, stride_value); } @@ -492,7 +494,7 @@ InductionVarRange::Value InductionVarRange::GetLinear(HInductionVarAnalysis::Ind bool in_body, bool is_min) const { DCHECK(info != nullptr); - DCHECK(info->induction_class == HInductionVarAnalysis::kLinear); + DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kLinear); // Detect common situation where an offset inside the trip-count cancels out during range // analysis (finding max a * (TC - 1) + OFFSET for a == 1 and TC = UPPER - OFFSET or finding // min a * (TC - 1) + OFFSET for a == -1 and TC = OFFSET - UPPER) to avoid losing information @@ -539,25 +541,49 @@ InductionVarRange::Value InductionVarRange::GetLinear(HInductionVarAnalysis::Ind GetVal(info->op_b, trip, in_body, is_min)); } +InductionVarRange::Value InductionVarRange::GetPolynomial(HInductionVarAnalysis::InductionInfo* info, + HInductionVarAnalysis::InductionInfo* trip, + bool in_body, + bool is_min) const { + DCHECK(info != nullptr); + DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kPolynomial); + int64_t a = 0; + int64_t b = 0; + if (IsConstant(info->op_a->op_a, kExact, &a) && CanLongValueFitIntoInt(a) && a >= 0 && + IsConstant(info->op_a->op_b, kExact, &b) && CanLongValueFitIntoInt(b) && b >= 0) { + // Evaluate bounds on sum_i=0^m-1(a * i + b) + c with a,b >= 0 for known + // maximum index value m as a * (m * (m-1)) / 2 + b * m + c. + Value c = GetVal(info->op_b, trip, in_body, is_min); + if (is_min) { + return c; + } else { + Value m = GetVal(trip, trip, in_body, is_min); + Value t = DivValue(MulValue(m, SubValue(m, Value(1))), Value(2)); + Value x = MulValue(Value(a), t); + Value y = MulValue(Value(b), m); + return AddValue(AddValue(x, y), c); + } + } + return Value(); +} + InductionVarRange::Value InductionVarRange::GetGeometric(HInductionVarAnalysis::InductionInfo* info, HInductionVarAnalysis::InductionInfo* trip, bool in_body, bool is_min) const { DCHECK(info != nullptr); - DCHECK(info->induction_class == HInductionVarAnalysis::kGeometric); + DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kGeometric); int64_t a = 0; int64_t f = 0; if (IsConstant(info->op_a, kExact, &a) && CanLongValueFitIntoInt(a) && - IsIntAndGet(info->fetch, &f) && - CanLongValueFitIntoInt(f) && f >= 1) { - // Conservative bounds on a * f^-i + b with f >= 1 can be computed without trip count. - // Same for mod. All other forms would require a much more elaborate evaluation. + IsIntAndGet(info->fetch, &f) && f >= 1) { + // Conservative bounds on a * f^-i + b with f >= 1 can be computed without + // trip count. Other forms would require a much more elaborate evaluation. const bool is_min_a = a >= 0 ? is_min : !is_min; if (info->operation == HInductionVarAnalysis::kDiv) { - return AddValue(Value(is_min_a ? 0 : a), GetVal(info->op_b, trip, in_body, is_min)); - } else if (info->operation == HInductionVarAnalysis::kNop) { - return AddValue(Value(is_min_a ? (a % f) : a), GetVal(info->op_b, trip, in_body, is_min)); + Value b = GetVal(info->op_b, trip, in_body, is_min); + return is_min_a ? b : AddValue(Value(a), b); } } return Value(); @@ -572,7 +598,7 @@ InductionVarRange::Value InductionVarRange::GetFetch(HInstruction* instruction, if (chase_hint_ == nullptr && in_body && trip != nullptr && instruction == trip->op_a->fetch) { if (is_min) { return Value(1); - } else if (!IsUnsafeTripCount(trip)) { + } else if (!instruction->IsConstant() && !IsUnsafeTripCount(trip)) { return Value(std::numeric_limits<int32_t>::max()); } } @@ -650,6 +676,8 @@ InductionVarRange::Value InductionVarRange::GetVal(HInductionVarAnalysis::Induct return GetMul(info->op_a, info->op_b, trip, in_body, is_min); case HInductionVarAnalysis::kDiv: return GetDiv(info->op_a, info->op_b, trip, in_body, is_min); + case HInductionVarAnalysis::kRem: + return GetRem(info->op_a, info->op_b); case HInductionVarAnalysis::kXor: return GetXor(info->op_a, info->op_b); case HInductionVarAnalysis::kFetch: @@ -675,7 +703,7 @@ InductionVarRange::Value InductionVarRange::GetVal(HInductionVarAnalysis::Induct case HInductionVarAnalysis::kLinear: return CorrectForType(GetLinear(info, trip, in_body, is_min), info->type); case HInductionVarAnalysis::kPolynomial: - break; + return GetPolynomial(info, trip, in_body, is_min); case HInductionVarAnalysis::kGeometric: return GetGeometric(info, trip, in_body, is_min); case HInductionVarAnalysis::kWrapAround: @@ -757,6 +785,21 @@ InductionVarRange::Value InductionVarRange::GetDiv(HInductionVarAnalysis::Induct return Value(); } +InductionVarRange::Value InductionVarRange::GetRem( + HInductionVarAnalysis::InductionInfo* info1, + HInductionVarAnalysis::InductionInfo* info2) const { + int64_t v1 = 0; + int64_t v2 = 0; + // Only accept exact values. + if (IsConstant(info1, kExact, &v1) && IsConstant(info2, kExact, &v2) && v2 != 0) { + int64_t value = v1 % v2; + if (CanLongValueFitIntoInt(value)) { + return Value(static_cast<int32_t>(value)); + } + } + return Value(); +} + InductionVarRange::Value InductionVarRange::GetXor( HInductionVarAnalysis::InductionInfo* info1, HInductionVarAnalysis::InductionInfo* info2) const { @@ -898,8 +941,12 @@ bool InductionVarRange::GenerateRangeOrLastValue(HInstruction* context, upper = nullptr; } break; + case HInductionVarAnalysis::kPolynomial: + return GenerateLastValuePolynomial(info, trip, graph, block, lower); case HInductionVarAnalysis::kGeometric: return GenerateLastValueGeometric(info, trip, graph, block, lower); + case HInductionVarAnalysis::kWrapAround: + return GenerateLastValueWrapAround(info, trip, graph, block, lower); case HInductionVarAnalysis::kPeriodic: return GenerateLastValuePeriodic(info, trip, graph, block, lower, needs_taken_test); default: @@ -925,13 +972,43 @@ bool InductionVarRange::GenerateRangeOrLastValue(HInstruction* context, GenerateCode(info, trip, graph, block, upper, in_body, /* is_min */ false); } +bool InductionVarRange::GenerateLastValuePolynomial(HInductionVarAnalysis::InductionInfo* info, + HInductionVarAnalysis::InductionInfo* trip, + HGraph* graph, + HBasicBlock* block, + /*out*/HInstruction** result) const { + DCHECK(info != nullptr); + DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kPolynomial); + // Detect known coefficients and trip count (always taken). + int64_t a = 0; + int64_t b = 0; + int64_t m = 0; + if (IsConstant(info->op_a->op_a, kExact, &a) && a >= 0 && + IsConstant(info->op_a->op_b, kExact, &b) && b >= 0 && + IsConstant(trip->op_a, kExact, &m) && m >= 1) { + // Evaluate bounds on sum_i=0^m-1(a * i + b) + c with a,b >= 0 for known + // maximum index value m as a * (m * (m-1)) / 2 + b * m + c. + // TODO: generalize + HInstruction* c_instr = nullptr; + if (GenerateCode(info->op_b, nullptr, graph, block, graph ? &c_instr : nullptr, false, false)) { + if (graph != nullptr) { + int64_t sum = a * ((m * (m - 1)) / 2) + b * m; + *result = Insert(block, new (graph->GetArena()) HAdd(info->type, + graph->GetIntConstant(sum), c_instr)); + } + return true; + } + } + return false; +} + bool InductionVarRange::GenerateLastValueGeometric(HInductionVarAnalysis::InductionInfo* info, HInductionVarAnalysis::InductionInfo* trip, HGraph* graph, HBasicBlock* block, /*out*/HInstruction** result) const { DCHECK(info != nullptr); - DCHECK(info->induction_class == HInductionVarAnalysis::kGeometric); + DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kGeometric); // Detect known base and trip count (always taken). int64_t f = 0; int64_t t = 0; @@ -940,15 +1017,6 @@ bool InductionVarRange::GenerateLastValueGeometric(HInductionVarAnalysis::Induct HInstruction* opb = nullptr; if (GenerateCode(info->op_a, nullptr, graph, block, &opa, false, false) && GenerateCode(info->op_b, nullptr, graph, block, &opb, false, false)) { - // Generate a % f + b. - if (info->operation == HInductionVarAnalysis::kNop) { - if (graph != nullptr) { - HInstruction* rem = - Insert(block, new (graph->GetArena()) HRem(info->type, opa, info->fetch, kNoDexPc)); - *result = Insert(block, new (graph->GetArena()) HAdd(info->type, rem, opb)); - } - return true; - } // Compute f ^ t. int64_t fpowt = IntPow(f, t); if (graph != nullptr) { @@ -980,6 +1048,28 @@ bool InductionVarRange::GenerateLastValueGeometric(HInductionVarAnalysis::Induct return false; } +bool InductionVarRange::GenerateLastValueWrapAround(HInductionVarAnalysis::InductionInfo* info, + HInductionVarAnalysis::InductionInfo* trip, + HGraph* graph, + HBasicBlock* block, + /*out*/HInstruction** result) const { + DCHECK(info != nullptr); + DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kWrapAround); + // Count depth. + int32_t depth = 0; + for (; info->induction_class == HInductionVarAnalysis::kWrapAround; + info = info->op_b, ++depth) {} + // Handle wrap(x, wrap(.., y)) if trip count reaches an invariant at end. + // TODO: generalize + int64_t t = 0; + if (info->induction_class == HInductionVarAnalysis::kInvariant && + IsConstant(trip->op_a, kExact, &t) && t >= depth && + GenerateCode(info, nullptr, graph, block, result, false, false)) { + return true; + } + return false; +} + bool InductionVarRange::GenerateLastValuePeriodic(HInductionVarAnalysis::InductionInfo* info, HInductionVarAnalysis::InductionInfo* trip, HGraph* graph, @@ -987,17 +1077,18 @@ bool InductionVarRange::GenerateLastValuePeriodic(HInductionVarAnalysis::Inducti /*out*/HInstruction** result, /*out*/bool* needs_taken_test) const { DCHECK(info != nullptr); - DCHECK(info->induction_class == HInductionVarAnalysis::kPeriodic); + DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kPeriodic); // Count period. int32_t period = 1; for (HInductionVarAnalysis::InductionInfo* p = info; p->induction_class == HInductionVarAnalysis::kPeriodic; p = p->op_b, ++period) {} // Handle periodic(x, y) case for restricted types. + // TODO: generalize if (period != 2 || trip->op_a->type != Primitive::kPrimInt || (info->type != Primitive::kPrimInt && info->type != Primitive::kPrimBoolean)) { - return false; // TODO: easy to generalize + return false; } HInstruction* x_instr = nullptr; HInstruction* y_instr = nullptr; @@ -1058,6 +1149,7 @@ bool InductionVarRange::GenerateCode(HInductionVarAnalysis::InductionInfo* info, // invariants, some effort is made to keep this parameter consistent). switch (info->operation) { case HInductionVarAnalysis::kAdd: + case HInductionVarAnalysis::kRem: // no proper is_min for second arg case HInductionVarAnalysis::kXor: // no proper is_min for second arg case HInductionVarAnalysis::kLT: case HInductionVarAnalysis::kLE: @@ -1070,6 +1162,8 @@ bool InductionVarRange::GenerateCode(HInductionVarAnalysis::InductionInfo* info, switch (info->operation) { case HInductionVarAnalysis::kAdd: operation = new (graph->GetArena()) HAdd(type, opa, opb); break; + case HInductionVarAnalysis::kRem: + operation = new (graph->GetArena()) HRem(type, opa, opb, kNoDexPc); break; case HInductionVarAnalysis::kXor: operation = new (graph->GetArena()) HXor(type, opa, opb); break; case HInductionVarAnalysis::kLT: diff --git a/compiler/optimizing/induction_var_range.h b/compiler/optimizing/induction_var_range.h index f7360e83db..ba14847d82 100644 --- a/compiler/optimizing/induction_var_range.h +++ b/compiler/optimizing/induction_var_range.h @@ -190,6 +190,10 @@ class InductionVarRange { HInductionVarAnalysis::InductionInfo* trip, bool in_body, bool is_min) const; + Value GetPolynomial(HInductionVarAnalysis::InductionInfo* info, + HInductionVarAnalysis::InductionInfo* trip, + bool in_body, + bool is_min) const; Value GetGeometric(HInductionVarAnalysis::InductionInfo* info, HInductionVarAnalysis::InductionInfo* trip, bool in_body, @@ -212,6 +216,8 @@ class InductionVarRange { HInductionVarAnalysis::InductionInfo* trip, bool in_body, bool is_min) const; + Value GetRem(HInductionVarAnalysis::InductionInfo* info1, + HInductionVarAnalysis::InductionInfo* info2) const; Value GetXor(HInductionVarAnalysis::InductionInfo* info1, HInductionVarAnalysis::InductionInfo* info2) const; @@ -249,12 +255,24 @@ class InductionVarRange { /*out*/ bool* needs_finite_test, /*out*/ bool* needs_taken_test) const; + bool GenerateLastValuePolynomial(HInductionVarAnalysis::InductionInfo* info, + HInductionVarAnalysis::InductionInfo* trip, + HGraph* graph, + HBasicBlock* block, + /*out*/HInstruction** result) const; + bool GenerateLastValueGeometric(HInductionVarAnalysis::InductionInfo* info, HInductionVarAnalysis::InductionInfo* trip, HGraph* graph, HBasicBlock* block, /*out*/HInstruction** result) const; + bool GenerateLastValueWrapAround(HInductionVarAnalysis::InductionInfo* info, + HInductionVarAnalysis::InductionInfo* trip, + HGraph* graph, + HBasicBlock* block, + /*out*/HInstruction** result) const; + bool GenerateLastValuePeriodic(HInductionVarAnalysis::InductionInfo* info, HInductionVarAnalysis::InductionInfo* trip, HGraph* graph, diff --git a/compiler/optimizing/induction_var_range_test.cc b/compiler/optimizing/induction_var_range_test.cc index 510841eb68..aa3e1aab4f 100644 --- a/compiler/optimizing/induction_var_range_test.cc +++ b/compiler/optimizing/induction_var_range_test.cc @@ -135,6 +135,8 @@ class InductionVarRangeTest : public CommonCompilerTest { case 'n': op = HInductionVarAnalysis::kNeg; break; case '*': op = HInductionVarAnalysis::kMul; break; case '/': op = HInductionVarAnalysis::kDiv; break; + case '%': op = HInductionVarAnalysis::kRem; break; + case '^': op = HInductionVarAnalysis::kXor; break; case '<': op = HInductionVarAnalysis::kLT; break; default: op = HInductionVarAnalysis::kNop; break; } @@ -178,12 +180,21 @@ class InductionVarRangeTest : public CommonCompilerTest { Primitive::kPrimInt); } + /** Constructs a polynomial sum(a * i + b) + c induction. */ + HInductionVarAnalysis::InductionInfo* CreatePolynomial(int32_t a, int32_t b, int32_t c) { + return iva_->CreateInduction(HInductionVarAnalysis::kPolynomial, + HInductionVarAnalysis::kNop, + CreateLinear(a, b), + CreateConst(c), + nullptr, + Primitive::kPrimInt); + } + /** Constructs a geometric a * f^i + b induction. */ HInductionVarAnalysis::InductionInfo* CreateGeometric(int32_t a, int32_t b, int32_t f, char op) { return iva_->CreateInduction(HInductionVarAnalysis::kGeometric, - op == '*' ? HInductionVarAnalysis::kMul : - op == '/' ? HInductionVarAnalysis::kDiv : - HInductionVarAnalysis::kNop, + op == '*' ? HInductionVarAnalysis::kMul + : HInductionVarAnalysis::kDiv, CreateConst(a), CreateConst(b), graph_->GetIntConstant(f), @@ -200,7 +211,7 @@ class InductionVarRangeTest : public CommonCompilerTest { Primitive::kPrimInt); } - /** Constructs a wrap-around induction consisting of a constant, followed info */ + /** Constructs a wrap-around induction consisting of a constant, followed by info. */ HInductionVarAnalysis::InductionInfo* CreateWrapAround( int32_t initial, HInductionVarAnalysis::InductionInfo* info) { @@ -256,6 +267,16 @@ class InductionVarRangeTest : public CommonCompilerTest { return range_.GetDiv(info1, info2, nullptr, /* in_body */ true, is_min); } + Value GetRem(HInductionVarAnalysis::InductionInfo* info1, + HInductionVarAnalysis::InductionInfo* info2) { + return range_.GetRem(info1, info2); + } + + Value GetXor(HInductionVarAnalysis::InductionInfo* info1, + HInductionVarAnalysis::InductionInfo* info2) { + return range_.GetXor(info1, info2); + } + bool IsExact(HInductionVarAnalysis::InductionInfo* info, int64_t* value) { return range_.IsConstant(info, InductionVarRange::kExact, value); } @@ -438,6 +459,27 @@ TEST_F(InductionVarRangeTest, GetMinMaxWrapAround) { ExpectEqual(Value(20), GetMax(CreateWrapAround(20, -1, 10), nullptr)); } +TEST_F(InductionVarRangeTest, GetMinMaxPolynomial) { + ExpectEqual(Value(7), GetMin(CreatePolynomial(3, 5, 7), nullptr)); + ExpectEqual(Value(), GetMax(CreatePolynomial(3, 5, 7), nullptr)); + ExpectEqual(Value(7), GetMin(CreatePolynomial(3, 5, 7), CreateTripCount(5, true, true))); + ExpectEqual(Value(45), GetMax(CreatePolynomial(3, 5, 7), CreateTripCount(5, true, true))); + ExpectEqual(Value(7), GetMin(CreatePolynomial(3, 5, 7), CreateTripCount(10, true, true))); + ExpectEqual(Value(160), GetMax(CreatePolynomial(3, 5, 7), CreateTripCount(10, true, true))); + ExpectEqual(Value(-7), GetMin(CreatePolynomial(11, 13, -7), + CreateTripCount(5, true, true))); + ExpectEqual(Value(111), GetMax(CreatePolynomial(11, 13, -7), + CreateTripCount(5, true, true))); + ExpectEqual(Value(-7), GetMin(CreatePolynomial(11, 13, -7), + CreateTripCount(10, true, true))); + ExpectEqual(Value(506), GetMax(CreatePolynomial(11, 13, -7), + CreateTripCount(10, true, true))); + ExpectEqual(Value(), GetMin(CreatePolynomial(-3, 5, 7), CreateTripCount(10, true, true))); + ExpectEqual(Value(), GetMax(CreatePolynomial(-3, 5, 7), CreateTripCount(10, true, true))); + ExpectEqual(Value(), GetMin(CreatePolynomial(3, -5, 7), CreateTripCount(10, true, true))); + ExpectEqual(Value(), GetMax(CreatePolynomial(3, -5, 7), CreateTripCount(10, true, true))); +} + TEST_F(InductionVarRangeTest, GetMinMaxGeometricMul) { ExpectEqual(Value(), GetMin(CreateGeometric(1, 1, 1, '*'), nullptr)); ExpectEqual(Value(), GetMax(CreateGeometric(1, 1, 1, '*'), nullptr)); @@ -454,17 +496,6 @@ TEST_F(InductionVarRangeTest, GetMinMaxGeometricDiv) { ExpectEqual(Value(-5), GetMax(CreateGeometric(-11, -5, 3, '/'), nullptr)); } -TEST_F(InductionVarRangeTest, GetMinMaxGeometricRem) { - ExpectEqual(Value(7), GetMin(CreateGeometric(11, 5, 3, '%'), nullptr)); - ExpectEqual(Value(16), GetMax(CreateGeometric(11, 5, 3, '%'), nullptr)); - ExpectEqual(Value(-3), GetMin(CreateGeometric(11, -5, 3, '%'), nullptr)); - ExpectEqual(Value(6), GetMax(CreateGeometric(11, -5, 3, '%'), nullptr)); - ExpectEqual(Value(-6), GetMin(CreateGeometric(-11, 5, 3, '%'), nullptr)); - ExpectEqual(Value(3), GetMax(CreateGeometric(-11, 5, 3, '%'), nullptr)); - ExpectEqual(Value(-16), GetMin(CreateGeometric(-11, -5, 3, '%'), nullptr)); - ExpectEqual(Value(-7), GetMax(CreateGeometric(-11, -5, 3, '%'), nullptr)); -} - TEST_F(InductionVarRangeTest, GetMinMaxPeriodic) { ExpectEqual(Value(-2), GetMin(CreateRange(-2, 99), nullptr)); ExpectEqual(Value(99), GetMax(CreateRange(-2, 99), nullptr)); @@ -530,6 +561,46 @@ TEST_F(InductionVarRangeTest, GetDivMax) { ExpectEqual(Value(), GetDiv(CreateRange(-1, 1), CreateRange(-1, 1), false)); } +TEST_F(InductionVarRangeTest, GetMinMaxRem) { + ExpectEqual(Value(), GetMin(CreateInvariant('%', CreateConst(2), CreateRange(10, 20)), nullptr)); + ExpectEqual(Value(), GetMax(CreateInvariant('%', CreateConst(2), CreateRange(10, 20)), nullptr)); + ExpectEqual(Value(), GetMin(CreateInvariant('%', CreateRange(10, 20), CreateConst(2)), nullptr)); + ExpectEqual(Value(), GetMax(CreateInvariant('%', CreateRange(10, 20), CreateConst(2)), nullptr)); + ExpectEqual(Value(2), GetMin(CreateInvariant('%', CreateConst(2), CreateConst(5)), nullptr)); + ExpectEqual(Value(2), GetMax(CreateInvariant('%', CreateConst(2), CreateConst(5)), nullptr)); + ExpectEqual(Value(1), GetMin(CreateInvariant('%', CreateConst(11), CreateConst(5)), nullptr)); + ExpectEqual(Value(1), GetMax(CreateInvariant('%', CreateConst(11), CreateConst(5)), nullptr)); +} + +TEST_F(InductionVarRangeTest, GetRem) { + ExpectEqual(Value(0), GetRem(CreateConst(1), CreateConst(1))); + ExpectEqual(Value(2), GetRem(CreateConst(2), CreateConst(5))); + ExpectEqual(Value(1), GetRem(CreateConst(11), CreateConst(5))); + ExpectEqual(Value(-2), GetRem(CreateConst(-2), CreateConst(5))); + ExpectEqual(Value(-1), GetRem(CreateConst(-11), CreateConst(5))); + ExpectEqual(Value(2), GetRem(CreateConst(2), CreateConst(-5))); + ExpectEqual(Value(1), GetRem(CreateConst(11), CreateConst(-5))); + ExpectEqual(Value(-2), GetRem(CreateConst(-2), CreateConst(-5))); + ExpectEqual(Value(-1), GetRem(CreateConst(-11), CreateConst(-5))); + ExpectEqual(Value(), GetRem(CreateConst(1), CreateConst(0))); +} + +TEST_F(InductionVarRangeTest, GetMinMaxXor) { + ExpectEqual(Value(), GetMin(CreateInvariant('^', CreateConst(2), CreateRange(10, 20)), nullptr)); + ExpectEqual(Value(), GetMax(CreateInvariant('^', CreateConst(2), CreateRange(10, 20)), nullptr)); + ExpectEqual(Value(), GetMin(CreateInvariant('^', CreateRange(10, 20), CreateConst(2)), nullptr)); + ExpectEqual(Value(), GetMax(CreateInvariant('^', CreateRange(10, 20), CreateConst(2)), nullptr)); + ExpectEqual(Value(3), GetMin(CreateInvariant('^', CreateConst(1), CreateConst(2)), nullptr)); + ExpectEqual(Value(3), GetMax(CreateInvariant('^', CreateConst(1), CreateConst(2)), nullptr)); +} + +TEST_F(InductionVarRangeTest, GetXor) { + ExpectEqual(Value(0), GetXor(CreateConst(1), CreateConst(1))); + ExpectEqual(Value(3), GetXor(CreateConst(1), CreateConst(2))); + ExpectEqual(Value(-2), GetXor(CreateConst(1), CreateConst(-1))); + ExpectEqual(Value(0), GetXor(CreateConst(-1), CreateConst(-1))); +} + TEST_F(InductionVarRangeTest, AddValue) { ExpectEqual(Value(110), AddValue(Value(10), Value(100))); ExpectEqual(Value(-5), AddValue(Value(x_, 1, -4), Value(x_, -1, -1))); diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc index f4616e39e6..9d73e29602 100644 --- a/compiler/optimizing/loop_optimization.cc +++ b/compiler/optimizing/loop_optimization.cc @@ -64,7 +64,8 @@ HLoopOptimization::HLoopOptimization(HGraph* graph, top_loop_(nullptr), last_loop_(nullptr), iset_(nullptr), - induction_simplication_count_(0) { + induction_simplication_count_(0), + simplified_(false) { } void HLoopOptimization::Run() { @@ -169,9 +170,15 @@ void HLoopOptimization::TraverseLoopsInnerToOuter(LoopNode* node) { if (current_induction_simplification_count != induction_simplication_count_) { induction_range_.ReVisit(node->loop_info); } - SimplifyBlocks(node); - SimplifyInduction(node); - SimplifyBlocks(node); + // Repeat simplifications until no more changes occur. Note that since + // each simplification consists of eliminating code (without introducing + // new code), this process is always finite. + do { + simplified_ = false; + SimplifyBlocks(node); + SimplifyInduction(node); + } while (simplified_); + // Remove inner loops when empty. if (node->inner == nullptr) { RemoveIfEmptyInnerLoop(node); } @@ -198,63 +205,57 @@ void HLoopOptimization::SimplifyInduction(LoopNode* node) { for (HInstruction* i : *iset_) { RemoveFromCycle(i); } + simplified_ = true; induction_simplication_count_++; } } } void HLoopOptimization::SimplifyBlocks(LoopNode* node) { - // Repeat the block simplifications until no more changes occur. Note that since - // each simplification consists of eliminating code (without introducing new code), - // this process is always finite. - bool changed; - do { - changed = false; - // Iterate over all basic blocks in the loop-body. - for (HBlocksInLoopIterator it(*node->loop_info); !it.Done(); it.Advance()) { - HBasicBlock* block = it.Current(); - // Remove dead instructions from the loop-body. - for (HBackwardInstructionIterator i(block->GetInstructions()); !i.Done(); i.Advance()) { - HInstruction* instruction = i.Current(); - if (instruction->IsDeadAndRemovable()) { - changed = true; - block->RemoveInstruction(instruction); - } + // Iterate over all basic blocks in the loop-body. + for (HBlocksInLoopIterator it(*node->loop_info); !it.Done(); it.Advance()) { + HBasicBlock* block = it.Current(); + // Remove dead instructions from the loop-body. + for (HBackwardInstructionIterator i(block->GetInstructions()); !i.Done(); i.Advance()) { + HInstruction* instruction = i.Current(); + if (instruction->IsDeadAndRemovable()) { + simplified_ = true; + block->RemoveInstruction(instruction); } - // Remove trivial control flow blocks from the loop-body. - HBasicBlock* succ = nullptr; - if (IsGotoBlock(block, &succ) && succ->GetPredecessors().size() == 1) { - // Trivial goto block can be removed. - HBasicBlock* pred = block->GetSinglePredecessor(); - changed = true; - pred->ReplaceSuccessor(block, succ); - block->RemoveDominatedBlock(succ); - block->DisconnectAndDelete(); - pred->AddDominatedBlock(succ); - succ->SetDominator(pred); - } else if (block->GetSuccessors().size() == 2) { - // Trivial if block can be bypassed to either branch. - HBasicBlock* succ0 = block->GetSuccessors()[0]; - HBasicBlock* succ1 = block->GetSuccessors()[1]; - HBasicBlock* meet0 = nullptr; - HBasicBlock* meet1 = nullptr; - if (succ0 != succ1 && - IsGotoBlock(succ0, &meet0) && - IsGotoBlock(succ1, &meet1) && - meet0 == meet1 && // meets again - meet0 != block && // no self-loop - meet0->GetPhis().IsEmpty()) { // not used for merging - changed = true; - succ0->DisconnectAndDelete(); - if (block->Dominates(meet0)) { - block->RemoveDominatedBlock(meet0); - succ1->AddDominatedBlock(meet0); - meet0->SetDominator(succ1); - } + } + // Remove trivial control flow blocks from the loop-body. + HBasicBlock* succ = nullptr; + if (IsGotoBlock(block, &succ) && succ->GetPredecessors().size() == 1) { + // Trivial goto block can be removed. + HBasicBlock* pred = block->GetSinglePredecessor(); + simplified_ = true; + pred->ReplaceSuccessor(block, succ); + block->RemoveDominatedBlock(succ); + block->DisconnectAndDelete(); + pred->AddDominatedBlock(succ); + succ->SetDominator(pred); + } else if (block->GetSuccessors().size() == 2) { + // Trivial if block can be bypassed to either branch. + HBasicBlock* succ0 = block->GetSuccessors()[0]; + HBasicBlock* succ1 = block->GetSuccessors()[1]; + HBasicBlock* meet0 = nullptr; + HBasicBlock* meet1 = nullptr; + if (succ0 != succ1 && + IsGotoBlock(succ0, &meet0) && + IsGotoBlock(succ1, &meet1) && + meet0 == meet1 && // meets again + meet0 != block && // no self-loop + meet0->GetPhis().IsEmpty()) { // not used for merging + simplified_ = true; + succ0->DisconnectAndDelete(); + if (block->Dominates(meet0)) { + block->RemoveDominatedBlock(meet0); + succ1->AddDominatedBlock(meet0); + meet0->SetDominator(succ1); } } } - } while (changed); + } } void HLoopOptimization::RemoveIfEmptyInnerLoop(LoopNode* node) { diff --git a/compiler/optimizing/loop_optimization.h b/compiler/optimizing/loop_optimization.h index 3391bef4e9..0f05b24c37 100644 --- a/compiler/optimizing/loop_optimization.h +++ b/compiler/optimizing/loop_optimization.h @@ -95,6 +95,9 @@ class HLoopOptimization : public HOptimization { // when the induction of inner loops has changed. int32_t induction_simplication_count_; + // Flag that tracks if any simplifications have occurred. + bool simplified_; + friend class LoopOptimizationTest; DISALLOW_COPY_AND_ASSIGN(HLoopOptimization); diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 313776943b..f3a5be2172 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -629,13 +629,13 @@ bool ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b // Sanity check Class[] and Object[]'s interfaces. GetDirectInterface may cause thread // suspension. CHECK_EQ(java_lang_Cloneable.Get(), - mirror::Class::GetDirectInterface(self, class_array_class, 0)); + mirror::Class::GetDirectInterface(self, class_array_class.Get(), 0)); CHECK_EQ(java_io_Serializable.Get(), - mirror::Class::GetDirectInterface(self, class_array_class, 1)); + mirror::Class::GetDirectInterface(self, class_array_class.Get(), 1)); CHECK_EQ(java_lang_Cloneable.Get(), - mirror::Class::GetDirectInterface(self, object_array_class, 0)); + mirror::Class::GetDirectInterface(self, object_array_class.Get(), 0)); CHECK_EQ(java_io_Serializable.Get(), - mirror::Class::GetDirectInterface(self, object_array_class, 1)); + mirror::Class::GetDirectInterface(self, object_array_class.Get(), 1)); CHECK_EQ(object_array_string.Get(), FindSystemClass(self, GetClassRootDescriptor(kJavaLangStringArrayClass))); @@ -4558,7 +4558,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass, StackHandleScope<1> hs_iface(self); MutableHandle<mirror::Class> handle_scope_iface(hs_iface.NewHandle<mirror::Class>(nullptr)); for (size_t i = 0; i < num_direct_interfaces; i++) { - handle_scope_iface.Assign(mirror::Class::GetDirectInterface(self, klass, i)); + handle_scope_iface.Assign(mirror::Class::GetDirectInterface(self, klass.Get(), i)); CHECK(handle_scope_iface.Get() != nullptr); CHECK(handle_scope_iface->IsInterface()); if (handle_scope_iface->HasBeenRecursivelyInitialized()) { @@ -4694,7 +4694,8 @@ bool ClassLinker::InitializeDefaultInterfaceRecursive(Thread* self, MutableHandle<mirror::Class> handle_super_iface(hs.NewHandle<mirror::Class>(nullptr)); // First we initialize all of iface's super-interfaces recursively. for (size_t i = 0; i < num_direct_ifaces; i++) { - ObjPtr<mirror::Class> super_iface = mirror::Class::GetDirectInterface(self, iface, i); + ObjPtr<mirror::Class> super_iface = mirror::Class::GetDirectInterface(self, iface.Get(), i); + DCHECK(super_iface != nullptr); if (!super_iface->HasBeenRecursivelyInitialized()) { // Recursive step handle_super_iface.Assign(super_iface); @@ -6455,7 +6456,7 @@ bool ClassLinker::SetupInterfaceLookupTable(Thread* self, Handle<mirror::Class> for (size_t i = 0; i < num_interfaces; i++) { ObjPtr<mirror::Class> interface = have_interfaces ? interfaces->GetWithoutChecks(i) - : mirror::Class::GetDirectInterface(self, klass, i); + : mirror::Class::GetDirectInterface(self, klass.Get(), i); DCHECK(interface != nullptr); if (UNLIKELY(!interface->IsInterface())) { std::string temp; @@ -6493,7 +6494,7 @@ bool ClassLinker::SetupInterfaceLookupTable(Thread* self, Handle<mirror::Class> std::vector<mirror::Class*> to_add; for (size_t i = 0; i < num_interfaces; i++) { ObjPtr<mirror::Class> interface = have_interfaces ? interfaces->Get(i) : - mirror::Class::GetDirectInterface(self, klass, i); + mirror::Class::GetDirectInterface(self, klass.Get(), i); to_add.push_back(interface.Ptr()); } @@ -7868,16 +7869,14 @@ ArtField* ClassLinker::ResolveField(const DexFile& dex_file, } const DexFile::FieldId& field_id = dex_file.GetFieldId(field_idx); Thread* const self = Thread::Current(); - StackHandleScope<1> hs(self); - Handle<mirror::Class> klass( - hs.NewHandle(ResolveType(dex_file, field_id.class_idx_, dex_cache, class_loader))); - if (klass.Get() == nullptr) { + ObjPtr<mirror::Class> klass = ResolveType(dex_file, field_id.class_idx_, dex_cache, class_loader); + if (klass == nullptr) { DCHECK(Thread::Current()->IsExceptionPending()); return nullptr; } if (is_static) { - resolved = mirror::Class::FindStaticField(self, klass.Get(), dex_cache.Get(), field_idx); + resolved = mirror::Class::FindStaticField(self, klass, dex_cache.Get(), field_idx); } else { resolved = klass->FindInstanceField(dex_cache.Get(), field_idx); } @@ -7891,7 +7890,7 @@ ArtField* ClassLinker::ResolveField(const DexFile& dex_file, resolved = klass->FindInstanceField(name, type); } if (resolved == nullptr) { - ThrowNoSuchFieldError(is_static ? "static " : "instance ", klass.Get(), type, name); + ThrowNoSuchFieldError(is_static ? "static " : "instance ", klass, type, name); return nullptr; } } @@ -7911,10 +7910,8 @@ ArtField* ClassLinker::ResolveFieldJLS(const DexFile& dex_file, } const DexFile::FieldId& field_id = dex_file.GetFieldId(field_idx); Thread* self = Thread::Current(); - StackHandleScope<1> hs(self); - Handle<mirror::Class> klass( - hs.NewHandle(ResolveType(dex_file, field_id.class_idx_, dex_cache, class_loader))); - if (klass.Get() == nullptr) { + ObjPtr<mirror::Class> klass(ResolveType(dex_file, field_id.class_idx_, dex_cache, class_loader)); + if (klass == nullptr) { DCHECK(Thread::Current()->IsExceptionPending()); return nullptr; } @@ -7926,7 +7923,7 @@ ArtField* ClassLinker::ResolveFieldJLS(const DexFile& dex_file, if (resolved != nullptr) { dex_cache->SetResolvedField(field_idx, resolved, image_pointer_size_); } else { - ThrowNoSuchFieldError("", klass.Get(), type, name); + ThrowNoSuchFieldError("", klass, type, name); } return resolved; } @@ -8150,7 +8147,7 @@ jobject ClassLinker::CreatePathClassLoader(Thread* self, ScopedObjectAccessUnchecked soa(self); // For now, create a libcore-level DexFile for each ART DexFile. This "explodes" multidex. - StackHandleScope<11> hs(self); + StackHandleScope<6> hs(self); ArtField* dex_elements_field = jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements); @@ -8229,7 +8226,9 @@ jobject ClassLinker::CreatePathClassLoader(Thread* self, // Make a pretend boot-classpath. // TODO: Should we scan the image? ArtField* const parent_field = - mirror::Class::FindField(self, hs.NewHandle(h_path_class_loader->GetClass()), "parent", + mirror::Class::FindField(self, + h_path_class_loader->GetClass(), + "parent", "Ljava/lang/ClassLoader;"); DCHECK(parent_field != nullptr); ObjPtr<mirror::Object> boot_cl = diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index ddb9e590a7..862585af92 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -215,10 +215,12 @@ class ClassLinkerTest : public CommonRuntimeTest { EXPECT_TRUE(array->ShouldHaveEmbeddedVTable()); EXPECT_EQ(2, array->GetIfTableCount()); ASSERT_TRUE(array->GetIfTable() != nullptr); - ObjPtr<mirror::Class> direct_interface0 = mirror::Class::GetDirectInterface(self, array, 0); + ObjPtr<mirror::Class> direct_interface0 = + mirror::Class::GetDirectInterface(self, array.Get(), 0); EXPECT_TRUE(direct_interface0 != nullptr); EXPECT_STREQ(direct_interface0->GetDescriptor(&temp), "Ljava/lang/Cloneable;"); - ObjPtr<mirror::Class> direct_interface1 = mirror::Class::GetDirectInterface(self, array, 1); + ObjPtr<mirror::Class> direct_interface1 = + mirror::Class::GetDirectInterface(self, array.Get(), 1); EXPECT_STREQ(direct_interface1->GetDescriptor(&temp), "Ljava/io/Serializable;"); ObjPtr<mirror::Class> array_ptr = array->GetComponentType(); EXPECT_OBJ_PTR_EQ(class_linker_->FindArrayClass(self, &array_ptr), array.Get()); @@ -1019,48 +1021,48 @@ TEST_F(ClassLinkerTest, StaticFields) { EXPECT_EQ(9U, statics->NumStaticFields()); - ArtField* s0 = mirror::Class::FindStaticField(soa.Self(), statics, "s0", "Z"); + ArtField* s0 = mirror::Class::FindStaticField(soa.Self(), statics.Get(), "s0", "Z"); EXPECT_EQ(s0->GetTypeAsPrimitiveType(), Primitive::kPrimBoolean); EXPECT_EQ(true, s0->GetBoolean(statics.Get())); s0->SetBoolean<false>(statics.Get(), false); - ArtField* s1 = mirror::Class::FindStaticField(soa.Self(), statics, "s1", "B"); + ArtField* s1 = mirror::Class::FindStaticField(soa.Self(), statics.Get(), "s1", "B"); EXPECT_EQ(s1->GetTypeAsPrimitiveType(), Primitive::kPrimByte); EXPECT_EQ(5, s1->GetByte(statics.Get())); s1->SetByte<false>(statics.Get(), 6); - ArtField* s2 = mirror::Class::FindStaticField(soa.Self(), statics, "s2", "C"); + ArtField* s2 = mirror::Class::FindStaticField(soa.Self(), statics.Get(), "s2", "C"); EXPECT_EQ(s2->GetTypeAsPrimitiveType(), Primitive::kPrimChar); EXPECT_EQ('a', s2->GetChar(statics.Get())); s2->SetChar<false>(statics.Get(), 'b'); - ArtField* s3 = mirror::Class::FindStaticField(soa.Self(), statics, "s3", "S"); + ArtField* s3 = mirror::Class::FindStaticField(soa.Self(), statics.Get(), "s3", "S"); EXPECT_EQ(s3->GetTypeAsPrimitiveType(), Primitive::kPrimShort); EXPECT_EQ(-536, s3->GetShort(statics.Get())); s3->SetShort<false>(statics.Get(), -535); - ArtField* s4 = mirror::Class::FindStaticField(soa.Self(), statics, "s4", "I"); + ArtField* s4 = mirror::Class::FindStaticField(soa.Self(), statics.Get(), "s4", "I"); EXPECT_EQ(s4->GetTypeAsPrimitiveType(), Primitive::kPrimInt); EXPECT_EQ(2000000000, s4->GetInt(statics.Get())); s4->SetInt<false>(statics.Get(), 2000000001); - ArtField* s5 = mirror::Class::FindStaticField(soa.Self(), statics, "s5", "J"); + ArtField* s5 = mirror::Class::FindStaticField(soa.Self(), statics.Get(), "s5", "J"); EXPECT_EQ(s5->GetTypeAsPrimitiveType(), Primitive::kPrimLong); EXPECT_EQ(0x1234567890abcdefLL, s5->GetLong(statics.Get())); s5->SetLong<false>(statics.Get(), INT64_C(0x34567890abcdef12)); - ArtField* s6 = mirror::Class::FindStaticField(soa.Self(), statics, "s6", "F"); + ArtField* s6 = mirror::Class::FindStaticField(soa.Self(), statics.Get(), "s6", "F"); EXPECT_EQ(s6->GetTypeAsPrimitiveType(), Primitive::kPrimFloat); EXPECT_DOUBLE_EQ(0.5, s6->GetFloat(statics.Get())); s6->SetFloat<false>(statics.Get(), 0.75); - ArtField* s7 = mirror::Class::FindStaticField(soa.Self(), statics, "s7", "D"); + ArtField* s7 = mirror::Class::FindStaticField(soa.Self(), statics.Get(), "s7", "D"); EXPECT_EQ(s7->GetTypeAsPrimitiveType(), Primitive::kPrimDouble); EXPECT_DOUBLE_EQ(16777217.0, s7->GetDouble(statics.Get())); s7->SetDouble<false>(statics.Get(), 16777219); - ArtField* s8 = mirror::Class::FindStaticField(soa.Self(), statics, "s8", - "Ljava/lang/String;"); + ArtField* s8 = mirror::Class::FindStaticField( + soa.Self(), statics.Get(), "s8", "Ljava/lang/String;"); EXPECT_EQ(s8->GetTypeAsPrimitiveType(), Primitive::kPrimNot); EXPECT_TRUE(s8->GetObject(statics.Get())->AsString()->Equals("android")); mirror::String* str_value = mirror::String::AllocFromModifiedUtf8(soa.Self(), "robot"); @@ -1131,10 +1133,14 @@ TEST_F(ClassLinkerTest, Interfaces) { EXPECT_EQ(Aj1, A->FindVirtualMethodForVirtualOrInterface(Jj1, kRuntimePointerSize)); EXPECT_EQ(Aj2, A->FindVirtualMethodForVirtualOrInterface(Jj2, kRuntimePointerSize)); - ArtField* Afoo = mirror::Class::FindStaticField(soa.Self(), A, "foo", "Ljava/lang/String;"); - ArtField* Bfoo = mirror::Class::FindStaticField(soa.Self(), B, "foo", "Ljava/lang/String;"); - ArtField* Jfoo = mirror::Class::FindStaticField(soa.Self(), J, "foo", "Ljava/lang/String;"); - ArtField* Kfoo = mirror::Class::FindStaticField(soa.Self(), K, "foo", "Ljava/lang/String;"); + ArtField* Afoo = + mirror::Class::FindStaticField(soa.Self(), A.Get(), "foo", "Ljava/lang/String;"); + ArtField* Bfoo = + mirror::Class::FindStaticField(soa.Self(), B.Get(), "foo", "Ljava/lang/String;"); + ArtField* Jfoo = + mirror::Class::FindStaticField(soa.Self(), J.Get(), "foo", "Ljava/lang/String;"); + ArtField* Kfoo = + mirror::Class::FindStaticField(soa.Self(), K.Get(), "foo", "Ljava/lang/String;"); ASSERT_TRUE(Afoo != nullptr); EXPECT_EQ(Afoo, Bfoo); EXPECT_EQ(Afoo, Jfoo); diff --git a/runtime/debugger.cc b/runtime/debugger.cc index dc2ae2e215..e33966617f 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -1565,16 +1565,16 @@ JDWP::JdwpError Dbg::OutputDeclaredMethods(JDWP::RefTypeId class_id, bool with_g JDWP::JdwpError Dbg::OutputDeclaredInterfaces(JDWP::RefTypeId class_id, JDWP::ExpandBuf* pReply) { JDWP::JdwpError error; Thread* self = Thread::Current(); - StackHandleScope<1> hs(self); - Handle<mirror::Class> c(hs.NewHandle(DecodeClass(class_id, &error))); - if (c.Get() == nullptr) { + ObjPtr<mirror::Class> c = DecodeClass(class_id, &error); + if (c == nullptr) { return error; } size_t interface_count = c->NumDirectInterfaces(); expandBufAdd4BE(pReply, interface_count); for (size_t i = 0; i < interface_count; ++i) { - expandBufAddRefTypeId(pReply, - gRegistry->AddRefType(mirror::Class::GetDirectInterface(self, c, i))); + ObjPtr<mirror::Class> interface = mirror::Class::GetDirectInterface(self, c, i); + DCHECK(interface != nullptr); + expandBufAddRefTypeId(pReply, gRegistry->AddRefType(interface)); } return JDWP::ERR_NONE; } diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index 01a2ad8f23..3c641b0b75 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -224,8 +224,8 @@ static jfieldID FindFieldID(const ScopedObjectAccess& soa, jclass jni_class, con } std::string temp; if (is_static) { - field = mirror::Class::FindStaticField(soa.Self(), c, name, - field_type->GetDescriptor(&temp)); + field = mirror::Class::FindStaticField( + soa.Self(), c.Get(), name, field_type->GetDescriptor(&temp)); } else { field = c->FindInstanceField(name, field_type->GetDescriptor(&temp)); } diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index 0cfe29bed9..a862c9798a 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -276,7 +276,7 @@ void Class::DumpClass(std::ostream& os, int flags) { if (num_direct_interfaces > 0) { os << " interfaces (" << num_direct_interfaces << "):\n"; for (size_t i = 0; i < num_direct_interfaces; ++i) { - ObjPtr<Class> interface = GetDirectInterface(self, h_this, i); + ObjPtr<Class> interface = GetDirectInterface(self, h_this.Get(), i); if (interface == nullptr) { os << StringPrintf(" %2zd: nullptr!\n", i); } else { @@ -799,24 +799,21 @@ ArtField* Class::FindDeclaredStaticField(ObjPtr<DexCache> dex_cache, uint32_t de } ArtField* Class::FindStaticField(Thread* self, - Handle<Class> klass, + ObjPtr<Class> klass, const StringPiece& name, const StringPiece& type) { // Is the field in this class (or its interfaces), or any of its // superclasses (or their interfaces)? - for (ObjPtr<Class> k = klass.Get(); k != nullptr; k = k->GetSuperClass()) { + for (ObjPtr<Class> k = klass; k != nullptr; k = k->GetSuperClass()) { // Is the field in this class? ArtField* f = k->FindDeclaredStaticField(name, type); if (f != nullptr) { return f; } - // Wrap k incase it moves during GetDirectInterface. - StackHandleScope<1> hs(self); - HandleWrapperObjPtr<Class> h_k(hs.NewHandleWrapper(&k)); // Is this field in any of this class' interfaces? - for (uint32_t i = 0; i < h_k->NumDirectInterfaces(); ++i) { - StackHandleScope<1> hs2(self); - Handle<Class> interface(hs2.NewHandle(GetDirectInterface(self, h_k, i))); + for (uint32_t i = 0, num_interfaces = k->NumDirectInterfaces(); i != num_interfaces; ++i) { + ObjPtr<Class> interface = GetDirectInterface(self, k, i); + DCHECK(interface != nullptr); f = FindStaticField(self, interface, name, type); if (f != nullptr) { return f; @@ -839,11 +836,10 @@ ArtField* Class::FindStaticField(Thread* self, // Though GetDirectInterface() should not cause thread suspension when called // from here, it takes a Handle as an argument, so we need to wrap `k`. ScopedAssertNoThreadSuspension ants(__FUNCTION__); - StackHandleScope<1> hs(self); - Handle<Class> h_k(hs.NewHandle(k)); // Is this field in any of this class' interfaces? - for (uint32_t i = 0; i < h_k->NumDirectInterfaces(); ++i) { - ObjPtr<Class> interface = GetDirectInterface(self, h_k, i); + for (uint32_t i = 0, num_interfaces = k->NumDirectInterfaces(); i != num_interfaces; ++i) { + ObjPtr<Class> interface = GetDirectInterface(self, k, i); + DCHECK(interface != nullptr); f = FindStaticField(self, interface, dex_cache, dex_field_idx); if (f != nullptr) { return f; @@ -854,11 +850,11 @@ ArtField* Class::FindStaticField(Thread* self, } ArtField* Class::FindField(Thread* self, - Handle<Class> klass, + ObjPtr<Class> klass, const StringPiece& name, const StringPiece& type) { // Find a field using the JLS field resolution order - for (ObjPtr<Class> k = klass.Get(); k != nullptr; k = k->GetSuperClass()) { + for (ObjPtr<Class> k = klass; k != nullptr; k = k->GetSuperClass()) { // Is the field in this class? ArtField* f = k->FindDeclaredInstanceField(name, type); if (f != nullptr) { @@ -869,12 +865,10 @@ ArtField* Class::FindField(Thread* self, return f; } // Is this field in any of this class' interfaces? - StackHandleScope<1> hs(self); - HandleWrapperObjPtr<Class> h_k(hs.NewHandleWrapper(&k)); - for (uint32_t i = 0; i < h_k->NumDirectInterfaces(); ++i) { - StackHandleScope<1> hs2(self); - Handle<Class> interface(hs2.NewHandle(GetDirectInterface(self, h_k, i))); - f = interface->FindStaticField(self, interface, name, type); + for (uint32_t i = 0, num_interfaces = k->NumDirectInterfaces(); i != num_interfaces; ++i) { + ObjPtr<Class> interface = GetDirectInterface(self, k, i); + DCHECK(interface != nullptr); + f = FindStaticField(self, interface, name, type); if (f != nullptr) { return f; } @@ -929,36 +923,46 @@ dex::TypeIndex Class::GetDirectInterfaceTypeIdx(uint32_t idx) { return GetInterfaceTypeList()->GetTypeItem(idx).type_idx_; } -ObjPtr<Class> Class::GetDirectInterface(Thread* self, - Handle<Class> klass, - uint32_t idx) { - DCHECK(klass.Get() != nullptr); +ObjPtr<Class> Class::GetDirectInterface(Thread* self, ObjPtr<Class> klass, uint32_t idx) { + DCHECK(klass != nullptr); DCHECK(!klass->IsPrimitive()); if (klass->IsArrayClass()) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + // Use ClassLinker::LookupClass(); avoid poisoning ObjPtr<>s by ClassLinker::FindSystemClass(). + ObjPtr<Class> interface; if (idx == 0) { - return class_linker->FindSystemClass(self, "Ljava/lang/Cloneable;"); + interface = class_linker->LookupClass(self, "Ljava/lang/Cloneable;", nullptr); } else { DCHECK_EQ(1U, idx); - return class_linker->FindSystemClass(self, "Ljava/io/Serializable;"); + interface = class_linker->LookupClass(self, "Ljava/io/Serializable;", nullptr); } + DCHECK(interface != nullptr); + return interface; } else if (klass->IsProxyClass()) { - ObjPtr<ObjectArray<Class>> interfaces = klass.Get()->GetInterfaces(); + ObjPtr<ObjectArray<Class>> interfaces = klass->GetInterfaces(); DCHECK(interfaces != nullptr); return interfaces->Get(idx); } else { dex::TypeIndex type_idx = klass->GetDirectInterfaceTypeIdx(idx); ObjPtr<Class> interface = klass->GetDexCache()->GetResolvedType(type_idx); - if (interface == nullptr) { - interface = Runtime::Current()->GetClassLinker()->ResolveType(klass->GetDexFile(), - type_idx, - klass.Get()); - CHECK(interface != nullptr || self->IsExceptionPending()); - } return interface; } } +ObjPtr<Class> Class::ResolveDirectInterface(Thread* self, Handle<Class> klass, uint32_t idx) { + ObjPtr<Class> interface = GetDirectInterface(self, klass.Get(), idx); + if (interface == nullptr) { + DCHECK(!klass->IsArrayClass()); + DCHECK(!klass->IsProxyClass()); + dex::TypeIndex type_idx = klass->GetDirectInterfaceTypeIdx(idx); + interface = Runtime::Current()->GetClassLinker()->ResolveType(klass->GetDexFile(), + type_idx, + klass.Get()); + CHECK(interface != nullptr || self->IsExceptionPending()); + } + return interface; +} + ObjPtr<Class> Class::GetCommonSuperClass(Handle<Class> klass) { DCHECK(klass.Get() != nullptr); DCHECK(!klass->IsInterface()); diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index 248c941d1c..d7449c8008 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -1087,7 +1087,9 @@ class MANAGED Class FINAL : public Object { ArtField* GetStaticField(uint32_t i) REQUIRES_SHARED(Locks::mutator_lock_); // Find a static or instance field using the JLS resolution order - static ArtField* FindField(Thread* self, Handle<Class> klass, const StringPiece& name, + static ArtField* FindField(Thread* self, + ObjPtr<Class> klass, + const StringPiece& name, const StringPiece& type) REQUIRES_SHARED(Locks::mutator_lock_); @@ -1108,7 +1110,7 @@ class MANAGED Class FINAL : public Object { // Finds the given static field in this class or a superclass. static ArtField* FindStaticField(Thread* self, - Handle<Class> klass, + ObjPtr<Class> klass, const StringPiece& name, const StringPiece& type) REQUIRES_SHARED(Locks::mutator_lock_); @@ -1204,9 +1206,15 @@ class MANAGED Class FINAL : public Object { dex::TypeIndex GetDirectInterfaceTypeIdx(uint32_t idx) REQUIRES_SHARED(Locks::mutator_lock_); - static ObjPtr<Class> GetDirectInterface(Thread* self, - Handle<Class> klass, - uint32_t idx) + // Get the direct interface of the `klass` at index `idx` if resolved, otherwise return null. + // If the caller expects the interface to be resolved, for example for a resolved `klass`, + // that assumption should be checked by `DCHECK(result != nullptr)`. + static ObjPtr<Class> GetDirectInterface(Thread* self, ObjPtr<Class> klass, uint32_t idx) + REQUIRES_SHARED(Locks::mutator_lock_); + + // Resolve and get the direct interface of the `klass` at index `idx`. + // Returns null with a pending exception if the resolution fails. + static ObjPtr<Class> ResolveDirectInterface(Thread* self, Handle<Class> klass, uint32_t idx) REQUIRES_SHARED(Locks::mutator_lock_); const char* GetSourceFile() REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc index 4b47f7f614..a6f56aee42 100644 --- a/runtime/mirror/object_test.cc +++ b/runtime/mirror/object_test.cc @@ -140,9 +140,9 @@ TEST_F(ObjectTest, AllocObjectArray) { Handle<mirror::Class> klass(hs.NewHandle(oa->GetClass())); ASSERT_EQ(2U, klass->NumDirectInterfaces()); EXPECT_OBJ_PTR_EQ(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Cloneable;"), - mirror::Class::GetDirectInterface(soa.Self(), klass, 0)); + mirror::Class::GetDirectInterface(soa.Self(), klass.Get(), 0)); EXPECT_OBJ_PTR_EQ(class_linker_->FindSystemClass(soa.Self(), "Ljava/io/Serializable;"), - mirror::Class::GetDirectInterface(soa.Self(), klass, 1)); + mirror::Class::GetDirectInterface(soa.Self(), klass.Get(), 1)); } TEST_F(ObjectTest, AllocArray) { @@ -708,19 +708,19 @@ TEST_F(ObjectTest, FindStaticField) { // Wrong type. EXPECT_TRUE(c->FindDeclaredStaticField("CASE_INSENSITIVE_ORDER", "I") == nullptr); EXPECT_TRUE(mirror::Class::FindStaticField( - soa.Self(), c, "CASE_INSENSITIVE_ORDER", "I") == nullptr); + soa.Self(), c.Get(), "CASE_INSENSITIVE_ORDER", "I") == nullptr); // Wrong name. EXPECT_TRUE(c->FindDeclaredStaticField( "cASE_INSENSITIVE_ORDER", "Ljava/util/Comparator;") == nullptr); EXPECT_TRUE( - mirror::Class::FindStaticField(soa.Self(), c, "cASE_INSENSITIVE_ORDER", - "Ljava/util/Comparator;") == nullptr); + mirror::Class::FindStaticField( + soa.Self(), c.Get(), "cASE_INSENSITIVE_ORDER", "Ljava/util/Comparator;") == nullptr); // Right name and type. ArtField* f1 = c->FindDeclaredStaticField("CASE_INSENSITIVE_ORDER", "Ljava/util/Comparator;"); - ArtField* f2 = mirror::Class::FindStaticField(soa.Self(), c, "CASE_INSENSITIVE_ORDER", - "Ljava/util/Comparator;"); + ArtField* f2 = mirror::Class::FindStaticField( + soa.Self(), c.Get(), "CASE_INSENSITIVE_ORDER", "Ljava/util/Comparator;"); EXPECT_TRUE(f1 != nullptr); EXPECT_TRUE(f2 != nullptr); EXPECT_EQ(f1, f2); diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index 642826c621..3341f531e4 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -278,7 +278,7 @@ static mirror::Field* GetPublicFieldRecursive( uint32_t num_direct_interfaces = h_clazz->NumDirectInterfaces(); for (uint32_t i = 0; i < num_direct_interfaces; i++) { - ObjPtr<mirror::Class> iface = mirror::Class::GetDirectInterface(self, h_clazz, i); + ObjPtr<mirror::Class> iface = mirror::Class::ResolveDirectInterface(self, h_clazz, i); if (UNLIKELY(iface == nullptr)) { self->AssertPendingException(); return nullptr; diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index 5730cf2492..94c12af199 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -1297,7 +1297,7 @@ TEST_F(OatFileAssistantTest, DexOptStatusValues) { for (std::pair<OatFileAssistant::DexOptNeeded, const char*> field : mapping) { ArtField* art_field = mirror::Class::FindStaticField( - soa.Self(), dexfile, field.second, "I"); + soa.Self(), dexfile.Get(), field.second, "I"); ASSERT_FALSE(art_field == nullptr); EXPECT_EQ(art_field->GetTypeAsPrimitiveType(), Primitive::kPrimInt); EXPECT_EQ(field.first, art_field->GetInt(dexfile.Get())); diff --git a/runtime/obj_ptr.h b/runtime/obj_ptr.h index d24c6fbd2c..2da2ae5650 100644 --- a/runtime/obj_ptr.h +++ b/runtime/obj_ptr.h @@ -51,27 +51,24 @@ class ObjPtr { REQUIRES_SHARED(Locks::mutator_lock_) : reference_(0u) {} - template <typename Type> + template <typename Type, + typename = typename std::enable_if<std::is_base_of<MirrorType, Type>::value>::type> ALWAYS_INLINE ObjPtr(Type* ptr) // NOLINT REQUIRES_SHARED(Locks::mutator_lock_) : reference_(Encode(static_cast<MirrorType*>(ptr))) { - static_assert(std::is_base_of<MirrorType, Type>::value, - "Input type must be a subtype of the ObjPtr type"); } - template <typename Type> + template <typename Type, + typename = typename std::enable_if<std::is_base_of<MirrorType, Type>::value>::type> ALWAYS_INLINE ObjPtr(const ObjPtr<Type, kPoison>& other) // NOLINT REQUIRES_SHARED(Locks::mutator_lock_) : reference_(Encode(static_cast<MirrorType*>(other.Ptr()))) { - static_assert(std::is_base_of<MirrorType, Type>::value, - "Input type must be a subtype of the ObjPtr type"); } - template <typename Type> + template <typename Type, + typename = typename std::enable_if<std::is_base_of<MirrorType, Type>::value>::type> ALWAYS_INLINE ObjPtr& operator=(const ObjPtr<Type, kPoison>& other) REQUIRES_SHARED(Locks::mutator_lock_) { - static_assert(std::is_base_of<MirrorType, Type>::value, - "Input type must be a subtype of the ObjPtr type"); reference_ = Encode(static_cast<MirrorType*>(other.Ptr())); return *this; } diff --git a/runtime/openjdkjvmti/ti_heap.cc b/runtime/openjdkjvmti/ti_heap.cc index 5e588a8a36..5f18b7cbce 100644 --- a/runtime/openjdkjvmti/ti_heap.cc +++ b/runtime/openjdkjvmti/ti_heap.cc @@ -484,7 +484,7 @@ class FollowReferencesHelper FINAL { art::Handle<art::mirror::Class> h_klass(hs.NewHandle<art::mirror::Class>(klass)); for (size_t i = 0; i < h_klass->NumDirectInterfaces(); ++i) { art::ObjPtr<art::mirror::Class> inf_klass = - art::mirror::Class::GetDirectInterface(self, h_klass, i); + art::mirror::Class::ResolveDirectInterface(self, h_klass, i); if (inf_klass == nullptr) { // TODO: With a resolved class this should not happen... self->ClearException(); diff --git a/runtime/proxy_test.cc b/runtime/proxy_test.cc index fd7e56d726..1292a819a3 100644 --- a/runtime/proxy_test.cc +++ b/runtime/proxy_test.cc @@ -128,8 +128,8 @@ TEST_F(ProxyTest, ProxyClassHelper) { ASSERT_TRUE(proxy_class->IsInitialized()); EXPECT_EQ(2U, proxy_class->NumDirectInterfaces()); // Interfaces$I and Interfaces$J. - EXPECT_OBJ_PTR_EQ(I.Get(), mirror::Class::GetDirectInterface(soa.Self(), proxy_class, 0)); - EXPECT_OBJ_PTR_EQ(J.Get(), mirror::Class::GetDirectInterface(soa.Self(), proxy_class, 1)); + EXPECT_OBJ_PTR_EQ(I.Get(), mirror::Class::GetDirectInterface(soa.Self(), proxy_class.Get(), 0)); + EXPECT_OBJ_PTR_EQ(J.Get(), mirror::Class::GetDirectInterface(soa.Self(), proxy_class.Get(), 1)); std::string temp; const char* proxy_class_descriptor = proxy_class->GetDescriptor(&temp); EXPECT_STREQ("L$Proxy1234;", proxy_class_descriptor); diff --git a/test/530-checker-loops4/src/Main.java b/test/530-checker-loops4/src/Main.java index 2e19c88ff5..7d3d7d9bfe 100644 --- a/test/530-checker-loops4/src/Main.java +++ b/test/530-checker-loops4/src/Main.java @@ -88,11 +88,9 @@ public class Main { // /// CHECK-START: int Main.geo4(int) loop_optimization (after) /// CHECK-DAG: <<Par:i\d+>> ParameterValue loop:none - /// CHECK-DAG: <<Zer:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: <<Int:i\d+>> IntConstant 7 loop:none /// CHECK-DAG: <<Rem:i\d+>> Rem [<<Par>>,<<Int>>] loop:none - /// CHECK-DAG: <<Add:i\d+>> Add [<<Rem>>,<<Zer>>] loop:none - /// CHECK-DAG: Return [<<Add>>] loop:none + /// CHECK-DAG: Return [<<Rem>>] loop:none // /// CHECK-START: int Main.geo4(int) loop_optimization (after) /// CHECK-NOT: Phi @@ -104,6 +102,17 @@ public class Main { } // TODO: someday? + // + /// CHECK-START: int Main.geo1BCE() BCE (before) + /// CHECK-DAG: BoundsCheck loop:none + /// CHECK-DAG: BoundsCheck loop:{{B\d+}} + // + /// CHECK-START: int Main.geo1BCE() BCE (after) + /// CHECK-DAG: BoundsCheck loop:{{B\d+}} + // + /// CHECK-START: int Main.geo1BCE() BCE (after) + /// CHECK-NOT: BoundsCheck loop:none + /// CHECK-NOT: Deoptimize public static int geo1BCE() { int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 19, 19, 20, @@ -118,6 +127,17 @@ public class Main { } // TODO: someday? + // + /// CHECK-START: int Main.geo2BCE() BCE (before) + /// CHECK-DAG: BoundsCheck loop:none + /// CHECK-DAG: BoundsCheck loop:{{B\d+}} + // + /// CHECK-START: int Main.geo2BCE() BCE (after) + /// CHECK-DAG: BoundsCheck loop:{{B\d+}} + // + /// CHECK-START: int Main.geo2BCE() BCE (after) + /// CHECK-NOT: BoundsCheck loop:none + /// CHECK-NOT: Deoptimize public static int geo2BCE() { int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 19, 19, 20, @@ -225,16 +245,20 @@ public class Main { return a; } - // TODO: Rem is already optimized away by the time the loop optimizer runs; - // we could still optimize this case with last value on wrap-around! + // Even though Rem is already optimized away by the time induction analysis + // and the loop optimizer run, the loop is optimized with a trivial + // wrap-around induction just as the wrap-around for REM would. // /// CHECK-START: int Main.geoRemBlackHole(int) loop_optimization (before) /// CHECK-DAG: Phi loop:<<Loop:B\d+>> /// CHECK-DAG: Phi loop:<<Loop>> // /// CHECK-START: int Main.geoRemBlackHole(int) loop_optimization (after) - /// CHECK-DAG: Phi loop:<<Loop:B\d+>> - /// CHECK-DAG: Phi loop:<<Loop>> + /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 + /// CHECK-DAG: Return [<<Zero>>] + // + /// CHECK-START: int Main.geoRemBlackHole(int) loop_optimization (after) + /// CHECK-NOT: Phi public static int geoRemBlackHole(int a) { for (int i = 0; i < 100; i++) { a %= 1; diff --git a/test/530-checker-loops5/expected.txt b/test/530-checker-loops5/expected.txt new file mode 100644 index 0000000000..b0aad4deb5 --- /dev/null +++ b/test/530-checker-loops5/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/530-checker-loops5/info.txt b/test/530-checker-loops5/info.txt new file mode 100644 index 0000000000..15dbf37a3b --- /dev/null +++ b/test/530-checker-loops5/info.txt @@ -0,0 +1 @@ +Test on loop optimizations, in particular with polynomial induction. diff --git a/test/530-checker-loops5/src/Main.java b/test/530-checker-loops5/src/Main.java new file mode 100644 index 0000000000..54b54d08bc --- /dev/null +++ b/test/530-checker-loops5/src/Main.java @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2016 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. + */ + +// +// Test on loop optimizations, in particular around polynomial induction. +// +public class Main { + + /// CHECK-START: int Main.poly1() loop_optimization (before) + /// CHECK-DAG: Phi loop:<<Loop:B\d+>> + /// CHECK-DAG: Add loop:<<Loop>> + /// CHECK-DAG: Add loop:<<Loop>> + // + /// CHECK-START: int Main.poly1() loop_optimization (after) + /// CHECK-DAG: <<Zer:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Int:i\d+>> IntConstant 55 loop:none + /// CHECK-DAG: <<Add:i\d+>> Add [<<Int>>,<<Zer>>] loop:none + /// CHECK-DAG: Return [<<Add>>] loop:none + // + /// CHECK-START: int Main.poly1() instruction_simplifier$after_bce (after) + /// CHECK-DAG: <<Int:i\d+>> IntConstant 55 loop:none + /// CHECK-DAG: Return [<<Int>>] loop:none + // + /// CHECK-START: int Main.poly1() loop_optimization (after) + /// CHECK-NOT: Phi + public static int poly1() { + int a = 0; + for (int i = 0; i <= 10; i++) { + a += i; + } + return a; + } + + // Multiplication in linear induction has been optimized earlier, + // but that does not stop the induction variable recognition + // and loop optimizer. + // + /// CHECK-START: int Main.poly2(int) loop_optimization (before) + /// CHECK-DAG: Phi loop:<<Loop:B\d+>> + /// CHECK-DAG: Shl loop:<<Loop>> + /// CHECK-DAG: Add loop:<<Loop>> + /// CHECK-DAG: Add loop:<<Loop>> + /// CHECK-DAG: Add loop:<<Loop>> + /// CHECK-DAG: Add loop:<<Loop>> + // + /// CHECK-START: int Main.poly2(int) loop_optimization (after) + /// CHECK-DAG: <<Par:i\d+>> ParameterValue loop:none + /// CHECK-DAG: <<Int:i\d+>> IntConstant 185 loop:none + /// CHECK-DAG: <<Add:i\d+>> Add [<<Int>>,<<Par>>] loop:none + /// CHECK-DAG: Return [<<Add>>] loop:none + // + /// CHECK-START: int Main.poly2(int) loop_optimization (after) + /// CHECK-NOT: Phi + public static int poly2(int a) { + for (int i = 0; i < 10; i++) { + int k = 3 * i + 5; + a += k; + } + return a; + } + + /// CHECK-START: int Main.poly3() loop_optimization (before) + /// CHECK-DAG: Phi loop:<<Loop:B\d+>> + /// CHECK-DAG: Add loop:<<Loop>> + /// CHECK-DAG: Add loop:<<Loop>> + // + /// CHECK-START: int Main.poly3() loop_optimization (after) + /// CHECK-DAG: <<Ini:i\d+>> IntConstant 12345 loop:none + /// CHECK-DAG: <<Int:i\d+>> IntConstant -2146736968 loop:none + /// CHECK-DAG: <<Add:i\d+>> Add [<<Int>>,<<Ini>>] loop:none + /// CHECK-DAG: Return [<<Add>>] loop:none + // + /// CHECK-START: int Main.poly3() instruction_simplifier$after_bce (after) + /// CHECK-DAG: <<Int:i\d+>> IntConstant -2146724623 loop:none + /// CHECK-DAG: Return [<<Int>>] loop:none + // + /// CHECK-START: int Main.poly3() loop_optimization (after) + /// CHECK-NOT: Phi + public static int poly3() { + int a = 12345; + for (int i = 0; i <= 10; i++) { + a += (2147483646 * i + 67890); + } + return a; + } + + /// CHECK-START: int Main.polyBCE1() BCE (before) + /// CHECK-DAG: BoundsCheck loop:none + /// CHECK-DAG: BoundsCheck loop:{{B\d+}} + // + /// CHECK-START: int Main.polyBCE1() BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + public static int polyBCE1() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 19, 19, 20, + 21, 22 }; + int a = 0; + int r = 0; + for (int i = 0; i < 8; i++) { + r += x[a]; + a += i; + } + return r; + } + + /// CHECK-START: int Main.polyBCE2() BCE (before) + /// CHECK-DAG: BoundsCheck loop:none + /// CHECK-DAG: BoundsCheck loop:{{B\d+}} + // + /// CHECK-START: int Main.polyBCE2() BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + public static int polyBCE2() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 19, 19, 20, + 21, 22, 23, 24, 25, 26, 27 }; + int a = 1; + int r = 0; + for (int i = 0; i < 6; i++) { + int k = 2 * i + 1; + r -= x[a]; + a += k; + } + return r; + } + + /// CHECK-START: int Main.polyBCE3() BCE (before) + /// CHECK-DAG: BoundsCheck loop:none + /// CHECK-DAG: BoundsCheck loop:{{B\d+}} + // + /// CHECK-START: int Main.polyBCE3() BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + public static int polyBCE3() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 19, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 38 }; + int r = 0; + int a1 = 1; + int a2 = 2; + for (int i = 0; i < 5; i++) { + int t = a1 + a2; // two polynomials combined into new polynomial + r -= x[t]; + a1 += (3 * i + 1); + a2 += (2 * i); + } + return r; + } + + // + // Verifier. + // + + public static void main(String[] args) { + expectEquals(55, poly1()); + expectEquals(185, poly2(0)); + expectEquals(192, poly2(7)); + expectEquals(-2146724623, poly3()); + expectEquals(64, polyBCE1()); + expectEquals(-68, polyBCE2()); + expectEquals(-80, polyBCE3()); + + System.out.println("passed"); + } + + private static void expectEquals(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } +} diff --git a/test/618-checker-induction/src/Main.java b/test/618-checker-induction/src/Main.java index f85479aa54..87a69b25c4 100644 --- a/test/618-checker-induction/src/Main.java +++ b/test/618-checker-induction/src/Main.java @@ -25,7 +25,7 @@ public class Main { /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:none // /// CHECK-START: void Main.deadSingleLoop() loop_optimization (after) - /// CHECK-NOT: Phi loop:{{B\d+}} outer_loop:none + /// CHECK-NOT: Phi static void deadSingleLoop() { for (int i = 0; i < 4; i++) { } @@ -35,7 +35,7 @@ public class Main { /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:none // /// CHECK-START: void Main.deadSingleLoop() loop_optimization (after) - /// CHECK-NOT: Phi loop:{{B\d+}} outer_loop:none + /// CHECK-NOT: Phi static void deadSingleLoopN(int n) { for (int i = 0; i < n; i++) { } @@ -56,7 +56,7 @@ public class Main { /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:<<Loop>> // /// CHECK-START: void Main.deadNestedLoops() loop_optimization (after) - /// CHECK-NOT: Phi loop:{{B\d+}} + /// CHECK-NOT: Phi static void deadNestedLoops() { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { @@ -74,7 +74,7 @@ public class Main { /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:none // /// CHECK-START: void Main.deadNestedAndFollowingLoops() loop_optimization (after) - /// CHECK-NOT: Phi loop:{{B\d+}} + /// CHECK-NOT: Phi static void deadNestedAndFollowingLoops() { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { @@ -96,7 +96,7 @@ public class Main { /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:none // /// CHECK-START: void Main.deadConditional(int) loop_optimization (after) - /// CHECK-NOT: Phi loop:{{B\d+}} + /// CHECK-NOT: Phi public static void deadConditional(int n) { int k = 0; int m = 0; @@ -116,7 +116,7 @@ public class Main { /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none // /// CHECK-START: void Main.deadConditionalCycle(int) loop_optimization (after) - /// CHECK-NOT: Phi loop:{{B\d+}} + /// CHECK-NOT: Phi public static void deadConditionalCycle(int n) { int k = 0; int m = 0; @@ -215,12 +215,11 @@ public class Main { /// CHECK-DAG: Return [<<Phi1>>] loop:none // /// CHECK-START: int Main.closedFormInductionUp() loop_optimization (after) - /// CHECK-NOT: Phi loop:{{B\d+}} outer_loop:none - /// CHECK-DAG: Return loop:none + /// CHECK-NOT: Phi // /// CHECK-START: int Main.closedFormInductionUp() instruction_simplifier$after_bce (after) - /// CHECK-DAG: <<Int:i\d+>> IntConstant 12395 - /// CHECK-DAG: Return [<<Int>>] loop:none + /// CHECK-DAG: <<Int:i\d+>> IntConstant 12395 loop:none + /// CHECK-DAG: Return [<<Int>>] loop:none static int closedFormInductionUp() { int closed = 12345; for (int i = 0; i < 10; i++) { @@ -235,8 +234,13 @@ public class Main { /// CHECK-DAG: Return [<<Phi2>>] loop:none // /// CHECK-START: int Main.closedFormInductionInAndDown(int) loop_optimization (after) - /// CHECK-NOT: Phi loop:{{B\d+}} outer_loop:none - /// CHECK-DAG: Return loop:none + /// CHECK-NOT: Phi + // + /// CHECK-START: int Main.closedFormInductionInAndDown(int) instruction_simplifier$after_bce (after) + /// CHECK-DAG: <<Par:i\d+>> ParameterValue loop:none + /// CHECK-DAG: <<Int:i\d+>> IntConstant -50 loop:none + /// CHECK-DAG: <<Add:i\d+>> Add [<<Int>>,<<Par>>] loop:none + /// CHECK-DAG: Return [<<Add>>] loop:none static int closedFormInductionInAndDown(int closed) { for (int i = 0; i < 10; i++) { closed -= 5; @@ -252,12 +256,10 @@ public class Main { /// CHECK-DAG: Return [<<Phi1>>] loop:none // /// CHECK-START: int Main.closedFormNested() loop_optimization (after) - /// CHECK-NOT: Phi loop:{{B\d+}} outer_loop:none - /// CHECK-NOT: Phi loop:{{B\d+}} outer_loop:loop{{B\d+}} - /// CHECK-DAG: Return loop:none + /// CHECK-NOT: Phi // /// CHECK-START: int Main.closedFormNested() instruction_simplifier$after_bce (after) - /// CHECK-DAG: <<Int:i\d+>> IntConstant 100 + /// CHECK-DAG: <<Int:i\d+>> IntConstant 100 loop:none /// CHECK-DAG: Return [<<Int>>] loop:none static int closedFormNested() { int closed = 0; @@ -277,13 +279,11 @@ public class Main { /// CHECK-DAG: Return [<<Phi1>>] loop:none // /// CHECK-START: int Main.closedFormNestedAlt() loop_optimization (after) - /// CHECK-NOT: Phi loop:{{B\d+}} outer_loop:none - /// CHECK-NOT: Phi loop:{{B\d+}} outer_loop:loop{{B\d+}} - /// CHECK-DAG: Return loop:none + /// CHECK-NOT: Phi // /// CHECK-START: int Main.closedFormNestedAlt() instruction_simplifier$after_bce (after) - /// CHECK-DAG: <<Int:i\d+>> IntConstant 15082 - /// CHECK-DAG: Return [<<Int>>] loop:none + /// CHECK-DAG: <<Int:i\d+>> IntConstant 15082 loop:none + /// CHECK-DAG: Return [<<Int>>] loop:none static int closedFormNestedAlt() { int closed = 12345; for (int i = 0; i < 17; i++) { @@ -360,11 +360,10 @@ public class Main { /// CHECK-DAG: Return [<<Phi>>] loop:none // /// CHECK-START: int Main.mainIndexReturned() loop_optimization (after) - /// CHECK-NOT: Phi loop:{{B\d+}} outer_loop:none - /// CHECK-DAG: Return loop:none + /// CHECK-NOT: Phi // /// CHECK-START: int Main.mainIndexReturned() instruction_simplifier$after_bce (after) - /// CHECK-DAG: <<Int:i\d+>> IntConstant 10 + /// CHECK-DAG: <<Int:i\d+>> IntConstant 10 loop:none /// CHECK-DAG: Return [<<Int>>] loop:none static int mainIndexReturned() { int i; @@ -378,11 +377,10 @@ public class Main { /// CHECK-DAG: Return [<<Phi2>>] loop:none // /// CHECK-START: int Main.periodicReturned9() loop_optimization (after) - /// CHECK-NOT: Phi loop:{{B\d+}} outer_loop:none - /// CHECK-DAG: Return loop:none + /// CHECK-NOT: Phi // /// CHECK-START: int Main.periodicReturned9() instruction_simplifier$after_bce (after) - /// CHECK-DAG: <<Int:i\d+>> IntConstant 1 + /// CHECK-DAG: <<Int:i\d+>> IntConstant 1 loop:none /// CHECK-DAG: Return [<<Int>>] loop:none static int periodicReturned9() { int k = 0; @@ -398,11 +396,10 @@ public class Main { /// CHECK-DAG: Return [<<Phi2>>] loop:none // /// CHECK-START: int Main.periodicReturned10() loop_optimization (after) - /// CHECK-NOT: Phi loop:{{B\d+}} outer_loop:none - /// CHECK-DAG: Return loop:none + /// CHECK-NOT: Phi // /// CHECK-START: int Main.periodicReturned10() instruction_simplifier$after_bce (after) - /// CHECK-DAG: <<Int:i\d+>> IntConstant 0 + /// CHECK-DAG: <<Int:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: Return [<<Int>>] loop:none static int periodicReturned10() { int k = 0; @@ -412,7 +409,18 @@ public class Main { return k; } - // If ever replaced by closed form, last value should be correct! + /// CHECK-START: int Main.getSum21() loop_optimization (before) + /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Phi3:i\d+>> Phi loop:<<Loop>> outer_loop:none + /// CHECK-DAG: Return [<<Phi3>>] loop:none + // + /// CHECK-START: int Main.getSum21() loop_optimization (after) + /// CHECK-NOT: Phi + // + /// CHECK-START: int Main.getSum21() instruction_simplifier$after_bce (after) + /// CHECK-DAG: <<Int:i\d+>> IntConstant 21 loop:none + /// CHECK-DAG: Return [<<Int>>] loop:none private static int getSum21() { int k = 0; int sum = 0; @@ -436,8 +444,7 @@ public class Main { /// CHECK-DAG: Return [<<Phi2>>] loop:none // /// CHECK-START: int Main.periodicReturnedN(int) loop_optimization (after) - /// CHECK-NOT: Phi loop:{{B\d+}} outer_loop:none - /// CHECK-DAG: Return loop:none + /// CHECK-NOT: Phi static int periodicReturnedN(int n) { int k = 0; for (int i = 0; i < n; i++) { @@ -480,11 +487,10 @@ public class Main { /// CHECK-EVAL: "<<Loop1>>" != "<<Loop2>>" // /// CHECK-START: int Main.closedFeed() loop_optimization (after) - /// CHECK-NOT: Phi loop:{{B\d+}} outer_loop:none - /// CHECK-DAG: Return loop:none + /// CHECK-NOT: Phi // /// CHECK-START: int Main.closedFeed() instruction_simplifier$after_bce (after) - /// CHECK-DAG: <<Int:i\d+>> IntConstant 20 + /// CHECK-DAG: <<Int:i\d+>> IntConstant 20 loop:none /// CHECK-DAG: Return [<<Int>>] loop:none private static int closedFeed() { int closed = 0; @@ -505,11 +511,10 @@ public class Main { /// CHECK-DAG: Return [<<Phi1>>] loop:none // /// CHECK-START: int Main.closedLargeUp() loop_optimization (after) - /// CHECK-NOT: Phi loop:B\d+ outer_loop:none - /// CHECK-DAG: Return loop:none + /// CHECK-NOT: Phi // /// CHECK-START: int Main.closedLargeUp() instruction_simplifier$after_bce (after) - /// CHECK-DAG: <<Int:i\d+>> IntConstant -10 + /// CHECK-DAG: <<Int:i\d+>> IntConstant -10 loop:none /// CHECK-DAG: Return [<<Int>>] loop:none private static int closedLargeUp() { int closed = 0; @@ -525,11 +530,10 @@ public class Main { /// CHECK-DAG: Return [<<Phi1>>] loop:none // /// CHECK-START: int Main.closedLargeDown() loop_optimization (after) - /// CHECK-NOT: Phi loop:B\d+ outer_loop:none - /// CHECK-DAG: Return loop:none + /// CHECK-NOT: Phi // /// CHECK-START: int Main.closedLargeDown() instruction_simplifier$after_bce (after) - /// CHECK-DAG: <<Int:i\d+>> IntConstant 10 + /// CHECK-DAG: <<Int:i\d+>> IntConstant 10 loop:none /// CHECK-DAG: Return [<<Int>>] loop:none private static int closedLargeDown() { int closed = 0; @@ -548,11 +552,10 @@ public class Main { /// CHECK-DAG: Return [<<Phi5>>] loop:none // /// CHECK-START: int Main.waterFall() loop_optimization (after) - /// CHECK-NOT: Phi loop:B\d+ outer_loop:none - /// CHECK-DAG: Return loop:none + /// CHECK-NOT: Phi // /// CHECK-START: int Main.waterFall() instruction_simplifier$after_bce (after) - /// CHECK-DAG: <<Int:i\d+>> IntConstant 50 + /// CHECK-DAG: <<Int:i\d+>> IntConstant 50 loop:none /// CHECK-DAG: Return [<<Int>>] loop:none private static int waterFall() { int i = 0; @@ -570,11 +573,10 @@ public class Main { /// CHECK-DAG: Return [<<Phi2>>] loop:none // /// CHECK-START: boolean Main.periodicBoolIdiom1() loop_optimization (after) - /// CHECK-NOT: Phi loop:{{B\d+}} outer_loop:none - /// CHECK-DAG: Return loop:none + /// CHECK-NOT: Phi // /// CHECK-START: boolean Main.periodicBoolIdiom1() instruction_simplifier$after_bce (after) - /// CHECK-DAG: <<Int:i\d+>> IntConstant 0 + /// CHECK-DAG: <<Int:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: Return [<<Int>>] loop:none private static boolean periodicBoolIdiom1() { boolean x = true; @@ -590,11 +592,10 @@ public class Main { /// CHECK-DAG: Return [<<Phi2>>] loop:none // /// CHECK-START: boolean Main.periodicBoolIdiom2() loop_optimization (after) - /// CHECK-NOT: Phi loop:{{B\d+}} outer_loop:none - /// CHECK-DAG: Return loop:none + /// CHECK-NOT: Phi // /// CHECK-START: boolean Main.periodicBoolIdiom2() instruction_simplifier$after_bce (after) - /// CHECK-DAG: <<Int:i\d+>> IntConstant 0 + /// CHECK-DAG: <<Int:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: Return [<<Int>>] loop:none private static boolean periodicBoolIdiom2() { boolean x = true; @@ -610,11 +611,10 @@ public class Main { /// CHECK-DAG: Return [<<Phi2>>] loop:none // /// CHECK-START: boolean Main.periodicBoolIdiom3() loop_optimization (after) - /// CHECK-NOT: Phi loop:{{B\d+}} outer_loop:none - /// CHECK-DAG: Return loop:none + /// CHECK-NOT: Phi // /// CHECK-START: boolean Main.periodicBoolIdiom3() instruction_simplifier$after_bce (after) - /// CHECK-DAG: <<Int:i\d+>> IntConstant 0 + /// CHECK-DAG: <<Int:i\d+>> IntConstant 0 loop:none /// CHECK-DAG: Return [<<Int>>] loop:none private static boolean periodicBoolIdiom3() { boolean x = true; @@ -630,8 +630,7 @@ public class Main { /// CHECK-DAG: Return [<<Phi2>>] loop:none // /// CHECK-START: boolean Main.periodicBoolIdiom1N(boolean, int) loop_optimization (after) - /// CHECK-NOT: Phi loop:{{B\d+}} outer_loop:none - /// CHECK-DAG: Return loop:none + /// CHECK-NOT: Phi private static boolean periodicBoolIdiom1N(boolean x, int n) { for (int i = 0; i < n; i++) { x = !x; @@ -645,8 +644,7 @@ public class Main { /// CHECK-DAG: Return [<<Phi2>>] loop:none // /// CHECK-START: boolean Main.periodicBoolIdiom2N(boolean, int) loop_optimization (after) - /// CHECK-NOT: Phi loop:{{B\d+}} outer_loop:none - /// CHECK-DAG: Return loop:none + /// CHECK-NOT: Phi private static boolean periodicBoolIdiom2N(boolean x, int n) { for (int i = 0; i < n; i++) { x = (x != true); @@ -660,8 +658,7 @@ public class Main { /// CHECK-DAG: Return [<<Phi2>>] loop:none // /// CHECK-START: boolean Main.periodicBoolIdiom3N(boolean, int) loop_optimization (after) - /// CHECK-NOT: Phi loop:{{B\d+}} outer_loop:none - /// CHECK-DAG: Return loop:none + /// CHECK-NOT: Phi private static boolean periodicBoolIdiom3N(boolean x, int n) { for (int i = 0; i < n; i++) { x = (x == false); diff --git a/test/956-methodhandles/src/Main.java b/test/956-methodhandles/src/Main.java index ee9c43604a..17b56b4fd9 100644 --- a/test/956-methodhandles/src/Main.java +++ b/test/956-methodhandles/src/Main.java @@ -1080,7 +1080,7 @@ public class Main { return result; } public static Long sumToReference(int... ints) { - System.err.println("Hi"); + System.out.println("Hi"); return new Long(sumToPrimitive(ints)); } public static MethodHandles.Lookup lookup() { @@ -1432,13 +1432,13 @@ public class Main { assertEquals(Long.valueOf(10l), (Long) mh.invoke(1, 2, 3, 4)); try { // WrongMethodTypeException should be raised before invoke here. - System.err.print("Expect Hi here: "); + System.out.print("Expect Hi here: "); assertEquals(Long.valueOf(10l), (Byte) mh.invoke(1, 2, 3, 4)); fail(); } catch (ClassCastException e) {} try { // WrongMethodTypeException should be raised before invoke here. - System.err.println("Don't expect Hi now"); + System.out.println("Don't expect Hi now"); byte b = (byte) mh.invoke(1, 2, 3, 4); fail(); } catch (WrongMethodTypeException e) {} |