summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/driver/compiler_driver.cc28
-rw-r--r--compiler/optimizing/code_generator_arm_vixl.cc900
-rw-r--r--compiler/optimizing/code_generator_arm_vixl.h23
-rw-r--r--compiler/optimizing/induction_var_analysis.cc178
-rw-r--r--compiler/optimizing/induction_var_analysis.h24
-rw-r--r--compiler/optimizing/induction_var_analysis_test.cc155
-rw-r--r--compiler/optimizing/induction_var_range.cc140
-rw-r--r--compiler/optimizing/induction_var_range.h18
-rw-r--r--compiler/optimizing/induction_var_range_test.cc101
-rw-r--r--compiler/optimizing/loop_optimization.cc103
-rw-r--r--compiler/optimizing/loop_optimization.h3
-rw-r--r--runtime/class_linker.cc41
-rw-r--r--runtime/class_linker_test.cc38
-rw-r--r--runtime/debugger.cc10
-rw-r--r--runtime/jni_internal.cc4
-rw-r--r--runtime/mirror/class.cc72
-rw-r--r--runtime/mirror/class.h18
-rw-r--r--runtime/mirror/object_test.cc14
-rw-r--r--runtime/native/java_lang_Class.cc2
-rw-r--r--runtime/oat_file_assistant_test.cc2
-rw-r--r--runtime/obj_ptr.h15
-rw-r--r--runtime/openjdkjvmti/ti_heap.cc2
-rw-r--r--runtime/proxy_test.cc4
-rw-r--r--test/530-checker-loops4/src/Main.java38
-rw-r--r--test/530-checker-loops5/expected.txt1
-rw-r--r--test/530-checker-loops5/info.txt1
-rw-r--r--test/530-checker-loops5/src/Main.java186
-rw-r--r--test/618-checker-induction/src/Main.java117
-rw-r--r--test/956-methodhandles/src/Main.java6
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(&parallel_move);
+ } else {
+ codegen->GetMoveResolver()->EmitNativeCode(&parallel_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) {}