Merge "Fixed a few bugs in dexfuzz:"
diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc
index 669d8cd..9d39bf2 100644
--- a/compiler/dex/verification_results.cc
+++ b/compiler/dex/verification_results.cc
@@ -103,6 +103,17 @@
return (it != verified_methods_.end()) ? it->second : nullptr;
}
+void VerificationResults::CreateVerifiedMethodFor(MethodReference ref) {
+ // This method should only be called for classes verified at compile time,
+ // which have no verifier error, nor has methods that we know will throw
+ // at runtime.
+ AtomicMap::InsertResult result = atomic_verified_methods_.Insert(
+ ref,
+ /*expected*/ nullptr,
+ new VerifiedMethod(/* encountered_error_types */ 0, /* has_runtime_throw */ false));
+ DCHECK_EQ(result, AtomicMap::kInsertResultSuccess);
+}
+
void VerificationResults::AddRejectedClass(ClassReference ref) {
{
WriterMutexLock mu(Thread::Current(), rejected_classes_lock_);
diff --git a/compiler/dex/verification_results.h b/compiler/dex/verification_results.h
index ea38f4d..22749fa 100644
--- a/compiler/dex/verification_results.h
+++ b/compiler/dex/verification_results.h
@@ -32,6 +32,7 @@
namespace verifier {
class MethodVerifier;
+class VerifierDepsTest;
} // namespace verifier
class CompilerOptions;
@@ -47,6 +48,9 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!verified_methods_lock_);
+ void CreateVerifiedMethodFor(MethodReference ref)
+ REQUIRES(!verified_methods_lock_);
+
const VerifiedMethod* GetVerifiedMethod(MethodReference ref)
REQUIRES(!verified_methods_lock_);
@@ -77,6 +81,8 @@
// Rejected classes.
ReaderWriterMutex rejected_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
std::set<ClassReference> rejected_classes_ GUARDED_BY(rejected_classes_lock_);
+
+ friend class verifier::VerifierDepsTest;
};
} // namespace art
diff --git a/compiler/dex/verified_method.h b/compiler/dex/verified_method.h
index 04331e5..ce53417 100644
--- a/compiler/dex/verified_method.h
+++ b/compiler/dex/verified_method.h
@@ -32,6 +32,8 @@
class VerifiedMethod {
public:
+ VerifiedMethod(uint32_t encountered_error_types, bool has_runtime_throw);
+
// Cast elision set type.
// Since we're adding the dex PCs to the set in increasing order, a sorted vector
// is better for performance (not just memory usage), especially for large sets.
@@ -80,8 +82,6 @@
}
private:
- VerifiedMethod(uint32_t encountered_error_types, bool has_runtime_throw);
-
/*
* Generate the GC map for a method that has just been verified (i.e. we're doing this as part of
* verification). For type-precise determination we have all the data we need, so we just need to
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 6b62110..a2bab80 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -2005,6 +2005,35 @@
}
}
+static void PopulateVerifiedMethods(const DexFile& dex_file,
+ uint32_t class_def_index,
+ VerificationResults* verification_results) {
+ const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
+ const uint8_t* class_data = dex_file.GetClassData(class_def);
+ if (class_data == nullptr) {
+ return;
+ }
+ ClassDataItemIterator it(dex_file, class_data);
+ // Skip fields
+ while (it.HasNextStaticField()) {
+ it.Next();
+ }
+ while (it.HasNextInstanceField()) {
+ it.Next();
+ }
+
+ while (it.HasNextDirectMethod()) {
+ verification_results->CreateVerifiedMethodFor(MethodReference(&dex_file, it.GetMemberIndex()));
+ it.Next();
+ }
+
+ while (it.HasNextVirtualMethod()) {
+ verification_results->CreateVerifiedMethodFor(MethodReference(&dex_file, it.GetMemberIndex()));
+ it.Next();
+ }
+ DCHECK(!it.HasNext());
+}
+
void CompilerDriver::Verify(jobject jclass_loader,
const std::vector<const DexFile*>& dex_files,
TimingLogger* timings) {
@@ -2041,6 +2070,13 @@
} else if (set.find(class_def.class_idx_) == set.end()) {
ObjectLock<mirror::Class> lock(soa.Self(), cls);
mirror::Class::SetStatus(cls, mirror::Class::kStatusVerified, soa.Self());
+ // Create `VerifiedMethod`s for each methods, the compiler expects one for
+ // quickening or compiling.
+ // Note that this means:
+ // - We're only going to compile methods that did verify.
+ // - Quickening will not do checkcast ellision.
+ // TODO(ngeoffray): Reconsider this once we refactor compiler filters.
+ PopulateVerifiedMethods(*dex_file, i, verification_results_);
}
}
}
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 1ca439e..4b24ac3 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -63,9 +63,10 @@
// We expected this for both core and fpu register pairs.
return ((location.low() & 1) == 0) && (location.low() + 1 == location.high());
}
-
+// Use a local definition to prevent copying mistakes.
+static constexpr size_t kArmWordSize = static_cast<size_t>(kArmPointerSize);
+static constexpr size_t kArmBitsPerWord = kArmWordSize * kBitsPerByte;
static constexpr int kCurrentMethodStackOffset = 0;
-static constexpr size_t kArmInstrMaxSizeInBytes = 4u;
static constexpr uint32_t kPackedSwitchCompareJumpThreshold = 7;
#ifdef __
@@ -438,6 +439,62 @@
DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathARMVIXL);
};
+class LoadStringSlowPathARMVIXL : public SlowPathCodeARMVIXL {
+ public:
+ explicit LoadStringSlowPathARMVIXL(HLoadString* instruction)
+ : SlowPathCodeARMVIXL(instruction) {}
+
+ void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ LocationSummary* locations = instruction_->GetLocations();
+ DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
+ HLoadString* load = instruction_->AsLoadString();
+ const uint32_t string_index = load->GetStringIndex().index_;
+ vixl32::Register out = OutputRegister(load);
+ vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
+ constexpr bool call_saves_everything_except_r0 = (!kUseReadBarrier || kUseBakerReadBarrier);
+
+ CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
+ __ Bind(GetEntryLabel());
+ SaveLiveRegisters(codegen, locations);
+
+ InvokeRuntimeCallingConventionARMVIXL calling_convention;
+ // In the unlucky case that the `temp` is R0, we preserve the address in `out` across
+ // the kSaveEverything call (or use `out` for the address after non-kSaveEverything call).
+ bool temp_is_r0 = (temp.Is(calling_convention.GetRegisterAt(0)));
+ vixl32::Register entry_address = temp_is_r0 ? out : temp;
+ DCHECK(!entry_address.Is(calling_convention.GetRegisterAt(0)));
+ if (call_saves_everything_except_r0 && temp_is_r0) {
+ __ Mov(entry_address, temp);
+ }
+
+ __ Mov(calling_convention.GetRegisterAt(0), string_index);
+ arm_codegen->InvokeRuntime(kQuickResolveString, instruction_, instruction_->GetDexPc(), this);
+ CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
+
+ // Store the resolved String to the .bss entry.
+ if (call_saves_everything_except_r0) {
+ // The string entry address was preserved in `entry_address` thanks to kSaveEverything.
+ __ Str(r0, MemOperand(entry_address));
+ } else {
+ // For non-Baker read barrier, we need to re-calculate the address of the string entry.
+ CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
+ arm_codegen->NewPcRelativeStringPatch(load->GetDexFile(), string_index);
+ arm_codegen->EmitMovwMovtPlaceholder(labels, out);
+ __ Str(r0, MemOperand(entry_address));
+ }
+
+ arm_codegen->Move32(locations->Out(), LocationFrom(r0));
+ RestoreLiveRegisters(codegen, locations);
+
+ __ B(GetExitLabel());
+ }
+
+ const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathARMVIXL"; }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathARMVIXL);
+};
+
class TypeCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
public:
TypeCheckSlowPathARMVIXL(HInstruction* instruction, bool is_fatal)
@@ -630,9 +687,30 @@
return mask;
}
-size_t CodeGeneratorARMVIXL::RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
- GetAssembler()->LoadSFromOffset(vixl32::SRegister(reg_id), sp, stack_index);
- return kArmWordSize;
+// Saves the register in the stack. Returns the size taken on stack.
+size_t CodeGeneratorARMVIXL::SaveCoreRegister(size_t stack_index ATTRIBUTE_UNUSED,
+ uint32_t reg_id ATTRIBUTE_UNUSED) {
+ TODO_VIXL32(FATAL);
+ return 0;
+}
+
+// Restores the register from the stack. Returns the size taken on stack.
+size_t CodeGeneratorARMVIXL::RestoreCoreRegister(size_t stack_index ATTRIBUTE_UNUSED,
+ uint32_t reg_id ATTRIBUTE_UNUSED) {
+ TODO_VIXL32(FATAL);
+ return 0;
+}
+
+size_t CodeGeneratorARMVIXL::SaveFloatingPointRegister(size_t stack_index ATTRIBUTE_UNUSED,
+ uint32_t reg_id ATTRIBUTE_UNUSED) {
+ TODO_VIXL32(FATAL);
+ return 0;
+}
+
+size_t CodeGeneratorARMVIXL::RestoreFloatingPointRegister(size_t stack_index ATTRIBUTE_UNUSED,
+ uint32_t reg_id ATTRIBUTE_UNUSED) {
+ TODO_VIXL32(FATAL);
+ return 0;
}
#undef __
@@ -655,7 +733,11 @@
instruction_visitor_(graph, this),
move_resolver_(graph->GetArena(), this),
assembler_(graph->GetArena()),
- isa_features_(isa_features) {
+ isa_features_(isa_features),
+ relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ pc_relative_string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ pc_relative_type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
// Always save the LR register to mimic Quick.
AddAllocatedRegister(Location::RegisterLocation(LR));
// Give d14 and d15 as scratch registers to VIXL.
@@ -793,7 +875,7 @@
__ Sub(temp, sp, static_cast<int32_t>(GetStackOverflowReservedBytes(kArm)));
// The load must immediately precede RecordPcInfo.
AssemblerAccurateScope aas(GetVIXLAssembler(),
- kArmInstrMaxSizeInBytes,
+ vixl32::kMaxInstructionSizeInBytes,
CodeBufferCheckScope::kMaximumSize);
__ ldr(temp, MemOperand(temp));
RecordPcInfo(nullptr, 0);
@@ -853,6 +935,116 @@
__ Bind(GetLabelOf(block));
}
+Location InvokeDexCallingConventionVisitorARMVIXL::GetNextLocation(Primitive::Type type) {
+ switch (type) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimNot: {
+ uint32_t index = gp_index_++;
+ uint32_t stack_index = stack_index_++;
+ if (index < calling_convention.GetNumberOfRegisters()) {
+ return LocationFrom(calling_convention.GetRegisterAt(index));
+ } else {
+ return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index));
+ }
+ }
+
+ case Primitive::kPrimLong: {
+ uint32_t index = gp_index_;
+ uint32_t stack_index = stack_index_;
+ gp_index_ += 2;
+ stack_index_ += 2;
+ if (index + 1 < calling_convention.GetNumberOfRegisters()) {
+ if (calling_convention.GetRegisterAt(index).Is(r1)) {
+ // Skip R1, and use R2_R3 instead.
+ gp_index_++;
+ index++;
+ }
+ }
+ if (index + 1 < calling_convention.GetNumberOfRegisters()) {
+ DCHECK_EQ(calling_convention.GetRegisterAt(index).GetCode() + 1,
+ calling_convention.GetRegisterAt(index + 1).GetCode());
+
+ return LocationFrom(calling_convention.GetRegisterAt(index),
+ calling_convention.GetRegisterAt(index + 1));
+ } else {
+ return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index));
+ }
+ }
+
+ case Primitive::kPrimFloat: {
+ uint32_t stack_index = stack_index_++;
+ if (float_index_ % 2 == 0) {
+ float_index_ = std::max(double_index_, float_index_);
+ }
+ if (float_index_ < calling_convention.GetNumberOfFpuRegisters()) {
+ return LocationFrom(calling_convention.GetFpuRegisterAt(float_index_++));
+ } else {
+ return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index));
+ }
+ }
+
+ case Primitive::kPrimDouble: {
+ double_index_ = std::max(double_index_, RoundUp(float_index_, 2));
+ uint32_t stack_index = stack_index_;
+ stack_index_ += 2;
+ if (double_index_ + 1 < calling_convention.GetNumberOfFpuRegisters()) {
+ uint32_t index = double_index_;
+ double_index_ += 2;
+ Location result = LocationFrom(
+ calling_convention.GetFpuRegisterAt(index),
+ calling_convention.GetFpuRegisterAt(index + 1));
+ DCHECK(ExpectedPairLayout(result));
+ return result;
+ } else {
+ return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index));
+ }
+ }
+
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unexpected parameter type " << type;
+ break;
+ }
+ return Location::NoLocation();
+}
+
+Location InvokeDexCallingConventionVisitorARMVIXL::GetReturnLocation(Primitive::Type type) const {
+ switch (type) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimNot: {
+ return LocationFrom(r0);
+ }
+
+ case Primitive::kPrimFloat: {
+ return LocationFrom(s0);
+ }
+
+ case Primitive::kPrimLong: {
+ return LocationFrom(r0, r1);
+ }
+
+ case Primitive::kPrimDouble: {
+ return LocationFrom(s0, s1);
+ }
+
+ case Primitive::kPrimVoid:
+ return Location::NoLocation();
+ }
+
+ UNREACHABLE();
+}
+
+Location InvokeDexCallingConventionVisitorARMVIXL::GetMethodLocation() const {
+ return LocationFrom(kMethodRegister);
+}
+
void CodeGeneratorARMVIXL::Move32(Location destination, Location source) {
if (source.Equals(destination)) {
return;
@@ -924,10 +1116,14 @@
uint32_t dex_pc,
SlowPathCode* slow_path) {
ValidateInvokeRuntime(entrypoint, instruction, slow_path);
- GenerateInvokeRuntime(GetThreadOffset<kArmPointerSize>(entrypoint).Int32Value());
+ __ Ldr(lr, MemOperand(tr, GetThreadOffset<kArmPointerSize>(entrypoint).Int32Value()));
+ // Ensure the pc position is recorded immediately after the `blx` instruction.
+ // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
+ AssemblerAccurateScope aas(GetVIXLAssembler(),
+ vixl32::k16BitT32InstructionSizeInBytes,
+ CodeBufferCheckScope::kExactSize);
+ __ blx(lr);
if (EntrypointRequiresStackMap(entrypoint)) {
- // TODO(VIXL): If necessary, use a scope to ensure we record the pc info immediately after the
- // previous instruction.
RecordPcInfo(instruction, dex_pc, slow_path);
}
}
@@ -936,11 +1132,7 @@
HInstruction* instruction,
SlowPathCode* slow_path) {
ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction, slow_path);
- GenerateInvokeRuntime(entry_point_offset);
-}
-
-void CodeGeneratorARMVIXL::GenerateInvokeRuntime(int32_t entry_point_offset) {
- GetAssembler()->LoadFromOffset(kLoadWord, lr, tr, entry_point_offset);
+ __ Ldr(lr, MemOperand(tr, entry_point_offset));
__ Blx(lr);
}
@@ -1270,6 +1462,19 @@
/* false_target */ nullptr);
}
+void LocationsBuilderARMVIXL::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
+ LocationSummary* locations = new (GetGraph()->GetArena())
+ LocationSummary(flag, LocationSummary::kNoCall);
+ locations->SetOut(Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
+ GetAssembler()->LoadFromOffset(kLoadWord,
+ OutputRegister(flag),
+ sp,
+ codegen_->GetStackOffsetOfShouldDeoptimizeFlag());
+}
+
void LocationsBuilderARMVIXL::VisitSelect(HSelect* select) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select);
if (Primitive::IsFloatingPointType(select->GetType())) {
@@ -1360,7 +1565,7 @@
CodeGenerator::GetInt32ValueOf(right.GetConstant()));
}
AssemblerAccurateScope aas(GetVIXLAssembler(),
- kArmInstrMaxSizeInBytes * 3u,
+ 3 * vixl32::kMaxInstructionSizeInBytes,
CodeBufferCheckScope::kMaximumSize);
__ ite(ARMCondition(cond->GetCondition()));
__ mov(ARMCondition(cond->GetCondition()), OutputRegister(cond), 1);
@@ -1575,7 +1780,10 @@
HandleInvoke(invoke);
- // TODO(VIXL): invoke->HasPcRelativeDexCache()
+ // For PC-relative dex cache the invoke has an extra input, the PC-relative address base.
+ if (invoke->HasPcRelativeDexCache()) {
+ invoke->GetLocations()->SetInAt(invoke->GetSpecialInputIndex(), Location::RequiresRegister());
+ }
}
static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorARMVIXL* codegen) {
@@ -1597,15 +1805,13 @@
}
LocationSummary* locations = invoke->GetLocations();
- DCHECK(locations->HasTemps());
- codegen_->GenerateStaticOrDirectCall(invoke, locations->GetTemp(0));
- // TODO(VIXL): If necessary, use a scope to ensure we record the pc info immediately after the
- // previous instruction.
+ codegen_->GenerateStaticOrDirectCall(
+ invoke, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation());
codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
}
void LocationsBuilderARMVIXL::HandleInvoke(HInvoke* invoke) {
- InvokeDexCallingConventionVisitorARM calling_convention_visitor;
+ InvokeDexCallingConventionVisitorARMVIXL calling_convention_visitor;
CodeGenerator::CreateCommonInvokeLocationSummary(invoke, &calling_convention_visitor);
}
@@ -1624,10 +1830,8 @@
}
codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0));
- DCHECK(!codegen_->IsLeafMethod());
- // TODO(VIXL): If necessary, use a scope to ensure we record the pc info immediately after the
- // previous instruction.
codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
+ DCHECK(!codegen_->IsLeafMethod());
}
void LocationsBuilderARMVIXL::VisitInvokeInterface(HInvokeInterface* invoke) {
@@ -1646,10 +1850,15 @@
DCHECK(!receiver.IsStackSlot());
- // /* HeapReference<Class> */ temp = receiver->klass_
- GetAssembler()->LoadFromOffset(kLoadWord, temp, RegisterFrom(receiver), class_offset);
-
- codegen_->MaybeRecordImplicitNullCheck(invoke);
+ // Ensure the pc position is recorded immediately after the `ldr` instruction.
+ {
+ AssemblerAccurateScope aas(GetVIXLAssembler(),
+ vixl32::kMaxInstructionSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
+ // /* HeapReference<Class> */ temp = receiver->klass_
+ __ ldr(temp, MemOperand(RegisterFrom(receiver), class_offset));
+ codegen_->MaybeRecordImplicitNullCheck(invoke);
+ }
// Instead of simply (possibly) unpoisoning `temp` here, we should
// emit a read barrier for the previous class reference load.
// However this is not required in practice, as this is an
@@ -1688,15 +1897,16 @@
temps.Exclude(hidden_reg);
__ Mov(hidden_reg, invoke->GetDexMethodIndex());
}
-
{
+ // Ensure the pc position is recorded immediately after the `blx` instruction.
+ // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
AssemblerAccurateScope aas(GetVIXLAssembler(),
- kArmInstrMaxSizeInBytes,
- CodeBufferCheckScope::kMaximumSize);
+ vixl32::k16BitT32InstructionSizeInBytes,
+ CodeBufferCheckScope::kExactSize);
// LR();
__ blx(lr);
- DCHECK(!codegen_->IsLeafMethod());
codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
+ DCHECK(!codegen_->IsLeafMethod());
}
}
@@ -3067,7 +3277,7 @@
__ Subs(temp, o_l, Operand::From(kArmBitsPerWord));
{
AssemblerAccurateScope guard(GetVIXLAssembler(),
- 3 * kArmInstrMaxSizeInBytes,
+ 2 * vixl32::kMaxInstructionSizeInBytes,
CodeBufferCheckScope::kMaximumSize);
__ it(pl);
__ lsl(pl, o_h, low, temp);
@@ -3086,7 +3296,7 @@
__ Subs(temp, o_h, Operand::From(kArmBitsPerWord));
{
AssemblerAccurateScope guard(GetVIXLAssembler(),
- 3 * kArmInstrMaxSizeInBytes,
+ 2 * vixl32::kMaxInstructionSizeInBytes,
CodeBufferCheckScope::kMaximumSize);
__ it(pl);
__ asr(pl, o_l, high, temp);
@@ -3103,7 +3313,7 @@
__ Subs(temp, o_h, Operand::From(kArmBitsPerWord));
{
AssemblerAccurateScope guard(GetVIXLAssembler(),
- 3 * kArmInstrMaxSizeInBytes,
+ 2 * vixl32::kMaxInstructionSizeInBytes,
CodeBufferCheckScope::kMaximumSize);
__ it(pl);
__ lsr(pl, o_l, high, temp);
@@ -3220,9 +3430,10 @@
MemberOffset code_offset = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize);
GetAssembler()->LoadFromOffset(kLoadWord, temp, tr, QUICK_ENTRY_POINT(pNewEmptyString));
GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, code_offset.Int32Value());
+ // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
AssemblerAccurateScope aas(GetVIXLAssembler(),
- kArmInstrMaxSizeInBytes,
- CodeBufferCheckScope::kMaximumSize);
+ vixl32::k16BitT32InstructionSizeInBytes,
+ CodeBufferCheckScope::kExactSize);
__ blx(lr);
codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
} else {
@@ -3462,10 +3673,16 @@
addr = temp;
}
__ Bind(&fail);
- // We need a load followed by store. (The address used in a STREX instruction must
- // be the same as the address in the most recently executed LDREX instruction.)
- __ Ldrexd(temp1, temp2, MemOperand(addr));
- codegen_->MaybeRecordImplicitNullCheck(instruction);
+ {
+ // Ensure the pc position is recorded immediately after the `ldrexd` instruction.
+ AssemblerAccurateScope aas(GetVIXLAssembler(),
+ vixl32::kMaxInstructionSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
+ // We need a load followed by store. (The address used in a STREX instruction must
+ // be the same as the address in the most recently executed LDREX instruction.)
+ __ ldrexd(temp1, temp2, MemOperand(addr));
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ }
__ Strexd(temp1, value_lo, value_hi, MemOperand(addr));
__ CompareAndBranchIfNonZero(temp1, &fail);
}
@@ -3614,6 +3831,11 @@
// Longs and doubles are handled in the switch.
if (field_type != Primitive::kPrimLong && field_type != Primitive::kPrimDouble) {
+ // TODO(VIXL): Here and for other calls to `MaybeRecordImplicitNullCheck` in this method, we
+ // should use a scope and the assembler to emit the store instruction to guarantee that we
+ // record the pc at the correct position. But the `Assembler` does not automatically handle
+ // unencodable offsets. Practically, everything is fine because the helper and VIXL, at the time
+ // of writing, do generate the store instruction last.
codegen_->MaybeRecordImplicitNullCheck(instruction);
}
@@ -3788,7 +4010,6 @@
TODO_VIXL32(FATAL);
} else {
GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(out), base, offset);
- // TODO(VIXL): Scope to guarantee the position immediately after the load.
codegen_->MaybeRecordImplicitNullCheck(instruction);
if (is_volatile) {
codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
@@ -3825,7 +4046,6 @@
__ Vmov(out_dreg, lo, hi);
} else {
GetAssembler()->LoadDFromOffset(out_dreg, base, offset);
- // TODO(VIXL): Scope to guarantee the position immediately after the load.
codegen_->MaybeRecordImplicitNullCheck(instruction);
}
break;
@@ -3841,6 +4061,11 @@
// double fields, are handled in the previous switch statement.
} else {
// Address cases other than reference and double that may require an implicit null check.
+ // TODO(VIXL): Here and for other calls to `MaybeRecordImplicitNullCheck` in this method, we
+ // should use a scope and the assembler to emit the load instruction to guarantee that we
+ // record the pc at the correct position. But the `Assembler` does not automatically handle
+ // unencodable offsets. Practically, everything is fine because the helper and VIXL, at the time
+ // of writing, do generate the store instruction last.
codegen_->MaybeRecordImplicitNullCheck(instruction);
}
@@ -3965,8 +4190,9 @@
}
UseScratchRegisterScope temps(GetVIXLAssembler());
+ // Ensure the pc position is recorded immediately after the `ldr` instruction.
AssemblerAccurateScope aas(GetVIXLAssembler(),
- kArmInstrMaxSizeInBytes,
+ vixl32::kMaxInstructionSizeInBytes,
CodeBufferCheckScope::kMaximumSize);
__ ldr(temps.Acquire(), MemOperand(InputRegisterAt(instruction, 0)));
RecordPcInfo(instruction, instruction->GetDexPc());
@@ -4233,6 +4459,11 @@
size_t offset =
(index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
GetAssembler()->LoadFromOffset(kLoadWord, out, obj, offset);
+ // TODO(VIXL): Here and for other calls to `MaybeRecordImplicitNullCheck` in this method,
+ // we should use a scope and the assembler to emit the load instruction to guarantee that
+ // we record the pc at the correct position. But the `Assembler` does not automatically
+ // handle unencodable offsets. Practically, everything is fine because the helper and
+ // VIXL, at the time of writing, do generate the store instruction last.
codegen_->MaybeRecordImplicitNullCheck(instruction);
// If read barriers are enabled, emit read barriers other than
// Baker's using a slow path (and also unpoison the loaded
@@ -4255,7 +4486,9 @@
}
codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, RegisterFrom(index));
temps.Release(temp);
-
+ // 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.
codegen_->MaybeRecordImplicitNullCheck(instruction);
// If read barriers are enabled, emit read barriers other than
// Baker's using a slow path (and also unpoison the loaded
@@ -4317,6 +4550,8 @@
// Potential implicit null checks, in the case of reference
// arrays, are handled in the previous switch statement.
} else if (!maybe_compressed_char_at) {
+ // TODO(VIXL): Use a scope to ensure we record the pc info immediately after
+ // the preceding load instruction.
codegen_->MaybeRecordImplicitNullCheck(instruction);
}
}
@@ -4417,6 +4652,8 @@
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.
codegen_->MaybeRecordImplicitNullCheck(instruction);
DCHECK(!needs_write_barrier);
DCHECK(!may_need_runtime_call_for_type_check);
@@ -4451,6 +4688,8 @@
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.
codegen_->MaybeRecordImplicitNullCheck(instruction);
__ B(&done);
__ Bind(&non_zero);
@@ -4464,9 +4703,15 @@
// negative, in which case we would take the ArraySet slow
// path.
- // /* HeapReference<Class> */ temp1 = array->klass_
- GetAssembler()->LoadFromOffset(kLoadWord, temp1, array, class_offset);
- codegen_->MaybeRecordImplicitNullCheck(instruction);
+ {
+ // Ensure we record the pc position immediately after the `ldr` instruction.
+ AssemblerAccurateScope aas(GetVIXLAssembler(),
+ vixl32::kMaxInstructionSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
+ // /* HeapReference<Class> */ temp1 = array->klass_
+ __ ldr(temp1, MemOperand(array, class_offset));
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ }
GetAssembler()->MaybeUnpoisonHeapReference(temp1);
// /* HeapReference<Class> */ temp1 = temp1->component_type_
@@ -4523,6 +4768,8 @@
}
if (!may_need_runtime_call_for_type_check) {
+ // TODO(VIXL): Ensure we record the pc position immediately after the preceding store
+ // instruction.
codegen_->MaybeRecordImplicitNullCheck(instruction);
}
@@ -4591,6 +4838,8 @@
// Objects are handled in the switch.
if (value_type != Primitive::kPrimNot) {
+ // TODO(VIXL): Ensure we record the pc position immediately after the preceding store
+ // instruction.
codegen_->MaybeRecordImplicitNullCheck(instruction);
}
}
@@ -4606,8 +4855,13 @@
uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction);
vixl32::Register obj = InputRegisterAt(instruction, 0);
vixl32::Register out = OutputRegister(instruction);
- GetAssembler()->LoadFromOffset(kLoadWord, out, obj, offset);
- codegen_->MaybeRecordImplicitNullCheck(instruction);
+ {
+ AssemblerAccurateScope aas(GetVIXLAssembler(),
+ vixl32::kMaxInstructionSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
+ __ ldr(out, MemOperand(obj, offset));
+ codegen_->MaybeRecordImplicitNullCheck(instruction);
+ }
// Mask out compression flag from String's array length.
if (mirror::kUseStringCompression && instruction->IsStringLength()) {
__ Lsr(out, out, 1u);
@@ -4985,12 +5239,37 @@
TODO_VIXL32(FATAL);
}
-// Check if the desired_class_load_kind is supported. If it is, return it,
-// otherwise return a fall-back kind that should be used instead.
HLoadClass::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadClassKind(
- HLoadClass::LoadKind desired_class_load_kind ATTRIBUTE_UNUSED) {
- // TODO(VIXL): Implement optimized code paths.
- return HLoadClass::LoadKind::kDexCacheViaMethod;
+ HLoadClass::LoadKind desired_class_load_kind) {
+ switch (desired_class_load_kind) {
+ case HLoadClass::LoadKind::kReferrersClass:
+ break;
+ case HLoadClass::LoadKind::kBootImageLinkTimeAddress:
+ // TODO(VIXL): Enable it back when literal pools are fixed in VIXL.
+ return HLoadClass::LoadKind::kDexCacheViaMethod;
+ case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
+ DCHECK(GetCompilerOptions().GetCompilePic());
+ break;
+ case HLoadClass::LoadKind::kBootImageAddress:
+ // TODO(VIXL): Enable it back when literal pools are fixed in VIXL.
+ return HLoadClass::LoadKind::kDexCacheViaMethod;
+ case HLoadClass::LoadKind::kDexCacheAddress:
+ // TODO(VIXL): Enable it back when literal pools are fixed in VIXL.
+ return HLoadClass::LoadKind::kDexCacheViaMethod;
+ case HLoadClass::LoadKind::kDexCachePcRelative:
+ DCHECK(!Runtime::Current()->UseJitCompilation());
+ // We disable pc-relative load when there is an irreducible loop, as the optimization
+ // is incompatible with it.
+ // TODO: Create as many ArmDexCacheArraysBase instructions as needed for methods
+ // with irreducible loops.
+ if (GetGraph()->HasIrreducibleLoops()) {
+ return HLoadClass::LoadKind::kDexCacheViaMethod;
+ }
+ break;
+ case HLoadClass::LoadKind::kDexCacheViaMethod:
+ break;
+ }
+ return desired_class_load_kind;
}
void LocationsBuilderARMVIXL::VisitLoadClass(HLoadClass* cls) {
@@ -5004,11 +5283,15 @@
return;
}
- // TODO(VIXL): read barrier code.
- LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || kEmitCompilerReadBarrier)
+ const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
+ LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier)
? LocationSummary::kCallOnSlowPath
: LocationSummary::kNoCall;
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
+ if (kUseBakerReadBarrier && requires_read_barrier && !cls->NeedsEnvironment()) {
+ TODO_VIXL32(FATAL);
+ }
+
HLoadClass::LoadKind load_kind = cls->GetLoadKind();
if (load_kind == HLoadClass::LoadKind::kReferrersClass ||
load_kind == HLoadClass::LoadKind::kDexCacheViaMethod ||
@@ -5030,7 +5313,9 @@
Location out_loc = locations->Out();
vixl32::Register out = OutputRegister(cls);
- // TODO(VIXL): read barrier code.
+ const ReadBarrierOption read_barrier_option = cls->IsInBootImage()
+ ? kWithoutReadBarrier
+ : kCompilerReadBarrierOption;
bool generate_null_check = false;
switch (cls->GetLoadKind()) {
case HLoadClass::LoadKind::kReferrersClass: {
@@ -5042,7 +5327,35 @@
out_loc,
current_method,
ArtMethod::DeclaringClassOffset().Int32Value(),
- kEmitCompilerReadBarrier);
+ read_barrier_option);
+ break;
+ }
+ case HLoadClass::LoadKind::kBootImageLinkTimeAddress: {
+ TODO_VIXL32(FATAL);
+ break;
+ }
+ case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: {
+ DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
+ CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
+ codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex());
+ codegen_->EmitMovwMovtPlaceholder(labels, out);
+ break;
+ }
+ case HLoadClass::LoadKind::kBootImageAddress: {
+ TODO_VIXL32(FATAL);
+ break;
+ }
+ case HLoadClass::LoadKind::kDexCacheAddress: {
+ TODO_VIXL32(FATAL);
+ break;
+ }
+ case HLoadClass::LoadKind::kDexCachePcRelative: {
+ vixl32::Register base_reg = InputRegisterAt(cls, 0);
+ HArmDexCacheArraysBase* base = cls->InputAt(0)->AsArmDexCacheArraysBase();
+ int32_t offset = cls->GetDexCacheElementOffset() - base->GetElementOffset();
+ // /* GcRoot<mirror::Class> */ out = *(dex_cache_arrays_base + offset)
+ GenerateGcRootFieldLoad(cls, out_loc, base_reg, offset, read_barrier_option);
+ generate_null_check = !cls->IsInDexCache();
break;
}
case HLoadClass::LoadKind::kDexCacheViaMethod: {
@@ -5054,7 +5367,7 @@
GetAssembler()->LoadFromOffset(kLoadWord, out, current_method, resolved_types_offset);
// /* GcRoot<mirror::Class> */ out = out[type_index]
size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_);
- GenerateGcRootFieldLoad(cls, out_loc, out, offset, kEmitCompilerReadBarrier);
+ GenerateGcRootFieldLoad(cls, out_loc, out, offset, read_barrier_option);
generate_null_check = !cls->IsInDexCache();
break;
}
@@ -5114,37 +5427,101 @@
__ Bind(slow_path->GetExitLabel());
}
-// Check if the desired_string_load_kind is supported. If it is, return it,
-// otherwise return a fall-back kind that should be used instead.
HLoadString::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadStringKind(
- HLoadString::LoadKind desired_string_load_kind ATTRIBUTE_UNUSED) {
- // TODO(VIXL): Implement optimized code paths. For now we always use the simpler fallback code.
- return HLoadString::LoadKind::kDexCacheViaMethod;
+ HLoadString::LoadKind desired_string_load_kind) {
+ switch (desired_string_load_kind) {
+ case HLoadString::LoadKind::kBootImageLinkTimeAddress:
+ // TODO(VIXL): Implement missing optimization.
+ return HLoadString::LoadKind::kDexCacheViaMethod;
+ case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
+ DCHECK(GetCompilerOptions().GetCompilePic());
+ break;
+ case HLoadString::LoadKind::kBootImageAddress:
+ // TODO(VIXL): Implement missing optimization.
+ return HLoadString::LoadKind::kDexCacheViaMethod;
+ case HLoadString::LoadKind::kBssEntry:
+ DCHECK(!Runtime::Current()->UseJitCompilation());
+ break;
+ case HLoadString::LoadKind::kJitTableAddress:
+ DCHECK(Runtime::Current()->UseJitCompilation());
+ // TODO(VIXL): Implement missing optimization.
+ return HLoadString::LoadKind::kDexCacheViaMethod;
+ case HLoadString::LoadKind::kDexCacheViaMethod:
+ break;
+ }
+ return desired_string_load_kind;
}
void LocationsBuilderARMVIXL::VisitLoadString(HLoadString* load) {
- LocationSummary::CallKind call_kind = load->NeedsEnvironment()
- ? LocationSummary::kCallOnMainOnly
- : LocationSummary::kNoCall;
+ LocationSummary::CallKind call_kind = CodeGenerator::GetLoadStringCallKind(load);
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
-
- // TODO(VIXL): Implement optimized code paths.
- // See InstructionCodeGeneratorARMVIXL::VisitLoadString.
HLoadString::LoadKind load_kind = load->GetLoadKind();
if (load_kind == HLoadString::LoadKind::kDexCacheViaMethod) {
- locations->SetInAt(0, Location::RequiresRegister());
- // TODO(VIXL): Use InvokeRuntimeCallingConventionARMVIXL instead.
locations->SetOut(LocationFrom(r0));
} else {
locations->SetOut(Location::RequiresRegister());
+ if (load_kind == HLoadString::LoadKind::kBssEntry) {
+ if (!kUseReadBarrier || kUseBakerReadBarrier) {
+ // Rely on the pResolveString and/or marking to save everything, including temps.
+ // Note that IP may theoretically be clobbered by saving/restoring the live register
+ // (only one thanks to the custom calling convention), so we request a different temp.
+ locations->AddTemp(Location::RequiresRegister());
+ RegisterSet caller_saves = RegisterSet::Empty();
+ InvokeRuntimeCallingConventionARMVIXL calling_convention;
+ caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0)));
+ // TODO: Add GetReturnLocation() to the calling convention so that we can DCHECK()
+ // that the the kPrimNot result register is the same as the first argument register.
+ locations->SetCustomSlowPathCallerSaves(caller_saves);
+ } else {
+ // For non-Baker read barrier we have a temp-clobbering call.
+ }
+ }
}
}
void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) {
- // TODO(VIXL): Implement optimized code paths.
- // We implemented the simplest solution to get first ART tests passing, we deferred the
- // optimized path until later, we should implement it using ARM64 implementation as a
- // reference. The same related to LocationsBuilderARMVIXL::VisitLoadString.
+ LocationSummary* locations = load->GetLocations();
+ Location out_loc = locations->Out();
+ vixl32::Register out = OutputRegister(load);
+ HLoadString::LoadKind load_kind = load->GetLoadKind();
+
+ switch (load_kind) {
+ case HLoadString::LoadKind::kBootImageLinkTimeAddress: {
+ TODO_VIXL32(FATAL);
+ break;
+ }
+ case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
+ DCHECK(codegen_->GetCompilerOptions().IsBootImage());
+ CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
+ codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex().index_);
+ codegen_->EmitMovwMovtPlaceholder(labels, out);
+ return; // No dex cache slow path.
+ }
+ case HLoadString::LoadKind::kBootImageAddress: {
+ TODO_VIXL32(FATAL);
+ break;
+ }
+ case HLoadString::LoadKind::kBssEntry: {
+ DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
+ vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
+ CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
+ codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex().index_);
+ codegen_->EmitMovwMovtPlaceholder(labels, temp);
+ GenerateGcRootFieldLoad(load, out_loc, temp, /* offset */ 0, kCompilerReadBarrierOption);
+ LoadStringSlowPathARMVIXL* slow_path =
+ new (GetGraph()->GetArena()) LoadStringSlowPathARMVIXL(load);
+ codegen_->AddSlowPath(slow_path);
+ __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
+ __ Bind(slow_path->GetExitLabel());
+ return;
+ }
+ case HLoadString::LoadKind::kJitTableAddress: {
+ TODO_VIXL32(FATAL);
+ break;
+ }
+ default:
+ break;
+ }
// TODO: Re-add the compiler code to do string dex cache lookup again.
DCHECK_EQ(load->GetLoadKind(), HLoadString::LoadKind::kDexCacheViaMethod);
@@ -5999,9 +6376,9 @@
Location root,
vixl32::Register obj,
uint32_t offset,
- bool requires_read_barrier) {
+ ReadBarrierOption read_barrier_option) {
vixl32::Register root_reg = RegisterFrom(root);
- if (requires_read_barrier) {
+ if (read_barrier_option == kWithReadBarrier) {
TODO_VIXL32(FATAL);
} else {
// Plain GC root load with no read barrier.
@@ -6062,15 +6439,51 @@
// 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(
- const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info ATTRIBUTE_UNUSED,
- HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) {
+ const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
+ HInvokeStaticOrDirect* invoke) {
// TODO(VIXL): Implement optimized code paths.
- return {
- HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod,
- HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
- 0u,
- 0u
- };
+ if (desired_dispatch_info.method_load_kind ==
+ HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup ||
+ desired_dispatch_info.code_ptr_location ==
+ HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup) {
+ return {
+ HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod,
+ HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
+ 0u,
+ 0u
+ };
+ }
+
+ HInvokeStaticOrDirect::DispatchInfo dispatch_info = desired_dispatch_info;
+ // We disable pc-relative load when there is an irreducible loop, as the optimization
+ // is incompatible with it.
+ // TODO: Create as many ArmDexCacheArraysBase instructions as needed for methods
+ // with irreducible loops.
+ if (GetGraph()->HasIrreducibleLoops() &&
+ (dispatch_info.method_load_kind ==
+ HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative)) {
+ dispatch_info.method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod;
+ }
+
+ if (dispatch_info.code_ptr_location == HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative) {
+ const DexFile& outer_dex_file = GetGraph()->GetDexFile();
+ if (&outer_dex_file != invoke->GetTargetMethod().dex_file) {
+ // Calls across dex files are more likely to exceed the available BL range,
+ // so use absolute patch with fixup if available and kCallArtMethod otherwise.
+ HInvokeStaticOrDirect::CodePtrLocation code_ptr_location =
+ (desired_dispatch_info.method_load_kind ==
+ HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup)
+ ? HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup
+ : HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod;
+ return HInvokeStaticOrDirect::DispatchInfo {
+ dispatch_info.method_load_kind,
+ code_ptr_location,
+ dispatch_info.method_load_data,
+ 0u
+ };
+ }
+ }
+ return dispatch_info;
}
vixl32::Register CodeGeneratorARMVIXL::GetInvokeStaticOrDirectExtraParameter(
@@ -6101,59 +6514,119 @@
void CodeGeneratorARMVIXL::GenerateStaticOrDirectCall(
HInvokeStaticOrDirect* invoke, Location temp) {
- Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp.
- vixl32::Register temp_reg = RegisterFrom(temp);
+ // For better instruction scheduling we load the direct code pointer before the method pointer.
+ switch (invoke->GetCodePtrLocation()) {
+ case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
+ // LR = code address from literal pool with link-time patch.
+ TODO_VIXL32(FATAL);
+ break;
+ case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
+ // LR = invoke->GetDirectCodePtr();
+ __ Mov(lr, Operand::From(invoke->GetDirectCodePtr()));
+ break;
+ default:
+ break;
+ }
+ Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp.
switch (invoke->GetMethodLoadKind()) {
case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: {
uint32_t offset =
GetThreadOffset<kArmPointerSize>(invoke->GetStringInitEntryPoint()).Int32Value();
// temp = thread->string_init_entrypoint
- GetAssembler()->LoadFromOffset(kLoadWord, temp_reg, tr, offset);
+ GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(temp), tr, offset);
+ break;
+ }
+ case HInvokeStaticOrDirect::MethodLoadKind::kRecursive:
+ callee_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
+ break;
+ case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress:
+ __ Mov(RegisterFrom(temp), Operand::From(invoke->GetMethodAddress()));
+ break;
+ case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup:
+ TODO_VIXL32(FATAL);
+ break;
+ case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative: {
+ HArmDexCacheArraysBase* base =
+ invoke->InputAt(invoke->GetSpecialInputIndex())->AsArmDexCacheArraysBase();
+ vixl32::Register base_reg = GetInvokeStaticOrDirectExtraParameter(invoke, RegisterFrom(temp));
+ int32_t offset = invoke->GetDexCacheArrayOffset() - base->GetElementOffset();
+ GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(temp), base_reg, offset);
break;
}
case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: {
Location current_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
vixl32::Register method_reg;
+ vixl32::Register reg = RegisterFrom(temp);
if (current_method.IsRegister()) {
method_reg = RegisterFrom(current_method);
} else {
DCHECK(invoke->GetLocations()->Intrinsified());
DCHECK(!current_method.IsValid());
- method_reg = temp_reg;
- GetAssembler()->LoadFromOffset(kLoadWord, temp_reg, sp, kCurrentMethodStackOffset);
+ method_reg = reg;
+ GetAssembler()->LoadFromOffset(kLoadWord, reg, sp, kCurrentMethodStackOffset);
}
// /* ArtMethod*[] */ temp = temp.ptr_sized_fields_->dex_cache_resolved_methods_;
GetAssembler()->LoadFromOffset(
kLoadWord,
- temp_reg,
+ reg,
method_reg,
ArtMethod::DexCacheResolvedMethodsOffset(kArmPointerSize).Int32Value());
// temp = temp[index_in_cache];
// Note: Don't use invoke->GetTargetMethod() as it may point to a different dex file.
uint32_t index_in_cache = invoke->GetDexMethodIndex();
GetAssembler()->LoadFromOffset(
- kLoadWord, temp_reg, temp_reg, CodeGenerator::GetCachePointerOffset(index_in_cache));
+ kLoadWord, reg, reg, CodeGenerator::GetCachePointerOffset(index_in_cache));
break;
}
- default:
- TODO_VIXL32(FATAL);
}
- // TODO(VIXL): Support `CodePtrLocation` values other than `kCallArtMethod`.
- if (invoke->GetCodePtrLocation() != HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod) {
- TODO_VIXL32(FATAL);
+ switch (invoke->GetCodePtrLocation()) {
+ case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf:
+ __ Bl(GetFrameEntryLabel());
+ break;
+ case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative:
+ relative_call_patches_.emplace_back(*invoke->GetTargetMethod().dex_file,
+ invoke->GetTargetMethod().dex_method_index);
+ {
+ AssemblerAccurateScope aas(GetVIXLAssembler(),
+ vixl32::kMaxInstructionSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
+ __ bind(&relative_call_patches_.back().label);
+ // Arbitrarily branch to the BL itself, override at link time.
+ __ bl(&relative_call_patches_.back().label);
+ }
+ break;
+ case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
+ case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
+ // LR prepared above for better instruction scheduling.
+ // LR()
+ {
+ // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
+ AssemblerAccurateScope aas(GetVIXLAssembler(),
+ vixl32::k16BitT32InstructionSizeInBytes,
+ CodeBufferCheckScope::kExactSize);
+ __ blx(lr);
+ }
+ break;
+ case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod:
+ // LR = callee_method->entry_point_from_quick_compiled_code_
+ GetAssembler()->LoadFromOffset(
+ kLoadWord,
+ lr,
+ RegisterFrom(callee_method),
+ ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value());
+ {
+ // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
+ AssemblerAccurateScope aas(GetVIXLAssembler(),
+ vixl32::k16BitT32InstructionSizeInBytes,
+ CodeBufferCheckScope::kExactSize);
+ // LR()
+ __ blx(lr);
+ }
+ break;
}
- // LR = callee_method->entry_point_from_quick_compiled_code_
- GetAssembler()->LoadFromOffset(
- kLoadWord,
- lr,
- RegisterFrom(callee_method),
- ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value());
- // LR()
- __ Blx(lr);
-
DCHECK(!IsLeafMethod());
}
@@ -6169,9 +6642,15 @@
InvokeDexCallingConventionARMVIXL calling_convention;
vixl32::Register receiver = calling_convention.GetRegisterAt(0);
uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
- // /* HeapReference<Class> */ temp = receiver->klass_
- GetAssembler()->LoadFromOffset(kLoadWord, temp, receiver, class_offset);
- MaybeRecordImplicitNullCheck(invoke);
+ {
+ // Make sure the pc is recorded immediately after the `ldr` instruction.
+ AssemblerAccurateScope aas(GetVIXLAssembler(),
+ vixl32::kMaxInstructionSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
+ // /* HeapReference<Class> */ temp = receiver->klass_
+ __ ldr(temp, MemOperand(receiver, class_offset));
+ MaybeRecordImplicitNullCheck(invoke);
+ }
// Instead of simply (possibly) unpoisoning `temp` here, we should
// emit a read barrier for the previous class reference load.
// However this is not required in practice, as this is an
@@ -6188,7 +6667,81 @@
// LR = temp->GetEntryPoint();
GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, entry_point);
// LR();
- __ Blx(lr);
+ // This `blx` *must* be the *last* instruction generated by this stub, so that calls to
+ // `RecordPcInfo()` immediately following record the correct pc. Use a scope to help guarantee
+ // that.
+ // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
+ AssemblerAccurateScope aas(GetVIXLAssembler(),
+ vixl32::k16BitT32InstructionSizeInBytes,
+ CodeBufferCheckScope::kExactSize);
+ __ blx(lr);
+}
+
+CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewPcRelativeStringPatch(
+ const DexFile& dex_file, uint32_t string_index) {
+ return NewPcRelativePatch(dex_file, string_index, &pc_relative_string_patches_);
+}
+
+CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewPcRelativeTypePatch(
+ const DexFile& dex_file, dex::TypeIndex type_index) {
+ return NewPcRelativePatch(dex_file, type_index.index_, &pc_relative_type_patches_);
+}
+
+CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewPcRelativeDexCacheArrayPatch(
+ const DexFile& dex_file, uint32_t element_offset) {
+ return NewPcRelativePatch(dex_file, element_offset, &pc_relative_dex_cache_patches_);
+}
+
+CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewPcRelativePatch(
+ const DexFile& dex_file, uint32_t offset_or_index, ArenaDeque<PcRelativePatchInfo>* patches) {
+ patches->emplace_back(dex_file, offset_or_index);
+ return &patches->back();
+}
+
+template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)>
+inline void CodeGeneratorARMVIXL::EmitPcRelativeLinkerPatches(
+ const ArenaDeque<PcRelativePatchInfo>& infos,
+ ArenaVector<LinkerPatch>* linker_patches) {
+ for (const PcRelativePatchInfo& info : infos) {
+ const DexFile& dex_file = info.target_dex_file;
+ size_t offset_or_index = info.offset_or_index;
+ DCHECK(info.add_pc_label.IsBound());
+ uint32_t add_pc_offset = dchecked_integral_cast<uint32_t>(info.add_pc_label.GetLocation());
+ // Add MOVW patch.
+ DCHECK(info.movw_label.IsBound());
+ uint32_t movw_offset = dchecked_integral_cast<uint32_t>(info.movw_label.GetLocation());
+ linker_patches->push_back(Factory(movw_offset, &dex_file, add_pc_offset, offset_or_index));
+ // Add MOVT patch.
+ DCHECK(info.movt_label.IsBound());
+ uint32_t movt_offset = dchecked_integral_cast<uint32_t>(info.movt_label.GetLocation());
+ linker_patches->push_back(Factory(movt_offset, &dex_file, add_pc_offset, offset_or_index));
+ }
+}
+
+void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) {
+ DCHECK(linker_patches->empty());
+ size_t size =
+ relative_call_patches_.size() +
+ /* MOVW+MOVT for each entry */ 2u * pc_relative_dex_cache_patches_.size() +
+ /* MOVW+MOVT for each entry */ 2u * pc_relative_string_patches_.size() +
+ /* MOVW+MOVT for each entry */ 2u * pc_relative_type_patches_.size();
+ linker_patches->reserve(size);
+ for (const PatchInfo<vixl32::Label>& info : relative_call_patches_) {
+ uint32_t literal_offset = info.label.GetLocation();
+ linker_patches->push_back(
+ LinkerPatch::RelativeCodePatch(literal_offset, &info.dex_file, info.index));
+ }
+ EmitPcRelativeLinkerPatches<LinkerPatch::DexCacheArrayPatch>(pc_relative_dex_cache_patches_,
+ linker_patches);
+ if (!GetCompilerOptions().IsBootImage()) {
+ EmitPcRelativeLinkerPatches<LinkerPatch::StringBssEntryPatch>(pc_relative_string_patches_,
+ linker_patches);
+ } else {
+ EmitPcRelativeLinkerPatches<LinkerPatch::RelativeStringPatch>(pc_relative_string_patches_,
+ linker_patches);
+ }
+ EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(pc_relative_type_patches_,
+ linker_patches);
}
void LocationsBuilderARMVIXL::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
@@ -6315,6 +6868,17 @@
jump_table->EmitTable(codegen_);
}
}
+void LocationsBuilderARMVIXL::VisitArmDexCacheArraysBase(HArmDexCacheArraysBase* base) {
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(base);
+ locations->SetOut(Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitArmDexCacheArraysBase(HArmDexCacheArraysBase* base) {
+ vixl32::Register base_reg = OutputRegister(base);
+ CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
+ codegen_->NewPcRelativeDexCacheArrayPatch(base->GetDexFile(), base->GetElementOffset());
+ codegen_->EmitMovwMovtPlaceholder(labels, base_reg);
+}
// Copy the result of a call into the given target.
void CodeGeneratorARMVIXL::MoveFromReturnRegister(Location trg, Primitive::Type type) {
@@ -6325,7 +6889,7 @@
DCHECK_NE(type, Primitive::kPrimVoid);
- Location return_loc = InvokeDexCallingConventionVisitorARM().GetReturnLocation(type);
+ Location return_loc = InvokeDexCallingConventionVisitorARMVIXL().GetReturnLocation(type);
if (return_loc.Equals(trg)) {
return;
}
@@ -6373,6 +6937,21 @@
}
}
+void CodeGeneratorARMVIXL::EmitMovwMovtPlaceholder(
+ CodeGeneratorARMVIXL::PcRelativePatchInfo* labels,
+ vixl32::Register out) {
+ AssemblerAccurateScope aas(GetVIXLAssembler(),
+ 3 * vixl32::kMaxInstructionSizeInBytes,
+ CodeBufferCheckScope::kMaximumSize);
+ // TODO(VIXL): Think about using mov instead of movw.
+ __ bind(&labels->movw_label);
+ __ movw(out, /* placeholder */ 0u);
+ __ bind(&labels->movt_label);
+ __ movt(out, /* placeholder */ 0u);
+ __ bind(&labels->add_pc_label);
+ __ add(out, out, pc);
+}
+
#undef __
#undef QUICK_ENTRY_POINT
#undef TODO_VIXL32
diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h
index bd91127..b7ba8dd 100644
--- a/compiler/optimizing/code_generator_arm_vixl.h
+++ b/compiler/optimizing/code_generator_arm_vixl.h
@@ -17,9 +17,15 @@
#ifndef ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM_VIXL_H_
#define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM_VIXL_H_
-#include "code_generator_arm.h"
+#include "base/enums.h"
+#include "code_generator.h"
#include "common_arm.h"
+#include "driver/compiler_options.h"
+#include "nodes.h"
+#include "string_reference.h"
+#include "parallel_move_resolver.h"
#include "utils/arm/assembler_arm_vixl.h"
+#include "utils/type_reference.h"
// TODO(VIXL): make vixl clean wrt -Wshadow.
#pragma GCC diagnostic push
@@ -44,7 +50,7 @@
vixl::aarch32::r2,
vixl::aarch32::r3
};
-static const size_t kParameterCoreRegistersLengthVIXL = arraysize(kParameterCoreRegisters);
+static const size_t kParameterCoreRegistersLengthVIXL = arraysize(kParameterCoreRegistersVIXL);
static const vixl::aarch32::SRegister kParameterFpuRegistersVIXL[] = {
vixl::aarch32::s0,
vixl::aarch32::s1,
@@ -63,7 +69,7 @@
vixl::aarch32::s14,
vixl::aarch32::s15
};
-static const size_t kParameterFpuRegistersLengthVIXL = arraysize(kParameterFpuRegisters);
+static const size_t kParameterFpuRegistersLengthVIXL = arraysize(kParameterFpuRegistersVIXL);
static const vixl::aarch32::Register kMethodRegister = vixl::aarch32::r0;
@@ -90,7 +96,7 @@
vixl::aarch32::r3
};
static const size_t kRuntimeParameterCoreRegistersLengthVIXL =
- arraysize(kRuntimeParameterCoreRegisters);
+ arraysize(kRuntimeParameterCoreRegistersVIXL);
static const vixl::aarch32::SRegister kRuntimeParameterFpuRegistersVIXL[] = {
vixl::aarch32::s0,
vixl::aarch32::s1,
@@ -98,98 +104,10 @@
vixl::aarch32::s3
};
static const size_t kRuntimeParameterFpuRegistersLengthVIXL =
- arraysize(kRuntimeParameterFpuRegisters);
+ arraysize(kRuntimeParameterFpuRegistersVIXL);
class LoadClassSlowPathARMVIXL;
-#define FOR_EACH_IMPLEMENTED_INSTRUCTION(M) \
- M(Above) \
- M(AboveOrEqual) \
- M(Add) \
- M(And) \
- M(ArrayGet) \
- M(ArrayLength) \
- M(ArraySet) \
- M(Below) \
- M(BelowOrEqual) \
- M(BitwiseNegatedRight) \
- M(BooleanNot) \
- M(BoundsCheck) \
- M(BoundType) \
- M(CheckCast) \
- M(ClassTableGet) \
- M(ClearException) \
- M(ClinitCheck) \
- M(Compare) \
- M(CurrentMethod) \
- M(Deoptimize) \
- M(Div) \
- M(DivZeroCheck) \
- M(DoubleConstant) \
- M(Equal) \
- M(Exit) \
- M(FloatConstant) \
- M(Goto) \
- M(GreaterThan) \
- M(GreaterThanOrEqual) \
- M(If) \
- M(InstanceFieldGet) \
- M(InstanceFieldSet) \
- M(InstanceOf) \
- M(IntConstant) \
- M(IntermediateAddress) \
- M(InvokeInterface) \
- M(InvokeStaticOrDirect) \
- M(InvokeUnresolved) \
- M(InvokeVirtual) \
- M(LessThan) \
- M(LessThanOrEqual) \
- M(LoadClass) \
- M(LoadException) \
- M(LoadString) \
- M(LongConstant) \
- M(MemoryBarrier) \
- M(MonitorOperation) \
- M(Mul) \
- M(MultiplyAccumulate) \
- M(NativeDebugInfo) \
- M(Neg) \
- M(NewArray) \
- M(NewInstance) \
- M(Not) \
- M(NotEqual) \
- M(NullCheck) \
- M(NullConstant) \
- M(Or) \
- M(PackedSwitch) \
- M(ParallelMove) \
- M(ParameterValue) \
- M(Phi) \
- M(Rem) \
- M(Return) \
- M(ReturnVoid) \
- M(Ror) \
- M(Select) \
- M(Shl) \
- M(Shr) \
- M(StaticFieldGet) \
- M(StaticFieldSet) \
- M(Sub) \
- M(SuspendCheck) \
- M(Throw) \
- M(TryBoundary) \
- M(TypeConversion) \
- M(UnresolvedInstanceFieldGet) \
- M(UnresolvedInstanceFieldSet) \
- M(UnresolvedStaticFieldGet) \
- M(UnresolvedStaticFieldSet) \
- M(UShr) \
- M(Xor) \
-
-// TODO: Remove once the VIXL32 backend is implemented completely.
-#define FOR_EACH_UNIMPLEMENTED_INSTRUCTION(M) \
- M(ArmDexCacheArraysBase) \
-
class CodeGeneratorARMVIXL;
class JumpTableARMVIXL : public DeletableArenaObject<kArenaAllocSwitchTable> {
@@ -248,6 +166,22 @@
DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConventionARMVIXL);
};
+class InvokeDexCallingConventionVisitorARMVIXL : public InvokeDexCallingConventionVisitor {
+ public:
+ InvokeDexCallingConventionVisitorARMVIXL() {}
+ virtual ~InvokeDexCallingConventionVisitorARMVIXL() {}
+
+ Location GetNextLocation(Primitive::Type type) OVERRIDE;
+ Location GetReturnLocation(Primitive::Type type) const OVERRIDE;
+ Location GetMethodLocation() const OVERRIDE;
+
+ private:
+ InvokeDexCallingConventionARMVIXL calling_convention;
+ uint32_t double_index_ = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConventionVisitorARMVIXL);
+};
+
class FieldAccessCallingConventionARMVIXL : public FieldAccessCallingConvention {
public:
FieldAccessCallingConventionARMVIXL() {}
@@ -319,27 +253,26 @@
DISALLOW_COPY_AND_ASSIGN(ParallelMoveResolverARMVIXL);
};
-#define DEFINE_IMPLEMENTED_INSTRUCTION_VISITOR(Name) \
- void Visit##Name(H##Name*) OVERRIDE;
-
-#define DEFINE_UNIMPLEMENTED_INSTRUCTION_VISITOR(Name) \
- void Visit##Name(H##Name* instr) OVERRIDE { \
- VisitUnimplemementedInstruction(instr); }
-
class LocationsBuilderARMVIXL : public HGraphVisitor {
public:
LocationsBuilderARMVIXL(HGraph* graph, CodeGeneratorARMVIXL* codegen)
: HGraphVisitor(graph), codegen_(codegen) {}
- FOR_EACH_IMPLEMENTED_INSTRUCTION(DEFINE_IMPLEMENTED_INSTRUCTION_VISITOR)
+#define DECLARE_VISIT_INSTRUCTION(name, super) \
+ void Visit##name(H##name* instr) OVERRIDE;
- FOR_EACH_UNIMPLEMENTED_INSTRUCTION(DEFINE_UNIMPLEMENTED_INSTRUCTION_VISITOR)
+ FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION_ARM(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION_SHARED(DECLARE_VISIT_INSTRUCTION)
- private:
- void VisitUnimplemementedInstruction(HInstruction* instruction) {
- LOG(FATAL) << "Unimplemented Instruction: " << instruction->DebugName();
+#undef DECLARE_VISIT_INSTRUCTION
+
+ void VisitInstruction(HInstruction* instruction) OVERRIDE {
+ LOG(FATAL) << "Unreachable instruction " << instruction->DebugName()
+ << " (id " << instruction->GetId() << ")";
}
+ private:
void HandleInvoke(HInvoke* invoke);
void HandleBitwiseOperation(HBinaryOperation* operation, Opcode opcode);
void HandleCondition(HCondition* condition);
@@ -355,7 +288,7 @@
bool CanEncodeConstantAsImmediate(uint32_t value, Opcode opcode, SetCc set_cc = kCcDontCare);
CodeGeneratorARMVIXL* const codegen_;
- InvokeDexCallingConventionVisitorARM parameter_visitor_;
+ InvokeDexCallingConventionVisitorARMVIXL parameter_visitor_;
DISALLOW_COPY_AND_ASSIGN(LocationsBuilderARMVIXL);
};
@@ -364,25 +297,30 @@
public:
InstructionCodeGeneratorARMVIXL(HGraph* graph, CodeGeneratorARMVIXL* codegen);
- FOR_EACH_IMPLEMENTED_INSTRUCTION(DEFINE_IMPLEMENTED_INSTRUCTION_VISITOR)
+#define DECLARE_VISIT_INSTRUCTION(name, super) \
+ void Visit##name(H##name* instr) OVERRIDE;
- FOR_EACH_UNIMPLEMENTED_INSTRUCTION(DEFINE_UNIMPLEMENTED_INSTRUCTION_VISITOR)
+ FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION_ARM(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION_SHARED(DECLARE_VISIT_INSTRUCTION)
+
+#undef DECLARE_VISIT_INSTRUCTION
+
+ void VisitInstruction(HInstruction* instruction) OVERRIDE {
+ LOG(FATAL) << "Unreachable instruction " << instruction->DebugName()
+ << " (id " << instruction->GetId() << ")";
+ }
ArmVIXLAssembler* GetAssembler() const { return assembler_; }
ArmVIXLMacroAssembler* GetVIXLAssembler() { return GetAssembler()->GetVIXLAssembler(); }
private:
- void VisitUnimplemementedInstruction(HInstruction* instruction) {
- LOG(FATAL) << "Unimplemented Instruction: " << instruction->DebugName();
- }
-
// Generate code for the given suspend check. If not null, `successor`
// is the block to branch to if the suspend check is not needed, and after
// the suspend call.
void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor);
void GenerateClassInitializationCheck(LoadClassSlowPathARMVIXL* slow_path,
vixl32::Register class_reg);
- void HandleGoto(HInstruction* got, HBasicBlock* successor);
void GenerateAndConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value);
void GenerateOrrConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value);
void GenerateEorConst(vixl::aarch32::Register out, vixl::aarch32::Register first, uint32_t value);
@@ -440,17 +378,16 @@
uint32_t offset,
Location maybe_temp,
ReadBarrierOption read_barrier_option);
-
// Generate a GC root reference load:
//
// root <- *(obj + offset)
//
- // while honoring read barriers if `requires_read_barrier` is true.
+ // while honoring read barriers based on read_barrier_option.
void GenerateGcRootFieldLoad(HInstruction* instruction,
Location root,
vixl::aarch32::Register obj,
uint32_t offset,
- bool requires_read_barrier);
+ ReadBarrierOption read_barrier_option);
void GenerateTestAndBranch(HInstruction* instruction,
size_t condition_input_index,
vixl::aarch32::Label* true_target,
@@ -470,6 +407,7 @@
void DivRemByPowerOfTwo(HBinaryOperation* instruction);
void GenerateDivRemWithAnyConstant(HBinaryOperation* instruction);
void GenerateDivRemConstantIntegral(HBinaryOperation* instruction);
+ void HandleGoto(HInstruction* got, HBasicBlock* successor);
ArmVIXLAssembler* const assembler_;
CodeGeneratorARMVIXL* const codegen_;
@@ -483,62 +421,50 @@
const ArmInstructionSetFeatures& isa_features,
const CompilerOptions& compiler_options,
OptimizingCompilerStats* stats = nullptr);
-
virtual ~CodeGeneratorARMVIXL() {}
- void Initialize() OVERRIDE {
- block_labels_.resize(GetGraph()->GetBlocks().size());
- }
-
void GenerateFrameEntry() OVERRIDE;
void GenerateFrameExit() OVERRIDE;
-
void Bind(HBasicBlock* block) OVERRIDE;
-
- vixl::aarch32::Label* GetLabelOf(HBasicBlock* block) {
- block = FirstNonEmptyBlock(block);
- return &(block_labels_[block->GetBlockId()]);
- }
-
void MoveConstant(Location destination, int32_t value) OVERRIDE;
void MoveLocation(Location dst, Location src, Primitive::Type dst_type) OVERRIDE;
void AddLocationAsTemp(Location location, LocationSummary* locations) OVERRIDE;
+ size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
+ size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
+ size_t SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
+ size_t RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
+
+ size_t GetWordSize() const OVERRIDE {
+ return static_cast<size_t>(kArmPointerSize);
+ }
+
+ size_t GetFloatingPointSpillSlotSize() const OVERRIDE { return vixl::aarch32::kRegSizeInBytes; }
+
+ HGraphVisitor* GetLocationBuilder() OVERRIDE { return &location_builder_; }
+
+ HGraphVisitor* GetInstructionVisitor() OVERRIDE { return &instruction_visitor_; }
+
ArmVIXLAssembler* GetAssembler() OVERRIDE { return &assembler_; }
const ArmVIXLAssembler& GetAssembler() const OVERRIDE { return assembler_; }
ArmVIXLMacroAssembler* GetVIXLAssembler() { return GetAssembler()->GetVIXLAssembler(); }
- size_t GetWordSize() const OVERRIDE { return kArmWordSize; }
-
- size_t GetFloatingPointSpillSlotSize() const OVERRIDE { return vixl::aarch32::kRegSizeInBytes; }
-
uintptr_t GetAddressOf(HBasicBlock* block) OVERRIDE {
vixl::aarch32::Label* block_entry_label = GetLabelOf(block);
DCHECK(block_entry_label->IsBound());
return block_entry_label->GetLocation();
}
- JumpTableARMVIXL* CreateJumpTable(HPackedSwitch* switch_instr) {
- jump_tables_.emplace_back(new (GetGraph()->GetArena()) JumpTableARMVIXL(switch_instr));
- return jump_tables_.back().get();
- }
-
- HGraphVisitor* GetLocationBuilder() OVERRIDE { return &location_builder_; }
-
- HGraphVisitor* GetInstructionVisitor() OVERRIDE { return &instruction_visitor_; }
-
void FixJumpTables();
- void GenerateMemoryBarrier(MemBarrierKind kind);
- void Finalize(CodeAllocator* allocator) OVERRIDE;
void SetupBlockedRegisters() const OVERRIDE;
void DumpCoreRegister(std::ostream& stream, int reg) const OVERRIDE;
void DumpFloatingPointRegister(std::ostream& stream, int reg) const OVERRIDE;
+ ParallelMoveResolver* GetMoveResolver() OVERRIDE { return &move_resolver_; }
InstructionSet GetInstructionSet() const OVERRIDE { return InstructionSet::kThumb2; }
-
// Helper method to move a 32-bit value between two locations.
void Move32(Location destination, Location source);
@@ -553,45 +479,6 @@
vixl::aarch32::Register reg_index,
vixl::aarch32::Condition cond = vixl::aarch32::al);
- const ArmInstructionSetFeatures& GetInstructionSetFeatures() const { return isa_features_; }
-
- vixl::aarch32::Label* GetFrameEntryLabel() { return &frame_entry_label_; }
-
- // Saves the register in the stack. Returns the size taken on stack.
- size_t SaveCoreRegister(size_t stack_index ATTRIBUTE_UNUSED,
- uint32_t reg_id ATTRIBUTE_UNUSED) OVERRIDE {
- UNIMPLEMENTED(INFO) << "TODO: SaveCoreRegister";
- return 0;
- }
-
- // Restores the register from the stack. Returns the size taken on stack.
- size_t RestoreCoreRegister(size_t stack_index ATTRIBUTE_UNUSED,
- uint32_t reg_id ATTRIBUTE_UNUSED) OVERRIDE {
- UNIMPLEMENTED(INFO) << "TODO: RestoreCoreRegister";
- return 0;
- }
-
- size_t SaveFloatingPointRegister(size_t stack_index ATTRIBUTE_UNUSED,
- uint32_t reg_id ATTRIBUTE_UNUSED) OVERRIDE {
- UNIMPLEMENTED(INFO) << "TODO: SaveFloatingPointRegister";
- return 0;
- }
-
- size_t RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
-
- bool NeedsTwoRegisters(Primitive::Type type) const OVERRIDE {
- return type == Primitive::kPrimDouble || type == Primitive::kPrimLong;
- }
-
- void ComputeSpillMask() OVERRIDE;
-
- void GenerateImplicitNullCheck(HNullCheck* null_check) OVERRIDE;
- void GenerateExplicitNullCheck(HNullCheck* null_check) OVERRIDE;
-
- ParallelMoveResolver* GetMoveResolver() OVERRIDE {
- return &move_resolver_;
- }
-
// Generate code to invoke a runtime entry point.
void InvokeRuntime(QuickEntrypointEnum entrypoint,
HInstruction* instruction,
@@ -604,8 +491,6 @@
HInstruction* instruction,
SlowPathCode* slow_path);
- void GenerateInvokeRuntime(int32_t entry_point_offset);
-
// Emit a write barrier.
void MarkGCCard(vixl::aarch32::Register temp,
vixl::aarch32::Register card,
@@ -613,6 +498,76 @@
vixl::aarch32::Register value,
bool can_be_null);
+ void GenerateMemoryBarrier(MemBarrierKind kind);
+
+ vixl::aarch32::Label* GetLabelOf(HBasicBlock* block) {
+ block = FirstNonEmptyBlock(block);
+ return &(block_labels_[block->GetBlockId()]);
+ }
+
+ void Initialize() OVERRIDE {
+ block_labels_.resize(GetGraph()->GetBlocks().size());
+ }
+
+ void Finalize(CodeAllocator* allocator) OVERRIDE;
+
+ const ArmInstructionSetFeatures& GetInstructionSetFeatures() const { return isa_features_; }
+
+ bool NeedsTwoRegisters(Primitive::Type type) const OVERRIDE {
+ return type == Primitive::kPrimDouble || type == Primitive::kPrimLong;
+ }
+
+ void ComputeSpillMask() OVERRIDE;
+
+ vixl::aarch32::Label* GetFrameEntryLabel() { return &frame_entry_label_; }
+
+ // Check if the desired_string_load_kind is supported. If it is, return it,
+ // otherwise return a fall-back kind that should be used instead.
+ HLoadString::LoadKind GetSupportedLoadStringKind(
+ HLoadString::LoadKind desired_string_load_kind) OVERRIDE;
+
+ // Check if the desired_class_load_kind is supported. If it is, return it,
+ // otherwise return a fall-back kind that should be used instead.
+ HLoadClass::LoadKind GetSupportedLoadClassKind(
+ HLoadClass::LoadKind desired_class_load_kind) OVERRIDE;
+
+ // 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 GetSupportedInvokeStaticOrDirectDispatch(
+ const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
+ HInvokeStaticOrDirect* invoke) OVERRIDE;
+
+ void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) OVERRIDE;
+ void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp) OVERRIDE;
+
+ void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE;
+
+ // The PcRelativePatchInfo is used for PC-relative addressing of dex cache arrays
+ // and boot image strings/types. The only difference is the interpretation of the
+ // offset_or_index. The PC-relative address is loaded with three instructions,
+ // MOVW+MOVT to load the offset to base_reg and then ADD base_reg, PC. The offset
+ // is calculated from the ADD's effective PC, i.e. PC+4 on Thumb2. Though we
+ // currently emit these 3 instructions together, instruction scheduling could
+ // split this sequence apart, so we keep separate labels for each of them.
+ struct PcRelativePatchInfo {
+ PcRelativePatchInfo(const DexFile& dex_file, uint32_t off_or_idx)
+ : target_dex_file(dex_file), offset_or_index(off_or_idx) { }
+ PcRelativePatchInfo(PcRelativePatchInfo&& other) = default;
+
+ const DexFile& target_dex_file;
+ // Either the dex cache array element offset or the string/type index.
+ uint32_t offset_or_index;
+ vixl::aarch32::Label movw_label;
+ vixl::aarch32::Label movt_label;
+ vixl::aarch32::Label add_pc_label;
+ };
+
+ PcRelativePatchInfo* NewPcRelativeStringPatch(const DexFile& dex_file, uint32_t string_index);
+ PcRelativePatchInfo* NewPcRelativeTypePatch(const DexFile& dex_file, dex::TypeIndex type_index);
+ PcRelativePatchInfo* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file,
+ uint32_t element_offset);
+ void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE;
+
// Fast path implementation of ReadBarrier::Barrier for a heap
// reference field load when Baker's read barriers are used.
void GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
@@ -679,33 +634,35 @@
uint32_t offset,
Location index = Location::NoLocation());
- // Check if the desired_string_load_kind is supported. If it is, return it,
- // otherwise return a fall-back kind that should be used instead.
- HLoadString::LoadKind GetSupportedLoadStringKind(
- HLoadString::LoadKind desired_string_load_kind) OVERRIDE;
-
- // Check if the desired_class_load_kind is supported. If it is, return it,
- // otherwise return a fall-back kind that should be used instead.
- HLoadClass::LoadKind GetSupportedLoadClassKind(
- HLoadClass::LoadKind desired_class_load_kind) OVERRIDE;
-
- // 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 GetSupportedInvokeStaticOrDirectDispatch(
- const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
- HInvokeStaticOrDirect* invoke) OVERRIDE;
-
- void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) OVERRIDE;
- void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp) OVERRIDE;
-
- void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE;
-
void GenerateNop() OVERRIDE;
+ void GenerateImplicitNullCheck(HNullCheck* instruction) OVERRIDE;
+ void GenerateExplicitNullCheck(HNullCheck* instruction) OVERRIDE;
+
+ JumpTableARMVIXL* CreateJumpTable(HPackedSwitch* switch_instr) {
+ jump_tables_.emplace_back(new (GetGraph()->GetArena()) JumpTableARMVIXL(switch_instr));
+ return jump_tables_.back().get();
+ }
+ void EmitJumpTables();
+
+ void EmitMovwMovtPlaceholder(CodeGeneratorARMVIXL::PcRelativePatchInfo* labels,
+ vixl::aarch32::Register out);
+
private:
vixl::aarch32::Register GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke,
vixl::aarch32::Register temp);
+ using Uint32ToLiteralMap = ArenaSafeMap<uint32_t, vixl::aarch32::Literal<uint32_t>*>;
+ using MethodToLiteralMap =
+ ArenaSafeMap<MethodReference, vixl::aarch32::Literal<uint32_t>*, MethodReferenceComparator>;
+
+ PcRelativePatchInfo* NewPcRelativePatch(const DexFile& dex_file,
+ uint32_t offset_or_index,
+ ArenaDeque<PcRelativePatchInfo>* patches);
+ template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)>
+ static void EmitPcRelativeLinkerPatches(const ArenaDeque<PcRelativePatchInfo>& infos,
+ ArenaVector<LinkerPatch>* linker_patches);
+
// Labels for each block that will be compiled.
// We use a deque so that the `vixl::aarch32::Label` objects do not move in memory.
ArenaDeque<vixl::aarch32::Label> block_labels_; // Indexed by block id.
@@ -719,15 +676,19 @@
ArmVIXLAssembler assembler_;
const ArmInstructionSetFeatures& isa_features_;
+ // Relative call patch info.
+ // Using ArenaDeque<> which retains element addresses on push/emplace_back().
+ ArenaDeque<PatchInfo<vixl::aarch32::Label>> relative_call_patches_;
+ // PC-relative patch info for each HArmDexCacheArraysBase.
+ ArenaDeque<PcRelativePatchInfo> pc_relative_dex_cache_patches_;
+ // PC-relative String patch info; type depends on configuration (app .bss or boot image PIC).
+ ArenaDeque<PcRelativePatchInfo> pc_relative_string_patches_;
+ // PC-relative type patch info.
+ ArenaDeque<PcRelativePatchInfo> pc_relative_type_patches_;
+
DISALLOW_COPY_AND_ASSIGN(CodeGeneratorARMVIXL);
};
-#undef FOR_EACH_IMPLEMENTED_INSTRUCTION
-#undef FOR_EACH_UNIMPLEMENTED_INSTRUCTION
-#undef DEFINE_IMPLEMENTED_INSTRUCTION_VISITOR
-#undef DEFINE_UNIMPLEMENTED_INSTRUCTION_VISITOR
-
-
} // namespace arm
} // namespace art
diff --git a/compiler/optimizing/common_arm.h b/compiler/optimizing/common_arm.h
index d3623f1..eabdbad 100644
--- a/compiler/optimizing/common_arm.h
+++ b/compiler/optimizing/common_arm.h
@@ -17,6 +17,11 @@
#ifndef ART_COMPILER_OPTIMIZING_COMMON_ARM_H_
#define ART_COMPILER_OPTIMIZING_COMMON_ARM_H_
+#include "debug/dwarf/register.h"
+#include "locations.h"
+#include "nodes.h"
+#include "utils/arm/constants_arm.h"
+
// TODO(VIXL): Make VIXL compile with -Wshadow.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
diff --git a/compiler/optimizing/dex_cache_array_fixups_arm.cc b/compiler/optimizing/dex_cache_array_fixups_arm.cc
index 82b8123..10a36c6 100644
--- a/compiler/optimizing/dex_cache_array_fixups_arm.cc
+++ b/compiler/optimizing/dex_cache_array_fixups_arm.cc
@@ -17,12 +17,24 @@
#include "dex_cache_array_fixups_arm.h"
#include "base/arena_containers.h"
+#ifdef ART_USE_VIXL_ARM_BACKEND
+#include "code_generator_arm_vixl.h"
+#include "intrinsics_arm_vixl.h"
+#else
#include "code_generator_arm.h"
#include "intrinsics_arm.h"
+#endif
#include "utils/dex_cache_arrays_layout-inl.h"
namespace art {
namespace arm {
+#ifdef ART_USE_VIXL_ARM_BACKEND
+typedef CodeGeneratorARMVIXL CodeGeneratorARMType;
+typedef IntrinsicLocationsBuilderARMVIXL IntrinsicLocationsBuilderARMType;
+#else
+typedef CodeGeneratorARM CodeGeneratorARMType;
+typedef IntrinsicLocationsBuilderARM IntrinsicLocationsBuilderARMType;
+#endif
/**
* Finds instructions that need the dex cache arrays base as an input.
@@ -31,7 +43,7 @@
public:
DexCacheArrayFixupsVisitor(HGraph* graph, CodeGenerator* codegen)
: HGraphVisitor(graph),
- codegen_(down_cast<CodeGeneratorARM*>(codegen)),
+ codegen_(down_cast<CodeGeneratorARMType*>(codegen)),
dex_cache_array_bases_(std::less<const DexFile*>(),
// Attribute memory use to code generator.
graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {}
@@ -66,7 +78,7 @@
// If this is an invoke with PC-relative access to the dex cache methods array,
// we need to add the dex cache arrays base as the special input.
if (invoke->HasPcRelativeDexCache() &&
- !IsCallFreeIntrinsic<IntrinsicLocationsBuilderARM>(invoke, codegen_)) {
+ !IsCallFreeIntrinsic<IntrinsicLocationsBuilderARMType>(invoke, codegen_)) {
HArmDexCacheArraysBase* base = GetOrCreateDexCacheArrayBase(invoke->GetDexFile());
// Update the element offset in base.
DexCacheArraysLayout layout(kArmPointerSize, &invoke->GetDexFile());
@@ -94,7 +106,7 @@
return base;
}
- CodeGeneratorARM* codegen_;
+ CodeGeneratorARMType* codegen_;
using DexCacheArraysBaseMap =
ArenaSafeMap<const DexFile*, HArmDexCacheArraysBase*, std::less<const DexFile*>>;
diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc
index f2602fb..1561502 100644
--- a/compiler/optimizing/induction_var_analysis.cc
+++ b/compiler/optimizing/induction_var_analysis.cc
@@ -218,20 +218,21 @@
} else if (instruction->IsSub()) {
info = TransferAddSub(LookupInfo(loop, instruction->InputAt(0)),
LookupInfo(loop, instruction->InputAt(1)), kSub);
+ } else if (instruction->IsNeg()) {
+ info = TransferNeg(LookupInfo(loop, instruction->InputAt(0)));
} else if (instruction->IsMul()) {
info = TransferMul(LookupInfo(loop, instruction->InputAt(0)),
LookupInfo(loop, instruction->InputAt(1)));
} else if (instruction->IsShl()) {
- info = TransferShl(LookupInfo(loop, instruction->InputAt(0)),
- LookupInfo(loop, instruction->InputAt(1)),
- instruction->InputAt(0)->GetType());
- } else if (instruction->IsNeg()) {
- info = TransferNeg(LookupInfo(loop, instruction->InputAt(0)));
+ HInstruction* mulc = GetMultConstantForShift(loop, instruction);
+ if (mulc != nullptr) {
+ info = TransferMul(LookupInfo(loop, instruction->InputAt(0)),
+ LookupInfo(loop, mulc));
+ }
} else if (instruction->IsTypeConversion()) {
info = TransferCnv(LookupInfo(loop, instruction->InputAt(0)),
instruction->AsTypeConversion()->GetInputType(),
instruction->AsTypeConversion()->GetResultType());
-
} else if (instruction->IsBoundsCheck()) {
info = LookupInfo(loop, instruction->InputAt(0)); // Pass-through.
}
@@ -271,7 +272,12 @@
if (size == 1) {
InductionInfo* update = TransferPhi(loop, phi, /* input_index */ 1);
if (update != nullptr) {
- AssignInfo(loop, phi, CreateInduction(kWrapAround, initial, update, type_));
+ AssignInfo(loop, phi, CreateInduction(kWrapAround,
+ kNop,
+ initial,
+ update,
+ /*fetch*/ nullptr,
+ type_));
}
return;
}
@@ -289,6 +295,20 @@
} else if (instruction->IsSub()) {
update = SolveAddSub(
loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kSub, true);
+ } else if (instruction->IsMul()) {
+ update = SolveGeo(
+ loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kMul);
+ } else if (instruction->IsDiv()) {
+ update = SolveGeo(
+ 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);
+ } else if (instruction->IsShl()) {
+ HInstruction* mulc = GetMultConstantForShift(loop, instruction);
+ if (mulc != nullptr) {
+ update = SolveGeo(loop, phi, instruction, instruction->InputAt(0), mulc, kMul);
+ }
} else if (instruction->IsXor()) {
update = SolveXor(loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1));
} else if (instruction->IsEqual()) {
@@ -309,9 +329,14 @@
if (induction != nullptr) {
switch (induction->induction_class) {
case kInvariant:
+ // Construct combined stride of the linear induction.
+ induction = CreateInduction(kLinear, kNop, induction, initial, /*fetch*/ nullptr, type_);
+ FALLTHROUGH_INTENDED;
+ case kPolynomial:
+ case kGeometric:
// Classify first phi and then the rest of the cycle "on-demand".
// Statements are scanned in order.
- AssignInfo(loop, phi, CreateInduction(kLinear, induction, initial, type_));
+ AssignInfo(loop, phi, induction);
for (size_t i = 1; i < size; i++) {
ClassifyTrivial(loop, scc_[i]);
}
@@ -341,10 +366,19 @@
// (b, c, d, e, a)
// in preparation of assigning this to the previous variable in the sequence.
if (induction->induction_class == kInvariant) {
- return CreateInduction(kPeriodic, induction, last, type_);
+ return CreateInduction(kPeriodic,
+ kNop,
+ induction,
+ last,
+ /*fetch*/ nullptr,
+ type_);
}
- return CreateInduction(
- kPeriodic, induction->op_a, RotatePeriodicInduction(induction->op_b, last), type_);
+ return CreateInduction(kPeriodic,
+ kNop,
+ induction->op_a,
+ RotatePeriodicInduction(induction->op_b, last),
+ /*fetch*/ nullptr,
+ type_);
}
HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferPhi(HLoopInformation* loop,
@@ -366,36 +400,55 @@
HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferAddSub(InductionInfo* a,
InductionInfo* b,
InductionOp op) {
- // Transfer over an addition or subtraction: any invariant, linear, wrap-around, or periodic
- // can be combined with an invariant to yield a similar result. Even two linear inputs can
- // be combined. All other combinations fail, however.
+ // 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.
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,
TransferAddSub(a->op_a, b->op_a, op),
TransferAddSub(a->op_b, b->op_b, op),
+ /*fetch*/ nullptr,
type_);
} else if (a->induction_class == kInvariant) {
InductionInfo* new_a = b->op_a;
InductionInfo* new_b = TransferAddSub(a, b->op_b, op);
- if (b->induction_class != kLinear) {
- DCHECK(b->induction_class == kWrapAround || b->induction_class == kPeriodic);
+ if (b->induction_class == kWrapAround || b->induction_class == kPeriodic) {
new_a = TransferAddSub(a, new_a, op);
} else if (op == kSub) { // Negation required.
new_a = TransferNeg(new_a);
}
- return CreateInduction(b->induction_class, new_a, new_b, type_);
+ return CreateInduction(b->induction_class, b->operation, new_a, new_b, b->fetch, type_);
} else if (b->induction_class == kInvariant) {
InductionInfo* new_a = a->op_a;
InductionInfo* new_b = TransferAddSub(a->op_b, b, op);
- if (a->induction_class != kLinear) {
- DCHECK(a->induction_class == kWrapAround || a->induction_class == kPeriodic);
+ if (a->induction_class == kWrapAround || a->induction_class == kPeriodic) {
new_a = TransferAddSub(new_a, b, op);
}
- return CreateInduction(a->induction_class, new_a, new_b, type_);
+ return CreateInduction(a->induction_class, a->operation, new_a, new_b, a->fetch, type_);
+ }
+ }
+ return nullptr;
+}
+
+HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferNeg(InductionInfo* a) {
+ // Transfer over a unary negation: an invariant, linear, polynomial, geometric (mul),
+ // wrap-around, or periodic input yields a similar but negated induction as result.
+ if (a != nullptr) {
+ type_ = Narrowest(type_, a->type);
+ if (a->induction_class == kInvariant) {
+ return CreateInvariantOp(kNeg, nullptr, a);
+ } else if (a->induction_class != kGeometric || a->operation == kMul) {
+ return CreateInduction(a->induction_class,
+ a->operation,
+ TransferNeg(a->op_a),
+ TransferNeg(a->op_b),
+ a->fetch,
+ type_);
}
}
return nullptr;
@@ -403,72 +456,45 @@
HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferMul(InductionInfo* a,
InductionInfo* b) {
- // Transfer over a multiplication: any invariant, linear, wrap-around, or periodic
- // can be multiplied with an invariant to yield a similar but multiplied result.
- // Two non-invariant inputs cannot be multiplied, however.
+ // Transfer over a multiplication: any invariant, linear, polynomial, geometric (mul),
+ // wrap-around, or periodic can be multiplied with an invariant to yield a similar
+ // but multiplied result. Two non-invariant inputs cannot be multiplied, however.
if (a != nullptr && b != nullptr) {
type_ = Narrowest(type_, Narrowest(a->type, b->type));
if (a->induction_class == kInvariant && b->induction_class == kInvariant) {
return CreateInvariantOp(kMul, a, b);
- } else if (a->induction_class == kInvariant) {
+ } else if (a->induction_class == kInvariant && (b->induction_class != kGeometric ||
+ b->operation == kMul)) {
return CreateInduction(b->induction_class,
+ b->operation,
TransferMul(a, b->op_a),
TransferMul(a, b->op_b),
+ b->fetch,
type_);
- } else if (b->induction_class == kInvariant) {
+ } else if (b->induction_class == kInvariant && (a->induction_class != kGeometric ||
+ a->operation == kMul)) {
return CreateInduction(a->induction_class,
+ a->operation,
TransferMul(a->op_a, b),
TransferMul(a->op_b, b),
+ a->fetch,
type_);
}
}
return nullptr;
}
-HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferShl(InductionInfo* a,
- InductionInfo* b,
- Primitive::Type type) {
- // Transfer over a shift left: treat shift by restricted constant as equivalent multiplication.
- int64_t value = -1;
- if (a != nullptr && IsExact(b, &value)) {
- // Obtain the constant needed for the multiplication. This yields an existing instruction
- // if the constants 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.
- if ((type == Primitive::kPrimInt && 0 <= value && value < 31) ||
- (type == Primitive::kPrimLong && 0 <= value && value < 63)) {
- return TransferMul(a, CreateConstant(1 << value, type));
- }
- }
- return nullptr;
-}
-
-HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferNeg(InductionInfo* a) {
- // Transfer over a unary negation: an invariant, linear, wrap-around, or periodic input
- // yields a similar but negated induction as result.
- if (a != nullptr) {
- type_ = Narrowest(type_, a->type);
- if (a->induction_class == kInvariant) {
- return CreateInvariantOp(kNeg, nullptr, a);
- }
- return CreateInduction(a->induction_class, TransferNeg(a->op_a), TransferNeg(a->op_b), type_);
- }
- return nullptr;
-}
-
HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferCnv(InductionInfo* a,
Primitive::Type from,
Primitive::Type to) {
if (a != nullptr) {
- // Allow narrowing conversion in certain cases.
+ // Allow narrowing conversion on linear induction in certain cases.
if (IsNarrowingIntegralConversion(from, to)) {
if (a->induction_class == kLinear) {
if (a->type == to || (a->type == from && IsNarrowingIntegralConversion(from, to))) {
- return CreateInduction(kLinear, a->op_a, a->op_b, to);
+ return CreateInduction(kLinear, kNop, a->op_a, a->op_b, /*fetch*/ nullptr, to);
}
}
- // TODO: other cases useful too?
}
}
return nullptr;
@@ -511,11 +537,11 @@
if (a != nullptr && a->induction_class == kInvariant) {
if (phi->InputAt(1) == entry_phi) {
InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0));
- return CreateInduction(kPeriodic, a, initial, type_);
+ return CreateInduction(kPeriodic, kNop, a, initial, /*fetch*/ nullptr, type_);
}
InductionInfo* b = SolvePhi(phi, /* input_index */ 1);
if (b != nullptr && b->induction_class == kPeriodic) {
- return CreateInduction(kPeriodic, a, b, type_);
+ return CreateInduction(kPeriodic, kNop, a, b, /*fetch*/ nullptr, type_);
}
}
}
@@ -530,7 +556,7 @@
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 induction.
+ // invariant value, seeded from phi, keeps adding to the stride of the linear induction.
InductionInfo* b = LookupInfo(loop, y);
if (b != nullptr && b->induction_class == kInvariant) {
if (x == entry_phi) {
@@ -553,12 +579,17 @@
}
} else if (op == kSub) {
// Solve within a tight cycle that is formed by exactly two instructions,
- // one phi and one update, for a periodic idiom of the form k = c - k;
+ // one phi and one update, for a periodic idiom of the form k = c - k.
if (y == entry_phi && entry_phi->InputCount() == 2 && instruction == entry_phi->InputAt(1)) {
InductionInfo* a = LookupInfo(loop, x);
if (a != nullptr && a->induction_class == kInvariant) {
InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0));
- return CreateInduction(kPeriodic, CreateInvariantOp(kSub, a, initial), initial, type_);
+ return CreateInduction(kPeriodic,
+ kNop,
+ CreateInvariantOp(kSub, a, initial),
+ initial,
+ /*fetch*/ nullptr,
+ type_);
}
}
}
@@ -566,21 +597,54 @@
return nullptr;
}
+HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveGeo(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 x = c ^ x or x = x ^ c.
+ // Solve within a tight cycle on periodic idiom of the form x = c ^ x or x = x ^ c.
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, CreateInvariantOp(kXor, a, initial), initial, type_);
+ return CreateInduction(kPeriodic,
+ kNop,
+ CreateInvariantOp(kXor, a, initial),
+ initial,
+ /*fetch*/ nullptr,
+ type_);
}
InductionInfo* b = LookupInfo(loop, y);
if (b != nullptr && b->induction_class == kInvariant && entry_phi == x) {
- return CreateInduction(kPeriodic, CreateInvariantOp(kXor, initial, b), initial, type_);
+ return CreateInduction(kPeriodic,
+ kNop,
+ CreateInvariantOp(kXor, initial, b),
+ initial,
+ /*fetch*/ nullptr,
+ type_);
}
}
return nullptr;
@@ -590,7 +654,7 @@
HInstruction* entry_phi,
HInstruction* instruction,
int64_t opposite_value) {
- // Detect hidden XOR construction in tight cycles on x = (x == 0) or x = (x != 1).
+ // Detect hidden XOR construction in x = (x == false) or x = (x != true).
int64_t value = -1;
HInstruction* x = instruction->InputAt(0);
HInstruction* y = instruction->InputAt(1);
@@ -881,11 +945,14 @@
HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::CreateConstant(int64_t value,
Primitive::Type type) {
- if (type == Primitive::kPrimInt) {
- return CreateInvariantFetch(graph_->GetIntConstant(value));
+ HInstruction* constant;
+ switch (type) {
+ case Primitive::kPrimDouble: constant = graph_->GetDoubleConstant(value); break;
+ case Primitive::kPrimFloat: constant = graph_->GetFloatConstant(value); break;
+ case Primitive::kPrimLong: constant = graph_->GetLongConstant(value); break;
+ default: constant = graph_->GetIntConstant(value); break;
}
- DCHECK_EQ(type, Primitive::kPrimLong);
- return CreateInvariantFetch(graph_->GetLongConstant(value));
+ return CreateInvariantFetch(constant);
}
HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::CreateSimplifiedInvariant(
@@ -948,6 +1015,26 @@
return new (graph_->GetArena()) InductionInfo(kInvariant, op, a, b, nullptr, b->type);
}
+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
+ // 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.
+ InductionInfo* b = LookupInfo(loop, instruction->InputAt(1));
+ int64_t value = -1;
+ if (IsExact(b, &value)) {
+ Primitive::Type type = instruction->InputAt(0)->GetType();
+ if (type == Primitive::kPrimInt && 0 <= value && value < 31) {
+ return graph_->GetIntConstant(1 << value);
+ }
+ if (type == Primitive::kPrimLong && 0 <= value && value < 63) {
+ return graph_->GetLongConstant(1L << value);
+ }
+ }
+ return nullptr;
+}
void HInductionVarAnalysis::AssignCycle(HPhi* phi) {
ArenaSet<HInstruction*>* set = &cycles_.Put(phi, ArenaSet<HInstruction*>(
@@ -993,6 +1080,16 @@
return info1 == info2;
}
+std::string HInductionVarAnalysis::FetchToString(HInstruction* fetch) {
+ DCHECK(fetch != nullptr);
+ if (fetch->IsIntConstant()) {
+ return std::to_string(fetch->AsIntConstant()->GetValue());
+ } else if (fetch->IsLongConstant()) {
+ return std::to_string(fetch->AsLongConstant()->GetValue());
+ }
+ return std::to_string(fetch->GetId()) + ":" + fetch->DebugName();
+}
+
std::string HInductionVarAnalysis::InductionToString(InductionInfo* info) {
if (info != nullptr) {
if (info->induction_class == kInvariant) {
@@ -1010,16 +1107,7 @@
case kLE: inv += " <= "; break;
case kGT: inv += " > "; break;
case kGE: inv += " >= "; break;
- case kFetch:
- DCHECK(info->fetch);
- if (info->fetch->IsIntConstant()) {
- inv += std::to_string(info->fetch->AsIntConstant()->GetValue());
- } else if (info->fetch->IsLongConstant()) {
- inv += std::to_string(info->fetch->AsLongConstant()->GetValue());
- } else {
- inv += std::to_string(info->fetch->GetId()) + ":" + info->fetch->DebugName();
- }
- break;
+ case kFetch: inv += FetchToString(info->fetch); break;
case kTripCountInLoop: inv += " (TC-loop) "; break;
case kTripCountInBody: inv += " (TC-body) "; break;
case kTripCountInLoopUnsafe: inv += " (TC-loop-unsafe) "; break;
@@ -1029,11 +1117,28 @@
inv += ")";
return inv;
} else {
- DCHECK(info->operation == kNop);
if (info->induction_class == kLinear) {
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) + ") + " +
+ InductionToString(info->op_b) + "):" +
+ Primitive::PrettyDescriptor(info->type);
+ } else if (info->induction_class == kGeometric) {
+ 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);
+ }
} else if (info->induction_class == kWrapAround) {
return "wrap(" + InductionToString(info->op_a) + ", " +
InductionToString(info->op_b) + "):" +
diff --git a/compiler/optimizing/induction_var_analysis.h b/compiler/optimizing/induction_var_analysis.h
index 7027179..94afc71 100644
--- a/compiler/optimizing/induction_var_analysis.h
+++ b/compiler/optimizing/induction_var_analysis.h
@@ -51,14 +51,15 @@
enum InductionClass {
kInvariant,
kLinear,
+ kPolynomial,
+ kGeometric,
kWrapAround,
kPeriodic
};
enum InductionOp {
- // No-operation: a true induction.
+ // Operations.
kNop,
- // Various invariant operations.
kAdd,
kSub,
kNeg,
@@ -81,16 +82,18 @@
/**
* Defines a detected induction as:
* (1) invariant:
- * operation: a + b, a - b, -b, a * b, a / b
- * or:
- * fetch: fetch from HIR
+ * op: a + b, a - b, -b, a * b, a / b, a % b, a ^ b, fetch
* (2) linear:
* nop: a * i + b
- * (3) wrap-around
+ * (3) polynomial: // TODO: coming soon
+ * nop: sum_i(a) + b, for linear a
+ * (4) geometric:
+ * op: a * fetch^i + b, a * fetch^-i + b, a mod_i fetch + b
+ * (5) wrap-around
* nop: a, then defined by b
- * (4) periodic
+ * (6) periodic
* nop: a, then defined by b (repeated when exhausted)
- * (5) trip-count:
+ * (7) trip-count:
* tc: defined by a, taken-test in b
*/
struct InductionInfo : public ArenaObject<kArenaAllocInductionVarAnalysis> {
@@ -138,11 +141,13 @@
}
InductionInfo* CreateInduction(InductionClass ic,
+ InductionOp op,
InductionInfo* a,
InductionInfo* b,
+ HInstruction* f,
Primitive::Type type) {
DCHECK(a != nullptr && b != nullptr);
- return new (graph_->GetArena()) InductionInfo(ic, kNop, a, b, nullptr, type);
+ return new (graph_->GetArena()) InductionInfo(ic, op, a, b, f, type);
}
// Methods for analysis.
@@ -156,9 +161,8 @@
// Transfer operations.
InductionInfo* TransferPhi(HLoopInformation* loop, HInstruction* phi, size_t input_index);
InductionInfo* TransferAddSub(InductionInfo* a, InductionInfo* b, InductionOp op);
- InductionInfo* TransferMul(InductionInfo* a, InductionInfo* b);
- InductionInfo* TransferShl(InductionInfo* a, InductionInfo* b, Primitive::Type type);
InductionInfo* TransferNeg(InductionInfo* a);
+ InductionInfo* TransferMul(InductionInfo* a, InductionInfo* b);
InductionInfo* TransferCnv(InductionInfo* a, Primitive::Type from, Primitive::Type to);
// Solvers.
@@ -173,6 +177,12 @@
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,
@@ -214,6 +224,7 @@
InductionInfo* LookupInfo(HLoopInformation* loop, HInstruction* instruction);
InductionInfo* CreateConstant(int64_t value, Primitive::Type type);
InductionInfo* CreateSimplifiedInvariant(InductionOp op, InductionInfo* a, InductionInfo* b);
+ HInstruction* GetMultConstantForShift(HLoopInformation* loop, HInstruction* instruction);
void AssignCycle(HPhi* phi);
ArenaSet<HInstruction*>* LookupCycle(HPhi* phi);
@@ -224,6 +235,7 @@
// Helpers.
static bool InductionEqual(InductionInfo* info1, InductionInfo* info2);
+ static std::string FetchToString(HInstruction* fetch);
static std::string InductionToString(InductionInfo* info);
// TODO: fine tune the following data structures, only keep relevant data.
diff --git a/compiler/optimizing/induction_var_analysis_test.cc b/compiler/optimizing/induction_var_analysis_test.cc
index 3425b88..2199c8e 100644
--- a/compiler/optimizing/induction_var_analysis_test.cc
+++ b/compiler/optimizing/induction_var_analysis_test.cc
@@ -84,6 +84,7 @@
entry_->AddInstruction(parameter_);
constant0_ = graph_->GetIntConstant(0);
constant1_ = graph_->GetIntConstant(1);
+ constant2_ = graph_->GetIntConstant(2);
constant100_ = graph_->GetIntConstant(100);
float_constant0_ = graph_->GetFloatConstant(0.0f);
return_->AddInstruction(new (&allocator_) HReturnVoid());
@@ -191,6 +192,7 @@
HInstruction* parameter_; // "this"
HInstruction* constant0_;
HInstruction* constant1_;
+ HInstruction* constant2_;
HInstruction* constant100_;
HInstruction* float_constant0_;
@@ -252,11 +254,11 @@
TEST_F(InductionVarAnalysisTest, FindDerivedInduction) {
// Setup:
// for (int i = 0; i < 100; i++) {
- // k = 100 + i;
- // k = 100 - i;
- // k = 100 * i;
- // k = i << 1;
- // k = - i;
+ // t = 100 + i;
+ // t = 100 - i;
+ // t = 100 * i;
+ // t = i << 1;
+ // t = - i;
// }
BuildLoopNest(1);
HInstruction* add = InsertInstruction(
@@ -288,18 +290,20 @@
// a[k] = 0;
// }
BuildLoopNest(1);
- HPhi* k = InsertLoopPhi(0, 0);
- k->AddInput(constant0_);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constant0_);
HInstruction* add = InsertInstruction(
- new (&allocator_) HAdd(Primitive::kPrimInt, k, constant100_), 0);
+ new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant100_), 0);
HInstruction* store1 = InsertArrayStore(add, 0);
HInstruction* sub = InsertInstruction(
new (&allocator_) HSub(Primitive::kPrimInt, add, constant1_), 0);
HInstruction* store2 = InsertArrayStore(sub, 0);
- k->AddInput(sub);
+ k_header->AddInput(sub);
PerformInductionVarAnalysis();
+ EXPECT_STREQ("(((100) - (1)) * i + (0)):PrimInt",
+ GetInductionInfo(k_header, 0).c_str());
EXPECT_STREQ("(((100) - (1)) * i + (100)):PrimInt",
GetInductionInfo(store1->InputAt(1), 0).c_str());
EXPECT_STREQ("(((100) - (1)) * i + ((100) - (1))):PrimInt",
@@ -335,6 +339,7 @@
k_header->AddInput(k_body);
PerformInductionVarAnalysis();
+ EXPECT_STREQ("((1) * i + (0)):PrimInt", GetInductionInfo(k_header, 0).c_str());
EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
// Both increments get same induction.
@@ -367,6 +372,153 @@
PerformInductionVarAnalysis();
EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
+
+ // Both increments get same induction.
+ EXPECT_TRUE(HaveSameInduction(store->InputAt(1), inc1));
+ EXPECT_TRUE(HaveSameInduction(store->InputAt(1), inc2));
+}
+
+TEST_F(InductionVarAnalysisTest, FindGeometricMulInduction) {
+ // Setup:
+ // k = 1;
+ // for (int i = 0; i < 100; i++) {
+ // k = k * 100; // geometric (x 100)
+ // }
+ BuildLoopNest(1);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constant1_);
+
+ HInstruction* mul = InsertInstruction(
+ new (&allocator_) HMul(Primitive::kPrimInt, k_header, constant100_), 0);
+ k_header->AddInput(mul);
+ PerformInductionVarAnalysis();
+
+ EXPECT_STREQ("geo((1) * 100 ^ i + (0)):PrimInt", GetInductionInfo(k_header, 0).c_str());
+ EXPECT_STREQ("geo((100) * 100 ^ i + (0)):PrimInt", GetInductionInfo(mul, 0).c_str());
+}
+
+TEST_F(InductionVarAnalysisTest, FindGeometricShlInductionAndDerived) {
+ // Setup:
+ // k = 1;
+ // for (int i = 0; i < 100; i++) {
+ // t = k + 1;
+ // k = k << 1; // geometric (x 2)
+ // t = k + 100;
+ // t = k - 1;
+ // t = - t;
+ // t = k * 2;
+ // t = k << 2;
+ // }
+ BuildLoopNest(1);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constant1_);
+
+ HInstruction* add1 = InsertInstruction(
+ new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant1_), 0);
+ HInstruction* shl1 = InsertInstruction(
+ new (&allocator_) HShl(Primitive::kPrimInt, k_header, constant1_), 0);
+ HInstruction* add2 = InsertInstruction(
+ new (&allocator_) HAdd(Primitive::kPrimInt, shl1, constant100_), 0);
+ HInstruction* sub = InsertInstruction(
+ new (&allocator_) HSub(Primitive::kPrimInt, shl1, constant1_), 0);
+ HInstruction* neg = InsertInstruction(
+ new (&allocator_) HNeg(Primitive::kPrimInt, sub), 0);
+ HInstruction* mul = InsertInstruction(
+ new (&allocator_) HMul(Primitive::kPrimInt, shl1, constant2_), 0);
+ HInstruction* shl2 = InsertInstruction(
+ new (&allocator_) HShl(Primitive::kPrimInt, shl1, constant2_), 0);
+ k_header->AddInput(shl1);
+ PerformInductionVarAnalysis();
+
+ EXPECT_STREQ("geo((1) * 2 ^ i + (0)):PrimInt", GetInductionInfo(k_header, 0).c_str());
+ EXPECT_STREQ("geo((1) * 2 ^ i + (1)):PrimInt", GetInductionInfo(add1, 0).c_str());
+ EXPECT_STREQ("geo((2) * 2 ^ i + (0)):PrimInt", GetInductionInfo(shl1, 0).c_str());
+ EXPECT_STREQ("geo((2) * 2 ^ i + (100)):PrimInt", GetInductionInfo(add2, 0).c_str());
+ EXPECT_STREQ("geo((2) * 2 ^ i + ((0) - (1))):PrimInt", GetInductionInfo(sub, 0).c_str());
+ EXPECT_STREQ("geo(( - (2)) * 2 ^ i + ( - ((0) - (1)))):PrimInt",
+ GetInductionInfo(neg, 0).c_str());
+ EXPECT_STREQ("geo(((2) * (2)) * 2 ^ i + (0)):PrimInt", GetInductionInfo(mul, 0).c_str());
+ EXPECT_STREQ("geo(((2) * (4)) * 2 ^ i + (0)):PrimInt", GetInductionInfo(shl2, 0).c_str());
+}
+
+TEST_F(InductionVarAnalysisTest, FindGeometricDivInductionAndDerived) {
+ // 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 / 100; // geometric (/ 100)
+ // }
+ 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* div = InsertInstruction(
+ new (&allocator_) HDiv(Primitive::kPrimInt, k_header, constant100_, kNoDexPc), 0);
+ k_header->AddInput(div);
+ PerformInductionVarAnalysis();
+
+ // Note, only the phi in the cycle and direct additive derived are classified.
+ EXPECT_STREQ("geo((1) * 100 ^ -i + (0)):PrimInt", GetInductionInfo(k_header, 0).c_str());
+ EXPECT_STREQ("geo((1) * 100 ^ -i + (100)):PrimInt", GetInductionInfo(add, 0).c_str());
+ EXPECT_STREQ("geo((1) * 100 ^ -i + ((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());
+ EXPECT_STREQ("", GetInductionInfo(div, 0).c_str());
+}
+
+TEST_F(InductionVarAnalysisTest, FindGeometricRemInductionAndDerived) {
+ // 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 % 100; // geometric (% 100)
+ // }
+ 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* rem = InsertInstruction(
+ new (&allocator_) HRem(Primitive::kPrimInt, k_header, constant100_, 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());
+ EXPECT_STREQ("", GetInductionInfo(rem, 0).c_str());
}
TEST_F(InductionVarAnalysisTest, FindFirstOrderWrapAroundInduction) {
@@ -377,17 +529,20 @@
// k = 100 - i;
// }
BuildLoopNest(1);
- HPhi* k = InsertLoopPhi(0, 0);
- k->AddInput(constant0_);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constant0_);
- HInstruction* store = InsertArrayStore(k, 0);
+ HInstruction* store = InsertArrayStore(k_header, 0);
HInstruction* sub = InsertInstruction(
new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0]), 0);
- k->AddInput(sub);
+ k_header->AddInput(sub);
PerformInductionVarAnalysis();
EXPECT_STREQ("wrap((0), (( - (1)) * i + (100)):PrimInt):PrimInt",
+ GetInductionInfo(k_header, 0).c_str());
+ EXPECT_STREQ("wrap((0), (( - (1)) * i + (100)):PrimInt):PrimInt",
GetInductionInfo(store->InputAt(1), 0).c_str());
+ EXPECT_STREQ("(( - (1)) * i + (100)):PrimInt", GetInductionInfo(sub, 0).c_str());
}
TEST_F(InductionVarAnalysisTest, FindSecondOrderWrapAroundInduction) {
@@ -400,13 +555,13 @@
// t = 100 - i;
// }
BuildLoopNest(1);
- HPhi* k = InsertLoopPhi(0, 0);
- k->AddInput(constant0_);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constant0_);
HPhi* t = InsertLoopPhi(1, 0);
t->AddInput(constant100_);
- HInstruction* store = InsertArrayStore(k, 0);
- k->AddInput(t);
+ HInstruction* store = InsertArrayStore(k_header, 0);
+ k_header->AddInput(t);
HInstruction* sub = InsertInstruction(
new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0], 0), 0);
t->AddInput(sub);
@@ -426,23 +581,27 @@
// t = k << 1;
// t = - k;
// k = i << 1;
+ // t = - k;
// }
BuildLoopNest(1);
- HPhi* k = InsertLoopPhi(0, 0);
- k->AddInput(constant0_);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constant0_);
HInstruction* add = InsertInstruction(
- new (&allocator_) HAdd(Primitive::kPrimInt, k, constant100_), 0);
+ new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant100_), 0);
HInstruction* sub = InsertInstruction(
- new (&allocator_) HSub(Primitive::kPrimInt, k, constant100_), 0);
+ new (&allocator_) HSub(Primitive::kPrimInt, k_header, constant100_), 0);
HInstruction* mul = InsertInstruction(
- new (&allocator_) HMul(Primitive::kPrimInt, k, constant100_), 0);
- HInstruction* shl = InsertInstruction(
- new (&allocator_) HShl(Primitive::kPrimInt, k, constant1_), 0);
- HInstruction* neg = InsertInstruction(
- new (&allocator_) HNeg(Primitive::kPrimInt, k), 0);
- k->AddInput(
- InsertInstruction(new (&allocator_) HShl(Primitive::kPrimInt, basic_[0], constant1_), 0));
+ new (&allocator_) HMul(Primitive::kPrimInt, k_header, constant100_), 0);
+ HInstruction* shl1 = InsertInstruction(
+ new (&allocator_) HShl(Primitive::kPrimInt, k_header, constant1_), 0);
+ HInstruction* neg1 = InsertInstruction(
+ new (&allocator_) HNeg(Primitive::kPrimInt, k_header), 0);
+ HInstruction* shl2 = InsertInstruction(
+ new (&allocator_) HShl(Primitive::kPrimInt, basic_[0], constant1_), 0);
+ HInstruction* neg2 = InsertInstruction(
+ new (&allocator_) HNeg(Primitive::kPrimInt, shl2), 0);
+ k_header->AddInput(shl2);
PerformInductionVarAnalysis();
EXPECT_STREQ("wrap((100), ((2) * i + (100)):PrimInt):PrimInt",
@@ -452,9 +611,11 @@
EXPECT_STREQ("wrap((0), (((2) * (100)) * i + (0)):PrimInt):PrimInt",
GetInductionInfo(mul, 0).c_str());
EXPECT_STREQ("wrap((0), (((2) * (2)) * i + (0)):PrimInt):PrimInt",
- GetInductionInfo(shl, 0).c_str());
+ GetInductionInfo(shl1, 0).c_str());
EXPECT_STREQ("wrap((0), (( - (2)) * i + (0)):PrimInt):PrimInt",
- GetInductionInfo(neg, 0).c_str());
+ GetInductionInfo(neg1, 0).c_str());
+ EXPECT_STREQ("((2) * i + (0)):PrimInt", GetInductionInfo(shl2, 0).c_str());
+ EXPECT_STREQ("(( - (2)) * i + (0)):PrimInt", GetInductionInfo(neg2, 0).c_str());
}
TEST_F(InductionVarAnalysisTest, FindPeriodicInduction) {
@@ -470,15 +631,15 @@
// k = d;
// }
BuildLoopNest(1);
- HPhi* k = InsertLoopPhi(0, 0);
- k->AddInput(constant0_);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constant0_);
HPhi* t = InsertLoopPhi(1, 0);
t->AddInput(constant100_);
- HInstruction* store1 = InsertArrayStore(k, 0);
+ HInstruction* store1 = InsertArrayStore(k_header, 0);
HInstruction* store2 = InsertArrayStore(t, 0);
- k->AddInput(t);
- t->AddInput(k);
+ k_header->AddInput(t);
+ t->AddInput(k_header);
PerformInductionVarAnalysis();
EXPECT_STREQ("periodic((0), (100)):PrimInt", GetInductionInfo(store1->InputAt(1), 0).c_str());
@@ -493,13 +654,13 @@
// k = 1 - k;
// }
BuildLoopNest(1);
- HPhi* k = InsertLoopPhi(0, 0);
- k->AddInput(constant0_);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constant0_);
- HInstruction* store = InsertArrayStore(k, 0);
+ HInstruction* store = InsertArrayStore(k_header, 0);
HInstruction* sub = InsertInstruction(
- new (&allocator_) HSub(Primitive::kPrimInt, constant1_, k), 0);
- k->AddInput(sub);
+ new (&allocator_) HSub(Primitive::kPrimInt, constant1_, k_header), 0);
+ k_header->AddInput(sub);
PerformInductionVarAnalysis();
EXPECT_STREQ("periodic((0), (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
@@ -514,13 +675,13 @@
// k = k ^ 1;
// }
BuildLoopNest(1);
- HPhi* k = InsertLoopPhi(0, 0);
- k->AddInput(constant0_);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constant0_);
- HInstruction* store = InsertArrayStore(k, 0);
+ HInstruction* store = InsertArrayStore(k_header, 0);
HInstruction* x = InsertInstruction(
- new (&allocator_) HXor(Primitive::kPrimInt, k, constant1_), 0);
- k->AddInput(x);
+ new (&allocator_) HXor(Primitive::kPrimInt, k_header, constant1_), 0);
+ k_header->AddInput(x);
PerformInductionVarAnalysis();
EXPECT_STREQ("periodic((0), (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
@@ -534,14 +695,15 @@
// k = 1 ^ k;
// }
BuildLoopNest(1);
- HPhi* k = InsertLoopPhi(0, 0);
- k->AddInput(constant1_);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constant1_);
HInstruction* x = InsertInstruction(
- new (&allocator_) HXor(Primitive::kPrimInt, constant1_, k), 0);
- k->AddInput(x);
+ new (&allocator_) HXor(Primitive::kPrimInt, constant1_, k_header), 0);
+ k_header->AddInput(x);
PerformInductionVarAnalysis();
+ EXPECT_STREQ("periodic((1), ((1) ^ (1))):PrimInt", GetInductionInfo(k_header, 0).c_str());
EXPECT_STREQ("periodic(((1) ^ (1)), (1)):PrimInt", GetInductionInfo(x, 0).c_str());
}
@@ -552,14 +714,15 @@
// k = k ^ 100;
// }
BuildLoopNest(1);
- HPhi* k = InsertLoopPhi(0, 0);
- k->AddInput(constant1_);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constant1_);
HInstruction* x = InsertInstruction(
- new (&allocator_) HXor(Primitive::kPrimInt, k, constant100_), 0);
- k->AddInput(x);
+ new (&allocator_) HXor(Primitive::kPrimInt, k_header, constant100_), 0);
+ k_header->AddInput(x);
PerformInductionVarAnalysis();
+ EXPECT_STREQ("periodic((1), ((1) ^ (100))):PrimInt", GetInductionInfo(k_header, 0).c_str());
EXPECT_STREQ("periodic(((1) ^ (100)), (1)):PrimInt", GetInductionInfo(x, 0).c_str());
}
@@ -570,13 +733,14 @@
// k = (k == 0);
// }
BuildLoopNest(1);
- HPhi* k = InsertLoopPhi(0, 0);
- k->AddInput(constant0_);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constant0_);
- HInstruction* x = InsertInstruction(new (&allocator_) HEqual(k, constant0_), 0);
- k->AddInput(x);
+ HInstruction* x = InsertInstruction(new (&allocator_) HEqual(k_header, constant0_), 0);
+ k_header->AddInput(x);
PerformInductionVarAnalysis();
+ EXPECT_STREQ("periodic((0), (1)):PrimBoolean", GetInductionInfo(k_header, 0).c_str());
EXPECT_STREQ("periodic((1), (0)):PrimBoolean", GetInductionInfo(x, 0).c_str());
}
@@ -587,13 +751,14 @@
// k = (0 == k);
// }
BuildLoopNest(1);
- HPhi* k = InsertLoopPhi(0, 0);
- k->AddInput(constant0_);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constant0_);
- HInstruction* x = InsertInstruction(new (&allocator_) HEqual(constant0_, k), 0);
- k->AddInput(x);
+ HInstruction* x = InsertInstruction(new (&allocator_) HEqual(constant0_, k_header), 0);
+ k_header->AddInput(x);
PerformInductionVarAnalysis();
+ EXPECT_STREQ("periodic((0), (1)):PrimBoolean", GetInductionInfo(k_header, 0).c_str());
EXPECT_STREQ("periodic((1), (0)):PrimBoolean", GetInductionInfo(x, 0).c_str());
}
@@ -604,13 +769,14 @@
// k = (k != 1);
// }
BuildLoopNest(1);
- HPhi* k = InsertLoopPhi(0, 0);
- k->AddInput(constant0_);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constant0_);
- HInstruction* x = InsertInstruction(new (&allocator_) HNotEqual(k, constant1_), 0);
- k->AddInput(x);
+ HInstruction* x = InsertInstruction(new (&allocator_) HNotEqual(k_header, constant1_), 0);
+ k_header->AddInput(x);
PerformInductionVarAnalysis();
+ EXPECT_STREQ("periodic((0), (1)):PrimBoolean", GetInductionInfo(k_header, 0).c_str());
EXPECT_STREQ("periodic((1), (0)):PrimBoolean", GetInductionInfo(x, 0).c_str());
}
@@ -621,13 +787,14 @@
// k = (1 != k);
// }
BuildLoopNest(1);
- HPhi* k = InsertLoopPhi(0, 0);
- k->AddInput(constant0_);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constant0_);
- HInstruction* x = InsertInstruction(new (&allocator_) HNotEqual(constant1_, k), 0);
- k->AddInput(x);
+ HInstruction* x = InsertInstruction(new (&allocator_) HNotEqual(constant1_, k_header), 0);
+ k_header->AddInput(x);
PerformInductionVarAnalysis();
+ EXPECT_STREQ("periodic((0), (1)):PrimBoolean", GetInductionInfo(k_header, 0).c_str());
EXPECT_STREQ("periodic((1), (0)):PrimBoolean", GetInductionInfo(x, 0).c_str());
}
@@ -635,6 +802,7 @@
// Setup:
// k = 0;
// for (int i = 0; i < 100; i++) {
+ // t = - k;
// k = 1 - k;
// t = k + 100;
// t = k - 100;
@@ -646,28 +814,31 @@
HPhi* k_header = InsertLoopPhi(0, 0);
k_header->AddInput(constant0_);
- HInstruction* k_body = InsertInstruction(
+ HInstruction* neg1 = InsertInstruction(
+ new (&allocator_) HNeg(Primitive::kPrimInt, k_header), 0);
+ HInstruction* idiom = InsertInstruction(
new (&allocator_) HSub(Primitive::kPrimInt, constant1_, k_header), 0);
- k_header->AddInput(k_body);
-
- // Derived expressions.
HInstruction* add = InsertInstruction(
- new (&allocator_) HAdd(Primitive::kPrimInt, k_body, constant100_), 0);
+ new (&allocator_) HAdd(Primitive::kPrimInt, idiom, constant100_), 0);
HInstruction* sub = InsertInstruction(
- new (&allocator_) HSub(Primitive::kPrimInt, k_body, constant100_), 0);
+ new (&allocator_) HSub(Primitive::kPrimInt, idiom, constant100_), 0);
HInstruction* mul = InsertInstruction(
- new (&allocator_) HMul(Primitive::kPrimInt, k_body, constant100_), 0);
+ new (&allocator_) HMul(Primitive::kPrimInt, idiom, constant100_), 0);
HInstruction* shl = InsertInstruction(
- new (&allocator_) HShl(Primitive::kPrimInt, k_body, constant1_), 0);
- HInstruction* neg = InsertInstruction(
- new (&allocator_) HNeg(Primitive::kPrimInt, k_body), 0);
+ new (&allocator_) HShl(Primitive::kPrimInt, idiom, constant1_), 0);
+ HInstruction* neg2 = InsertInstruction(
+ new (&allocator_) HNeg(Primitive::kPrimInt, idiom), 0);
+ k_header->AddInput(idiom);
PerformInductionVarAnalysis();
+ EXPECT_STREQ("periodic((0), (1)):PrimInt", GetInductionInfo(k_header, 0).c_str());
+ EXPECT_STREQ("periodic((0), ( - (1))):PrimInt", GetInductionInfo(neg1, 0).c_str());
+ EXPECT_STREQ("periodic((1), (0)):PrimInt", GetInductionInfo(idiom, 0).c_str());
EXPECT_STREQ("periodic(((1) + (100)), (100)):PrimInt", GetInductionInfo(add, 0).c_str());
EXPECT_STREQ("periodic(((1) - (100)), ((0) - (100))):PrimInt", GetInductionInfo(sub, 0).c_str());
EXPECT_STREQ("periodic((100), (0)):PrimInt", GetInductionInfo(mul, 0).c_str());
EXPECT_STREQ("periodic((2), (0)):PrimInt", GetInductionInfo(shl, 0).c_str());
- EXPECT_STREQ("periodic(( - (1)), (0)):PrimInt", GetInductionInfo(neg, 0).c_str());
+ EXPECT_STREQ("periodic(( - (1)), (0)):PrimInt", GetInductionInfo(neg2, 0).c_str());
}
TEST_F(InductionVarAnalysisTest, FindDeepLoopInduction) {
@@ -683,18 +854,18 @@
// }
BuildLoopNest(10);
- HPhi* k[10];
+ HPhi* k_header[10];
for (int d = 0; d < 10; d++) {
- k[d] = InsertLoopPhi(0, d);
+ k_header[d] = InsertLoopPhi(0, d);
}
HInstruction* inc = InsertInstruction(
- new (&allocator_) HAdd(Primitive::kPrimInt, constant1_, k[9]), 9);
+ new (&allocator_) HAdd(Primitive::kPrimInt, constant1_, k_header[9]), 9);
HInstruction* store = InsertArrayStore(inc, 9);
for (int d = 0; d < 10; d++) {
- k[d]->AddInput((d != 0) ? k[d - 1] : constant0_);
- k[d]->AddInput((d != 9) ? k[d + 1] : inc);
+ k_header[d]->AddInput((d != 0) ? k_header[d - 1] : constant0_);
+ k_header[d]->AddInput((d != 9) ? k_header[d + 1] : inc);
}
PerformInductionVarAnalysis();
diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc
index 235793d..75619a3 100644
--- a/compiler/optimizing/induction_var_range.cc
+++ b/compiler/optimizing/induction_var_range.cc
@@ -57,6 +57,21 @@
return false;
}
+/** Returns b^e for b,e >= 1. */
+static int64_t IntPow(int64_t b, int64_t e) {
+ DCHECK_GE(b, 1);
+ DCHECK_GE(e, 1);
+ int64_t pow = 1;
+ while (e) {
+ if (e & 1) {
+ pow *= b;
+ }
+ e >>= 1;
+ b *= b;
+ }
+ return pow;
+}
+
/**
* Detects an instruction that is >= 0. As long as the value is carried by
* a single instruction, arithmetic wrap-around cannot occur.
@@ -399,6 +414,8 @@
/*out*/ HLoopInformation** loop,
/*out*/ HInductionVarAnalysis::InductionInfo** info,
/*out*/ HInductionVarAnalysis::InductionInfo** trip) const {
+ DCHECK(context != nullptr);
+ DCHECK(context->GetBlock() != nullptr);
HLoopInformation* lp = context->GetBlock()->GetLoopInformation(); // closest enveloping loop
if (lp != nullptr) {
HInductionVarAnalysis::InductionInfo* i = induction_analysis_->LookupInfo(lp, instruction);
@@ -474,6 +491,8 @@
HInductionVarAnalysis::InductionInfo* trip,
bool in_body,
bool is_min) const {
+ DCHECK(info != nullptr);
+ DCHECK(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
@@ -520,6 +539,30 @@
GetVal(info->op_b, trip, in_body, is_min));
}
+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);
+ 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.
+ 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));
+ }
+ }
+ return Value();
+}
+
InductionVarRange::Value InductionVarRange::GetFetch(HInstruction* instruction,
HInductionVarAnalysis::InductionInfo* trip,
bool in_body,
@@ -631,6 +674,10 @@
break;
case HInductionVarAnalysis::kLinear:
return CorrectForType(GetLinear(info, trip, in_body, is_min), info->type);
+ case HInductionVarAnalysis::kPolynomial:
+ break;
+ case HInductionVarAnalysis::kGeometric:
+ return GetGeometric(info, trip, in_body, is_min);
case HInductionVarAnalysis::kWrapAround:
case HInductionVarAnalysis::kPeriodic:
return MergeVal(GetVal(info->op_a, trip, in_body, is_min),
@@ -842,17 +889,21 @@
*needs_taken_test = IsBodyTripCount(trip);
// Handle last value request.
if (is_last_value) {
- if (info->induction_class == HInductionVarAnalysis::kLinear) {
- if (*stride_value > 0) {
- lower = nullptr;
- } else {
- upper = nullptr;
- }
- } else if (info->induction_class == HInductionVarAnalysis::kPeriodic) {
- DCHECK(!in_body);
- return GenerateLastValuePeriodic(info, trip, graph, block, lower, needs_taken_test);
- } else {
- return false;
+ DCHECK(!in_body);
+ switch (info->induction_class) {
+ case HInductionVarAnalysis::kLinear:
+ if (*stride_value > 0) {
+ lower = nullptr;
+ } else {
+ upper = nullptr;
+ }
+ break;
+ case HInductionVarAnalysis::kGeometric:
+ return GenerateLastValueGeometric(info, trip, graph, block, lower);
+ case HInductionVarAnalysis::kPeriodic:
+ return GenerateLastValuePeriodic(info, trip, graph, block, lower, needs_taken_test);
+ default:
+ return false;
}
}
// Code generation for taken test: generate the code when requested or otherwise analyze
@@ -874,12 +925,68 @@
GenerateCode(info, trip, graph, block, upper, in_body, /* is_min */ 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);
+ // Detect known base and trip count (always taken).
+ int64_t f = 0;
+ int64_t t = 0;
+ if (IsIntAndGet(info->fetch, &f) && f >= 1 && IsConstant(trip->op_a, kExact, &t) && t >= 1) {
+ HInstruction* opa = nullptr;
+ 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) {
+ DCHECK(info->type == Primitive::kPrimInt); // due to codegen, generalize?
+ if (fpowt == 0) {
+ // Special case: repeated mul/div always yields zero.
+ *result = graph->GetIntConstant(0);
+ } else if (info->operation == HInductionVarAnalysis::kMul) {
+ // Last value multiplication: a * f ^ t + b.
+ HInstruction* mul = Insert(block,
+ new (graph->GetArena()) HMul(info->type,
+ opa,
+ graph->GetIntConstant(fpowt)));
+ *result = Insert(block, new (graph->GetArena()) HAdd(info->type, mul, opb));
+ } else {
+ // Last value multiplication: a * f ^ -t + b.
+ DCHECK_EQ(info->operation, HInductionVarAnalysis::kDiv);
+ HInstruction* div = Insert(block,
+ new (graph->GetArena()) HDiv(info->type,
+ opa,
+ graph->GetIntConstant(fpowt),
+ kNoDexPc));
+ *result = Insert(block, new (graph->GetArena()) HAdd(info->type, div, opb));
+ }
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
bool InductionVarRange::GenerateLastValuePeriodic(HInductionVarAnalysis::InductionInfo* info,
HInductionVarAnalysis::InductionInfo* trip,
HGraph* graph,
HBasicBlock* block,
/*out*/HInstruction** result,
/*out*/bool* needs_taken_test) const {
+ DCHECK(info != nullptr);
DCHECK(info->induction_class == HInductionVarAnalysis::kPeriodic);
// Count period.
int32_t period = 1;
@@ -937,6 +1044,7 @@
return true;
}
// Verify type safety.
+ // TODO: generalize
Primitive::Type type = Primitive::kPrimInt;
if (info->type != Primitive::kPrimInt && info->type != Primitive::kPrimBoolean) {
return false;
@@ -1058,6 +1166,9 @@
}
break;
}
+ case HInductionVarAnalysis::kPolynomial:
+ case HInductionVarAnalysis::kGeometric:
+ break;
case HInductionVarAnalysis::kWrapAround:
case HInductionVarAnalysis::kPeriodic: {
// Wrap-around and periodic inductions are restricted to constants only, so that extreme
@@ -1071,8 +1182,6 @@
}
break;
}
- default:
- break;
}
}
return false;
diff --git a/compiler/optimizing/induction_var_range.h b/compiler/optimizing/induction_var_range.h
index 034cf32..f7360e8 100644
--- a/compiler/optimizing/induction_var_range.h
+++ b/compiler/optimizing/induction_var_range.h
@@ -190,6 +190,10 @@
HInductionVarAnalysis::InductionInfo* trip,
bool in_body,
bool is_min) const;
+ Value GetGeometric(HInductionVarAnalysis::InductionInfo* info,
+ HInductionVarAnalysis::InductionInfo* trip,
+ bool in_body,
+ bool is_min) const;
Value GetFetch(HInstruction* instruction,
HInductionVarAnalysis::InductionInfo* trip,
bool in_body,
@@ -245,6 +249,12 @@
/*out*/ bool* needs_finite_test,
/*out*/ bool* needs_taken_test) const;
+ bool GenerateLastValueGeometric(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 4c99e3c..510841e 100644
--- a/compiler/optimizing/induction_var_range_test.cc
+++ b/compiler/optimizing/induction_var_range_test.cc
@@ -170,22 +170,46 @@
/** Constructs a linear a * i + b induction. */
HInductionVarAnalysis::InductionInfo* CreateLinear(int32_t a, int32_t b) {
- return iva_->CreateInduction(
- HInductionVarAnalysis::kLinear, CreateConst(a), CreateConst(b), Primitive::kPrimInt);
+ return iva_->CreateInduction(HInductionVarAnalysis::kLinear,
+ HInductionVarAnalysis::kNop,
+ CreateConst(a),
+ CreateConst(b),
+ 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,
+ CreateConst(a),
+ CreateConst(b),
+ graph_->GetIntConstant(f),
+ Primitive::kPrimInt);
}
/** Constructs a range [lo, hi] using a periodic induction. */
HInductionVarAnalysis::InductionInfo* CreateRange(int32_t lo, int32_t hi) {
- return iva_->CreateInduction(
- HInductionVarAnalysis::kPeriodic, CreateConst(lo), CreateConst(hi), Primitive::kPrimInt);
+ return iva_->CreateInduction(HInductionVarAnalysis::kPeriodic,
+ HInductionVarAnalysis::kNop,
+ CreateConst(lo),
+ CreateConst(hi),
+ nullptr,
+ Primitive::kPrimInt);
}
/** Constructs a wrap-around induction consisting of a constant, followed info */
HInductionVarAnalysis::InductionInfo* CreateWrapAround(
int32_t initial,
HInductionVarAnalysis::InductionInfo* info) {
- return iva_->CreateInduction(
- HInductionVarAnalysis::kWrapAround, CreateConst(initial), info, Primitive::kPrimInt);
+ return iva_->CreateInduction(HInductionVarAnalysis::kWrapAround,
+ HInductionVarAnalysis::kNop,
+ CreateConst(initial),
+ info,
+ nullptr,
+ Primitive::kPrimInt);
}
/** Constructs a wrap-around induction consisting of a constant, followed by a range. */
@@ -414,6 +438,33 @@
ExpectEqual(Value(20), GetMax(CreateWrapAround(20, -1, 10), nullptr));
}
+TEST_F(InductionVarRangeTest, GetMinMaxGeometricMul) {
+ ExpectEqual(Value(), GetMin(CreateGeometric(1, 1, 1, '*'), nullptr));
+ ExpectEqual(Value(), GetMax(CreateGeometric(1, 1, 1, '*'), nullptr));
+}
+
+TEST_F(InductionVarRangeTest, GetMinMaxGeometricDiv) {
+ ExpectEqual(Value(5), GetMin(CreateGeometric(11, 5, 3, '/'), nullptr));
+ ExpectEqual(Value(16), GetMax(CreateGeometric(11, 5, 3, '/'), nullptr));
+ ExpectEqual(Value(-5), 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(5), GetMax(CreateGeometric(-11, 5, 3, '/'), nullptr));
+ ExpectEqual(Value(-16), GetMin(CreateGeometric(-11, -5, 3, '/'), nullptr));
+ 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));
diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc
index 9e72447..433dced 100644
--- a/compiler/optimizing/intrinsics_arm_vixl.cc
+++ b/compiler/optimizing/intrinsics_arm_vixl.cc
@@ -71,7 +71,7 @@
: SlowPathCodeARMVIXL(invoke), invoke_(invoke) {}
Location MoveArguments(CodeGenerator* codegen) {
- InvokeDexCallingConventionVisitorARM calling_convention_visitor;
+ InvokeDexCallingConventionVisitorARMVIXL calling_convention_visitor;
IntrinsicVisitor::MoveArguments(invoke_, codegen, &calling_convention_visitor);
return calling_convention_visitor.GetMethodLocation();
}
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index e15e33d..8ea2b06 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -630,10 +630,8 @@
#if defined(ART_ENABLE_CODEGEN_arm)
case kThumb2:
case kArm: {
-#ifndef ART_USE_VIXL_ARM_BACKEND
arm::DexCacheArrayFixups* fixups =
new (arena) arm::DexCacheArrayFixups(graph, codegen, stats);
-#endif
arm::InstructionSimplifierArm* simplifier =
new (arena) arm::InstructionSimplifierArm(graph, stats);
SideEffectsAnalysis* side_effects = new (arena) SideEffectsAnalysis(graph);
@@ -642,9 +640,7 @@
simplifier,
side_effects,
gvn,
-#ifndef ART_USE_VIXL_ARM_BACKEND
fixups
-#endif
};
RunOptimizations(arm_optimizations, arraysize(arm_optimizations), pass_observer);
break;
diff --git a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
index fb6f172..2d026b8 100644
--- a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
+++ b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
@@ -428,8 +428,6 @@
UNIMPLEMENTED(FATAL);
}
-static constexpr uint32_t kArmInstrMaxSizeInBytes = 4;
-
void ArmVIXLJNIMacroAssembler::CreateHandleScopeEntry(ManagedRegister mout_reg,
FrameOffset handle_scope_offset,
ManagedRegister min_reg,
@@ -458,14 +456,14 @@
if (asm_.ShifterOperandCanHold(ADD, handle_scope_offset.Int32Value(), kCcDontCare)) {
if (!out_reg.Equals(in_reg)) {
AssemblerAccurateScope guard(asm_.GetVIXLAssembler(),
- 3 * kArmInstrMaxSizeInBytes,
+ 3 * vixl32::kMaxInstructionSizeInBytes,
CodeBufferCheckScope::kMaximumSize);
___ it(eq, 0xc);
___ mov(eq, out_reg.AsVIXLRegister(), 0);
asm_.AddConstantInIt(out_reg.AsVIXLRegister(), sp, handle_scope_offset.Int32Value(), ne);
} else {
AssemblerAccurateScope guard(asm_.GetVIXLAssembler(),
- 2 * kArmInstrMaxSizeInBytes,
+ 2 * vixl32::kMaxInstructionSizeInBytes,
CodeBufferCheckScope::kMaximumSize);
___ it(ne, 0x8);
asm_.AddConstantInIt(out_reg.AsVIXLRegister(), sp, handle_scope_offset.Int32Value(), ne);
@@ -496,7 +494,7 @@
if (asm_.ShifterOperandCanHold(ADD, handle_scope_offset.Int32Value(), kCcDontCare)) {
AssemblerAccurateScope guard(asm_.GetVIXLAssembler(),
- 2 * kArmInstrMaxSizeInBytes,
+ 2 * vixl32::kMaxInstructionSizeInBytes,
CodeBufferCheckScope::kMaximumSize);
___ it(ne, 0x8);
asm_.AddConstantInIt(scratch.AsVIXLRegister(), sp, handle_scope_offset.Int32Value(), ne);
@@ -589,7 +587,7 @@
___ Cmp(scratch.AsVIXLRegister(), 0);
{
AssemblerAccurateScope guard(asm_.GetVIXLAssembler(),
- kArmInstrMaxSizeInBytes,
+ vixl32::kMaxInstructionSizeInBytes,
CodeBufferCheckScope::kMaximumSize);
___ b(ne, Narrow, exception_blocks_.back()->Entry());
}
diff --git a/compiler/utils/atomic_method_ref_map-inl.h b/compiler/utils/atomic_method_ref_map-inl.h
index 70ea028..d71c2fe 100644
--- a/compiler/utils/atomic_method_ref_map-inl.h
+++ b/compiler/utils/atomic_method_ref_map-inl.h
@@ -78,6 +78,15 @@
}
}
+template <typename T>
+inline void AtomicMethodRefMap<T>::ClearEntries() {
+ for (auto& it : arrays_) {
+ for (auto& element : it.second) {
+ element.StoreRelaxed(nullptr);
+ }
+ }
+}
+
} // namespace art
#endif // ART_COMPILER_UTILS_ATOMIC_METHOD_REF_MAP_INL_H_
diff --git a/compiler/utils/atomic_method_ref_map.h b/compiler/utils/atomic_method_ref_map.h
index 11ab211..fed848f 100644
--- a/compiler/utils/atomic_method_ref_map.h
+++ b/compiler/utils/atomic_method_ref_map.h
@@ -55,6 +55,8 @@
template <typename Visitor>
void Visit(const Visitor& visitor);
+ void ClearEntries();
+
private:
// Verified methods. The method array is fixed to avoid needing a lock to extend it.
using ElementArray = dchecked_vector<Atomic<T>>;
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index 90fe6da..52765f9 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -19,8 +19,11 @@
#include "class_linker.h"
#include "compiler/common_compiler_test.h"
+#include "compiler/dex/verification_results.h"
+#include "compiler/dex/verified_method.h"
#include "compiler/driver/compiler_options.h"
#include "compiler/driver/compiler_driver.h"
+#include "compiler/utils/atomic_method_ref_map-inl.h"
#include "compiler_callbacks.h"
#include "dex_file.h"
#include "dex_file_types.h"
@@ -90,6 +93,14 @@
verifier_deps_.reset(callbacks_->GetVerifierDeps());
}
callbacks_->SetVerifierDeps(nullptr);
+ // Clear entries in the verification results to avoid hitting a DCHECK that
+ // we always succeed inserting a new entry after verifying.
+ AtomicMethodRefMap<const VerifiedMethod*>* map =
+ &compiler_driver_->GetVerificationResults()->atomic_verified_methods_;
+ map->Visit([](const MethodReference& ref ATTRIBUTE_UNUSED, const VerifiedMethod* method) {
+ delete method;
+ });
+ map->ClearEntries();
}
void SetVerifierDeps(const std::vector<const DexFile*>& dex_files) {
@@ -112,6 +123,9 @@
for (const DexFile* dex_file : dex_files_) {
class_linker_->RegisterDexFile(*dex_file, loader.Get());
}
+ for (const DexFile* dex_file : dex_files_) {
+ compiler_driver_->GetVerificationResults()->AddDexFile(dex_file);
+ }
}
void LoadDexFile(ScopedObjectAccess* soa) REQUIRES_SHARED(Locks::mutator_lock_) {
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 77d799f..96b6f18 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -79,7 +79,8 @@
DCHECK(ext->GetObsoleteDexCaches() != nullptr);
int32_t len = obsolete_methods->GetLength();
DCHECK_EQ(len, ext->GetObsoleteDexCaches()->GetLength());
- // TODO I think this is fine since images should never have obsolete methods in them.
+ // Using kRuntimePointerSize (instead of using the image's pointer size) is fine since images
+ // should never have obsolete methods in them so they should always be the same.
PointerSize pointer_size = kRuntimePointerSize;
DCHECK_EQ(kRuntimePointerSize, Runtime::Current()->GetClassLinker()->GetImagePointerSize());
for (int32_t i = 0; i < len; i++) {
diff --git a/runtime/barrier.cc b/runtime/barrier.cc
index 0d842cc..9bcda35 100644
--- a/runtime/barrier.cc
+++ b/runtime/barrier.cc
@@ -80,6 +80,11 @@
return timed_out;
}
+int Barrier::GetCount(Thread* self) {
+ MutexLock mu(self, lock_);
+ return count_;
+}
+
void Barrier::SetCountLocked(Thread* self, int count) {
count_ = count;
if (count == 0) {
diff --git a/runtime/barrier.h b/runtime/barrier.h
index 94977fb..d7c4661 100644
--- a/runtime/barrier.h
+++ b/runtime/barrier.h
@@ -61,6 +61,8 @@
// another thread is still in Wait(). See above.
void Init(Thread* self, int count) REQUIRES(!lock_);
+ int GetCount(Thread* self) REQUIRES(!lock_);
+
private:
void SetCountLocked(Thread* self, int count) REQUIRES(lock_);
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 992d4bf..674bad7 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -242,10 +242,11 @@
ScopedLocalRef<jthrowable> cause(env, env->ExceptionOccurred());
CHECK(cause.get() != nullptr);
- // Boot classpath classes should not fail initialization.
- if (!Runtime::Current()->IsAotCompiler()) {
+ // Boot classpath classes should not fail initialization. This is a sanity debug check. This
+ // cannot in general be guaranteed, but in all likelihood leads to breakage down the line.
+ if (klass->GetClassLoader() == nullptr && !Runtime::Current()->IsAotCompiler()) {
std::string tmp;
- CHECK(klass->GetClassLoader() != nullptr) << klass->GetDescriptor(&tmp);
+ LOG(kIsDebugBuild ? FATAL : WARNING) << klass->GetDescriptor(&tmp) << " failed initialization";
}
env->ExceptionClear();
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 19ee0fb..fbab73f 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -812,13 +812,13 @@
false_gray_stack_.clear();
}
-
void ConcurrentCopying::IssueEmptyCheckpoint() {
Thread* self = Thread::Current();
ThreadList* thread_list = Runtime::Current()->GetThreadList();
Barrier* barrier = thread_list->EmptyCheckpointBarrier();
barrier->Init(self, 0);
- size_t barrier_count = thread_list->RunEmptyCheckpoint();
+ std::vector<uint32_t> runnable_thread_ids; // Used in debug build only
+ size_t barrier_count = thread_list->RunEmptyCheckpoint(runnable_thread_ids);
// If there are no threads to wait which implys that all the checkpoint functions are finished,
// then no need to release the mutator lock.
if (barrier_count == 0) {
@@ -828,7 +828,27 @@
Locks::mutator_lock_->SharedUnlock(self);
{
ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun);
- barrier->Increment(self, barrier_count);
+ if (kIsDebugBuild) {
+ static constexpr uint64_t kEmptyCheckpointTimeoutMs = 600 * 1000; // 10 minutes.
+ bool timed_out = barrier->Increment(self, barrier_count, kEmptyCheckpointTimeoutMs);
+ if (timed_out) {
+ Runtime* runtime = Runtime::Current();
+ std::ostringstream ss;
+ ss << "Empty checkpoint timeout\n";
+ ss << "Barrier count " << barrier->GetCount(self) << "\n";
+ ss << "Runnable thread IDs";
+ for (uint32_t tid : runnable_thread_ids) {
+ ss << " " << tid;
+ }
+ ss << "\n";
+ Locks::mutator_lock_->Dump(ss);
+ ss << "\n";
+ runtime->GetThreadList()->Dump(ss);
+ LOG(FATAL) << ss.str();
+ }
+ } else {
+ barrier->Increment(self, barrier_count);
+ }
}
Locks::mutator_lock_->SharedLock(self);
}
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 8ff5e5a..3ed138c 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -294,66 +294,15 @@
}
// Load image space(s).
- if (!image_file_name.empty()) {
- // For code reuse, handle this like a work queue.
- std::vector<std::string> image_file_names;
- image_file_names.push_back(image_file_name);
- // The loaded spaces. Secondary images may fail to load, in which case we need to remove
- // already added spaces.
- std::vector<space::Space*> added_image_spaces;
- uint8_t* const original_requested_alloc_space_begin = requested_alloc_space_begin;
- for (size_t index = 0; index < image_file_names.size(); ++index) {
- std::string& image_name = image_file_names[index];
- std::string error_msg;
- std::unique_ptr<space::ImageSpace> boot_image_space_uptr = space::ImageSpace::CreateBootImage(
- image_name.c_str(),
- image_instruction_set,
- index > 0,
- &error_msg);
- if (boot_image_space_uptr != nullptr) {
- space::ImageSpace* boot_image_space = boot_image_space_uptr.release();
- AddSpace(boot_image_space);
- added_image_spaces.push_back(boot_image_space);
- // Oat files referenced by image files immediately follow them in memory, ensure alloc space
- // isn't going to get in the middle
- uint8_t* oat_file_end_addr = boot_image_space->GetImageHeader().GetOatFileEnd();
- CHECK_GT(oat_file_end_addr, boot_image_space->End());
- requested_alloc_space_begin = AlignUp(oat_file_end_addr, kPageSize);
- boot_image_spaces_.push_back(boot_image_space);
-
- if (index == 0) {
- // If this was the first space, check whether there are more images to load.
- const OatFile* boot_oat_file = boot_image_space->GetOatFile();
- if (boot_oat_file == nullptr) {
- continue;
- }
-
- const OatHeader& boot_oat_header = boot_oat_file->GetOatHeader();
- const char* boot_classpath =
- boot_oat_header.GetStoreValueByKey(OatHeader::kBootClassPathKey);
- if (boot_classpath == nullptr) {
- continue;
- }
-
- space::ImageSpace::ExtractMultiImageLocations(image_file_name,
- boot_classpath,
- &image_file_names);
- }
- } else {
- LOG(ERROR) << "Could not create image space with image file '" << image_file_name << "'. "
- << "Attempting to fall back to imageless running. Error was: " << error_msg
- << "\nAttempted image: " << image_name;
- // Remove already loaded spaces.
- for (space::Space* loaded_space : added_image_spaces) {
- RemoveSpace(loaded_space);
- delete loaded_space;
- }
- boot_image_spaces_.clear();
- requested_alloc_space_begin = original_requested_alloc_space_begin;
- break;
- }
+ if (space::ImageSpace::LoadBootImage(image_file_name,
+ image_instruction_set,
+ &boot_image_spaces_,
+ &requested_alloc_space_begin)) {
+ for (auto space : boot_image_spaces_) {
+ AddSpace(space);
}
}
+
/*
requested_alloc_space_begin -> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
+- nonmoving space (non_moving_space_capacity)+-
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 6019540..c726944 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -1617,6 +1617,81 @@
return nullptr;
}
+bool ImageSpace::LoadBootImage(const std::string& image_file_name,
+ const InstructionSet image_instruction_set,
+ std::vector<space::ImageSpace*>* boot_image_spaces,
+ uint8_t** oat_file_end) {
+ DCHECK(boot_image_spaces != nullptr);
+ DCHECK(boot_image_spaces->empty());
+ DCHECK(oat_file_end != nullptr);
+ DCHECK_NE(image_instruction_set, InstructionSet::kNone);
+
+ if (image_file_name.empty()) {
+ return false;
+ }
+
+ // For code reuse, handle this like a work queue.
+ std::vector<std::string> image_file_names;
+ image_file_names.push_back(image_file_name);
+
+ bool error = false;
+ uint8_t* oat_file_end_tmp = *oat_file_end;
+
+ for (size_t index = 0; index < image_file_names.size(); ++index) {
+ std::string& image_name = image_file_names[index];
+ std::string error_msg;
+ std::unique_ptr<space::ImageSpace> boot_image_space_uptr = CreateBootImage(
+ image_name.c_str(),
+ image_instruction_set,
+ index > 0,
+ &error_msg);
+ if (boot_image_space_uptr != nullptr) {
+ space::ImageSpace* boot_image_space = boot_image_space_uptr.release();
+ boot_image_spaces->push_back(boot_image_space);
+ // Oat files referenced by image files immediately follow them in memory, ensure alloc space
+ // isn't going to get in the middle
+ uint8_t* oat_file_end_addr = boot_image_space->GetImageHeader().GetOatFileEnd();
+ CHECK_GT(oat_file_end_addr, boot_image_space->End());
+ oat_file_end_tmp = AlignUp(oat_file_end_addr, kPageSize);
+
+ if (index == 0) {
+ // If this was the first space, check whether there are more images to load.
+ const OatFile* boot_oat_file = boot_image_space->GetOatFile();
+ if (boot_oat_file == nullptr) {
+ continue;
+ }
+
+ const OatHeader& boot_oat_header = boot_oat_file->GetOatHeader();
+ const char* boot_classpath =
+ boot_oat_header.GetStoreValueByKey(OatHeader::kBootClassPathKey);
+ if (boot_classpath == nullptr) {
+ continue;
+ }
+
+ ExtractMultiImageLocations(image_file_name, boot_classpath, &image_file_names);
+ }
+ } else {
+ error = true;
+ LOG(ERROR) << "Could not create image space with image file '" << image_file_name << "'. "
+ << "Attempting to fall back to imageless running. Error was: " << error_msg
+ << "\nAttempted image: " << image_name;
+ break;
+ }
+ }
+
+ if (error) {
+ // Remove already loaded spaces.
+ for (space::Space* loaded_space : *boot_image_spaces) {
+ delete loaded_space;
+ }
+ boot_image_spaces->clear();
+ return false;
+ }
+
+ *oat_file_end = oat_file_end_tmp;
+ return true;
+}
+
std::unique_ptr<ImageSpace> ImageSpace::CreateFromAppImage(const char* image,
const OatFile* oat_file,
std::string* error_msg) {
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index 0ba131b..489a289 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -17,6 +17,7 @@
#ifndef ART_RUNTIME_GC_SPACE_IMAGE_SPACE_H_
#define ART_RUNTIME_GC_SPACE_IMAGE_SPACE_H_
+#include "arch/instruction_set.h"
#include "gc/accounting/space_bitmap.h"
#include "runtime.h"
#include "space.h"
@@ -35,18 +36,15 @@
return kSpaceTypeImageSpace;
}
- // Create a boot image space from an image file for a specified instruction
- // set. Cannot be used for future allocation or collected.
+ // Load boot image spaces from a primary image file for a specified instruction set.
//
- // Create also opens the OatFile associated with the image file so
- // that it be contiguously allocated with the image before the
- // creation of the alloc space. The ReleaseOatFile will later be
- // used to transfer ownership of the OatFile to the ClassLinker when
- // it is initialized.
- static std::unique_ptr<ImageSpace> CreateBootImage(const char* image,
- InstructionSet image_isa,
- bool secondary_image,
- std::string* error_msg)
+ // On successful return, the loaded spaces are added to boot_image_spaces (which must be
+ // empty on entry) and oat_file_end is updated with the (page-aligned) end of the last
+ // oat file.
+ static bool LoadBootImage(const std::string& image_file_name,
+ const InstructionSet image_instruction_set,
+ std::vector<space::ImageSpace*>* boot_image_spaces,
+ uint8_t** oat_file_end)
REQUIRES_SHARED(Locks::mutator_lock_);
// Try to open an existing app image space.
@@ -189,6 +187,20 @@
friend class Space;
private:
+ // Create a boot image space from an image file for a specified instruction
+ // set. Cannot be used for future allocation or collected.
+ //
+ // Create also opens the OatFile associated with the image file so
+ // that it be contiguously allocated with the image before the
+ // creation of the alloc space. The ReleaseOatFile will later be
+ // used to transfer ownership of the OatFile to the ClassLinker when
+ // it is initialized.
+ static std::unique_ptr<ImageSpace> CreateBootImage(const char* image,
+ InstructionSet image_isa,
+ bool secondary_image,
+ std::string* error_msg)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
DISALLOW_COPY_AND_ASSIGN(ImageSpace);
};
diff --git a/runtime/interpreter/mterp/arm/entry.S b/runtime/interpreter/mterp/arm/entry.S
index a6b131d..e53c054 100644
--- a/runtime/interpreter/mterp/arm/entry.S
+++ b/runtime/interpreter/mterp/arm/entry.S
@@ -31,10 +31,19 @@
*
*/
-ExecuteMterpImpl:
- .fnstart
- .save {r3-r10,fp,lr}
+ENTRY ExecuteMterpImpl
stmfd sp!, {r3-r10,fp,lr} @ save 10 regs, (r3 just to align 64)
+ .cfi_adjust_cfa_offset 40
+ .cfi_rel_offset r3, 0
+ .cfi_rel_offset r4, 4
+ .cfi_rel_offset r5, 8
+ .cfi_rel_offset r6, 12
+ .cfi_rel_offset r7, 16
+ .cfi_rel_offset r8, 20
+ .cfi_rel_offset r9, 24
+ .cfi_rel_offset r10, 28
+ .cfi_rel_offset fp, 32
+ .cfi_rel_offset lr, 36
/* Remember the return register */
str r3, [r2, #SHADOWFRAME_RESULT_REGISTER_OFFSET]
diff --git a/runtime/interpreter/mterp/arm/footer.S b/runtime/interpreter/mterp/arm/footer.S
index cd32ea2..c6801e5 100644
--- a/runtime/interpreter/mterp/arm/footer.S
+++ b/runtime/interpreter/mterp/arm/footer.S
@@ -294,6 +294,5 @@
mov r0, rINST @ restore return value
ldmfd sp!, {r3-r10,fp,pc} @ restore 10 regs and return
- .fnend
- .size ExecuteMterpImpl, .-ExecuteMterpImpl
+ END ExecuteMterpImpl
diff --git a/runtime/interpreter/mterp/arm/header.S b/runtime/interpreter/mterp/arm/header.S
index 039bcbe..597d9d4 100644
--- a/runtime/interpreter/mterp/arm/header.S
+++ b/runtime/interpreter/mterp/arm/header.S
@@ -287,3 +287,24 @@
.macro REFRESH_IBASE
ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET]
.endm
+
+/*
+ * cfi support macros.
+ */
+.macro ENTRY name
+ .arm
+ .type \name, #function
+ .hidden \name // Hide this as a global symbol, so we do not incur plt calls.
+ .global \name
+ /* Cache alignment for function entry */
+ .balign 16
+\name:
+ .cfi_startproc
+ .fnstart
+.endm
+
+.macro END name
+ .fnend
+ .cfi_endproc
+ .size \name, .-\name
+.endm
diff --git a/runtime/interpreter/mterp/arm/op_double_to_long.S b/runtime/interpreter/mterp/arm/op_double_to_long.S
index b100810..19ff723 100644
--- a/runtime/interpreter/mterp/arm/op_double_to_long.S
+++ b/runtime/interpreter/mterp/arm/op_double_to_long.S
@@ -1,4 +1,3 @@
-@include "arm/unopWide.S" {"instr":"bl __aeabi_d2lz"}
%include "arm/unopWide.S" {"instr":"bl d2l_doconv"}
%break
@@ -10,43 +9,25 @@
* to modest integer. The EABI convert function isn't doing this for us.
*/
d2l_doconv:
- stmfd sp!, {r4, r5, lr} @ save regs
- mov r3, #0x43000000 @ maxlong, as a double (high word)
- add r3, #0x00e00000 @ 0x43e00000
- mov r2, #0 @ maxlong, as a double (low word)
- sub sp, sp, #4 @ align for EABI
- mov r4, r0 @ save a copy of r0
- mov r5, r1 @ and r1
- bl __aeabi_dcmpge @ is arg >= maxlong?
- cmp r0, #0 @ nonzero == yes
- mvnne r0, #0 @ return maxlong (7fffffffffffffff)
- mvnne r1, #0x80000000
- bne 1f
-
- mov r0, r4 @ recover arg
- mov r1, r5
- mov r3, #0xc3000000 @ minlong, as a double (high word)
- add r3, #0x00e00000 @ 0xc3e00000
- mov r2, #0 @ minlong, as a double (low word)
- bl __aeabi_dcmple @ is arg <= minlong?
- cmp r0, #0 @ nonzero == yes
- movne r0, #0 @ return minlong (8000000000000000)
- movne r1, #0x80000000
- bne 1f
-
- mov r0, r4 @ recover arg
- mov r1, r5
- mov r2, r4 @ compare against self
- mov r3, r5
- bl __aeabi_dcmpeq @ is arg == self?
- cmp r0, #0 @ zero == no
- moveq r1, #0 @ return zero for NaN
- beq 1f
-
- mov r0, r4 @ recover arg
- mov r1, r5
- bl __aeabi_d2lz @ convert double to long
-
-1:
- add sp, sp, #4
- ldmfd sp!, {r4, r5, pc}
+ ubfx r2, r1, #20, #11 @ grab the exponent
+ movw r3, #0x43e
+ cmp r2, r3 @ MINLONG < x > MAXLONG?
+ bhs d2l_special_cases
+ b __aeabi_d2lz @ tail call to convert double to long
+d2l_special_cases:
+ movw r3, #0x7ff
+ cmp r2, r3
+ beq d2l_maybeNaN @ NaN?
+d2l_notNaN:
+ adds r1, r1, r1 @ sign bit to carry
+ mov r0, #0xffffffff @ assume maxlong for lsw
+ mov r1, #0x7fffffff @ assume maxlong for msw
+ adc r0, r0, #0
+ adc r1, r1, #0 @ convert maxlong to minlong if exp negative
+ bx lr @ return
+d2l_maybeNaN:
+ orrs r3, r0, r1, lsl #12
+ beq d2l_notNaN @ if fraction is non-zero, it's a NaN
+ mov r0, #0
+ mov r1, #0
+ bx lr @ return 0 for NaN
diff --git a/runtime/interpreter/mterp/arm/op_float_to_long.S b/runtime/interpreter/mterp/arm/op_float_to_long.S
index 5c8680f..1770ea0 100644
--- a/runtime/interpreter/mterp/arm/op_float_to_long.S
+++ b/runtime/interpreter/mterp/arm/op_float_to_long.S
@@ -1,4 +1,3 @@
-@include "arm/unopWider.S" {"instr":"bl __aeabi_f2lz"}
%include "arm/unopWider.S" {"instr":"bl f2l_doconv"}
%break
@@ -10,30 +9,23 @@
* to modest integer. The EABI convert function isn't doing this for us.
*/
f2l_doconv:
- stmfd sp!, {r4, lr}
- mov r1, #0x5f000000 @ (float)maxlong
- mov r4, r0
- bl __aeabi_fcmpge @ is arg >= maxlong?
- cmp r0, #0 @ nonzero == yes
- mvnne r0, #0 @ return maxlong (7fffffff)
- mvnne r1, #0x80000000
- popne {r4, pc}
-
- mov r0, r4 @ recover arg
- mov r1, #0xdf000000 @ (float)minlong
- bl __aeabi_fcmple @ is arg <= minlong?
- cmp r0, #0 @ nonzero == yes
- movne r0, #0 @ return minlong (80000000)
- movne r1, #0x80000000
- popne {r4, pc}
-
- mov r0, r4 @ recover arg
- mov r1, r4
- bl __aeabi_fcmpeq @ is arg == self?
- cmp r0, #0 @ zero == no
- moveq r1, #0 @ return zero for NaN
- popeq {r4, pc}
-
- mov r0, r4 @ recover arg
- bl __aeabi_f2lz @ convert float to long
- ldmfd sp!, {r4, pc}
+ ubfx r2, r0, #23, #8 @ grab the exponent
+ cmp r2, #0xbe @ MININT < x > MAXINT?
+ bhs f2l_special_cases
+ b __aeabi_f2lz @ tail call to convert float to long
+f2l_special_cases:
+ cmp r2, #0xff @ NaN or infinity?
+ beq f2l_maybeNaN
+f2l_notNaN:
+ adds r0, r0, r0 @ sign bit to carry
+ mov r0, #0xffffffff @ assume maxlong for lsw
+ mov r1, #0x7fffffff @ assume maxlong for msw
+ adcs r0, r0, #0 @ convert maxlong to minlong if exp negative
+ adc r1, r1, #0 @ convert maxlong to minlong if exp negative
+ bx lr @ return
+f2l_maybeNaN:
+ lsls r3, r0, #9
+ beq f2l_notNaN @ if fraction is non-zero, it's a NaN
+ mov r0, #0
+ mov r1, #0
+ bx lr @ return 0 for NaN
diff --git a/runtime/interpreter/mterp/out/mterp_arm.S b/runtime/interpreter/mterp/out/mterp_arm.S
index 4d540d7..c80d6b9 100644
--- a/runtime/interpreter/mterp/out/mterp_arm.S
+++ b/runtime/interpreter/mterp/out/mterp_arm.S
@@ -295,6 +295,27 @@
ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET]
.endm
+/*
+ * cfi support macros.
+ */
+.macro ENTRY name
+ .arm
+ .type \name, #function
+ .hidden \name // Hide this as a global symbol, so we do not incur plt calls.
+ .global \name
+ /* Cache alignment for function entry */
+ .balign 16
+\name:
+ .cfi_startproc
+ .fnstart
+.endm
+
+.macro END name
+ .fnend
+ .cfi_endproc
+ .size \name, .-\name
+.endm
+
/* File: arm/entry.S */
/*
* Copyright (C) 2016 The Android Open Source Project
@@ -329,10 +350,19 @@
*
*/
-ExecuteMterpImpl:
- .fnstart
- .save {r3-r10,fp,lr}
+ENTRY ExecuteMterpImpl
stmfd sp!, {r3-r10,fp,lr} @ save 10 regs, (r3 just to align 64)
+ .cfi_adjust_cfa_offset 40
+ .cfi_rel_offset r3, 0
+ .cfi_rel_offset r4, 4
+ .cfi_rel_offset r5, 8
+ .cfi_rel_offset r6, 12
+ .cfi_rel_offset r7, 16
+ .cfi_rel_offset r8, 20
+ .cfi_rel_offset r9, 24
+ .cfi_rel_offset r10, 28
+ .cfi_rel_offset fp, 32
+ .cfi_rel_offset lr, 36
/* Remember the return register */
str r3, [r2, #SHADOWFRAME_RESULT_REGISTER_OFFSET]
@@ -3664,7 +3694,6 @@
.balign 128
.L_op_float_to_long: /* 0x88 */
/* File: arm/op_float_to_long.S */
-@include "arm/unopWider.S" {"instr":"bl __aeabi_f2lz"}
/* File: arm/unopWider.S */
/*
* Generic 32bit-to-64bit unary operation. Provide an "instr" line
@@ -3742,7 +3771,6 @@
.balign 128
.L_op_double_to_long: /* 0x8b */
/* File: arm/op_double_to_long.S */
-@include "arm/unopWide.S" {"instr":"bl __aeabi_d2lz"}
/* File: arm/unopWide.S */
/*
* Generic 64-bit unary operation. Provide an "instr" line that
@@ -7386,33 +7414,26 @@
* to modest integer. The EABI convert function isn't doing this for us.
*/
f2l_doconv:
- stmfd sp!, {r4, lr}
- mov r1, #0x5f000000 @ (float)maxlong
- mov r4, r0
- bl __aeabi_fcmpge @ is arg >= maxlong?
- cmp r0, #0 @ nonzero == yes
- mvnne r0, #0 @ return maxlong (7fffffff)
- mvnne r1, #0x80000000
- popne {r4, pc}
-
- mov r0, r4 @ recover arg
- mov r1, #0xdf000000 @ (float)minlong
- bl __aeabi_fcmple @ is arg <= minlong?
- cmp r0, #0 @ nonzero == yes
- movne r0, #0 @ return minlong (80000000)
- movne r1, #0x80000000
- popne {r4, pc}
-
- mov r0, r4 @ recover arg
- mov r1, r4
- bl __aeabi_fcmpeq @ is arg == self?
- cmp r0, #0 @ zero == no
- moveq r1, #0 @ return zero for NaN
- popeq {r4, pc}
-
- mov r0, r4 @ recover arg
- bl __aeabi_f2lz @ convert float to long
- ldmfd sp!, {r4, pc}
+ ubfx r2, r0, #23, #8 @ grab the exponent
+ cmp r2, #0xbe @ MININT < x > MAXINT?
+ bhs f2l_special_cases
+ b __aeabi_f2lz @ tail call to convert float to long
+f2l_special_cases:
+ cmp r2, #0xff @ NaN or infinity?
+ beq f2l_maybeNaN
+f2l_notNaN:
+ adds r0, r0, r0 @ sign bit to carry
+ mov r0, #0xffffffff @ assume maxlong for lsw
+ mov r1, #0x7fffffff @ assume maxlong for msw
+ adcs r0, r0, #0 @ convert maxlong to minlong if exp negative
+ adc r1, r1, #0 @ convert maxlong to minlong if exp negative
+ bx lr @ return
+f2l_maybeNaN:
+ lsls r3, r0, #9
+ beq f2l_notNaN @ if fraction is non-zero, it's a NaN
+ mov r0, #0
+ mov r1, #0
+ bx lr @ return 0 for NaN
/* continuation for op_double_to_long */
/*
@@ -7423,46 +7444,28 @@
* to modest integer. The EABI convert function isn't doing this for us.
*/
d2l_doconv:
- stmfd sp!, {r4, r5, lr} @ save regs
- mov r3, #0x43000000 @ maxlong, as a double (high word)
- add r3, #0x00e00000 @ 0x43e00000
- mov r2, #0 @ maxlong, as a double (low word)
- sub sp, sp, #4 @ align for EABI
- mov r4, r0 @ save a copy of r0
- mov r5, r1 @ and r1
- bl __aeabi_dcmpge @ is arg >= maxlong?
- cmp r0, #0 @ nonzero == yes
- mvnne r0, #0 @ return maxlong (7fffffffffffffff)
- mvnne r1, #0x80000000
- bne 1f
-
- mov r0, r4 @ recover arg
- mov r1, r5
- mov r3, #0xc3000000 @ minlong, as a double (high word)
- add r3, #0x00e00000 @ 0xc3e00000
- mov r2, #0 @ minlong, as a double (low word)
- bl __aeabi_dcmple @ is arg <= minlong?
- cmp r0, #0 @ nonzero == yes
- movne r0, #0 @ return minlong (8000000000000000)
- movne r1, #0x80000000
- bne 1f
-
- mov r0, r4 @ recover arg
- mov r1, r5
- mov r2, r4 @ compare against self
- mov r3, r5
- bl __aeabi_dcmpeq @ is arg == self?
- cmp r0, #0 @ zero == no
- moveq r1, #0 @ return zero for NaN
- beq 1f
-
- mov r0, r4 @ recover arg
- mov r1, r5
- bl __aeabi_d2lz @ convert double to long
-
-1:
- add sp, sp, #4
- ldmfd sp!, {r4, r5, pc}
+ ubfx r2, r1, #20, #11 @ grab the exponent
+ movw r3, #0x43e
+ cmp r2, r3 @ MINLONG < x > MAXLONG?
+ bhs d2l_special_cases
+ b __aeabi_d2lz @ tail call to convert double to long
+d2l_special_cases:
+ movw r3, #0x7ff
+ cmp r2, r3
+ beq d2l_maybeNaN @ NaN?
+d2l_notNaN:
+ adds r1, r1, r1 @ sign bit to carry
+ mov r0, #0xffffffff @ assume maxlong for lsw
+ mov r1, #0x7fffffff @ assume maxlong for msw
+ adcs r0, r0, #0
+ adc r1, r1, #0 @ convert maxlong to minlong if exp negative
+ bx lr @ return
+d2l_maybeNaN:
+ orrs r3, r0, r1, lsl #12
+ beq d2l_notNaN @ if fraction is non-zero, it's a NaN
+ mov r0, #0
+ mov r1, #0
+ bx lr @ return 0 for NaN
.size artMterpAsmSisterStart, .-artMterpAsmSisterStart
.global artMterpAsmSisterEnd
@@ -12115,6 +12118,17 @@
cmp rPROFILE, #0
bgt MterpProfileActive @ if > 0, we may have some counts to report.
ldmfd sp!, {r3-r10,fp,pc} @ restore 10 regs and return
+ .cfi_restore r3
+ .cfi_restore r4
+ .cfi_restore r5
+ .cfi_restore r6
+ .cfi_restore r7
+ .cfi_restore r9
+ .cfi_restore r9
+ .cfi_restore r10
+ .cfi_restore fp
+ .cfi_restore pc
+ .cfi_adjust_cfa_offset -40
MterpProfileActive:
mov rINST, r0 @ stash return value
@@ -12126,8 +12140,18 @@
bl MterpAddHotnessBatch @ (method, shadow_frame, self)
mov r0, rINST @ restore return value
ldmfd sp!, {r3-r10,fp,pc} @ restore 10 regs and return
+ .cfi_restore r3
+ .cfi_restore r4
+ .cfi_restore r5
+ .cfi_restore r6
+ .cfi_restore r7
+ .cfi_restore r9
+ .cfi_restore r9
+ .cfi_restore r10
+ .cfi_restore fp
+ .cfi_restore pc
+ .cfi_adjust_cfa_offset -40
- .fnend
- .size ExecuteMterpImpl, .-ExecuteMterpImpl
+ END ExecuteMterpImpl
diff --git a/runtime/mirror/class_ext.cc b/runtime/mirror/class_ext.cc
index 259bbbe..7c6a710 100644
--- a/runtime/mirror/class_ext.cc
+++ b/runtime/mirror/class_ext.cc
@@ -46,8 +46,9 @@
SetFieldObject<false>(obsolete_methods_off, methods.Ptr());
}
-// TODO We really need to be careful how we update this. If we ever in the future make it so that
-// these arrays are written into without all threads being suspended we have a race condition!
+// We really need to be careful how we update this. If we ever in the future make it so that
+// these arrays are written into without all threads being suspended we have a race condition! This
+// race could cause obsolete methods to be missed.
bool ClassExt::ExtendObsoleteArrays(Thread* self, uint32_t increase) {
DCHECK_EQ(GetLockOwnerThreadId(), Thread::Current()->GetThreadId())
<< "Obsolete arrays are set without synchronization!";
diff --git a/runtime/modifiers.h b/runtime/modifiers.h
index 8a01043..ae6b31d 100644
--- a/runtime/modifiers.h
+++ b/runtime/modifiers.h
@@ -67,7 +67,7 @@
// Set by the verifier for a method that could not be verified to follow structured locking.
static constexpr uint32_t kAccMustCountLocks = 0x02000000; // method (runtime)
-// Set to indicate that the ArtMethod is obsolete and has a different DexCache from it's declaring
+// Set to indicate that the ArtMethod is obsolete and has a different DexCache from its declaring
// class.
// TODO Might want to re-arrange some of these so that we can have obsolete + intrinsic methods.
static constexpr uint32_t kAccObsoleteMethod = 0x04000000; // method (runtime)
diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc
index 69bd887..d0349b9 100644
--- a/runtime/openjdkjvmti/ti_redefine.cc
+++ b/runtime/openjdkjvmti/ti_redefine.cc
@@ -66,7 +66,8 @@
return map;
}
memcpy(map->Begin(), dex_data, data_len);
- // Make the dex files mmap read only.
+ // Make the dex files mmap read only. This matches how other DexFiles are mmaped and prevents
+ // programs from corrupting it.
map->Protect(PROT_READ);
return map;
}
diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h
index f3a5834..c819acd 100644
--- a/runtime/openjdkjvmti/ti_redefine.h
+++ b/runtime/openjdkjvmti/ti_redefine.h
@@ -68,7 +68,7 @@
public:
// Redefine the given class with the given dex data. Note this function does not take ownership of
// the dex_data pointer. It is not used after this call however and may be freed if desired.
- // The caller is responsible for freeing it. The runtime makes it's own copy of the data.
+ // The caller is responsible for freeing it. The runtime makes its own copy of the data.
static jvmtiError RedefineClass(ArtJvmTiEnv* env,
art::Runtime* runtime,
art::Thread* self,
@@ -146,6 +146,7 @@
// This will check that no constraints are violated (more than 1 class in dex file, any changes in
// number/declaration of methods & fields, changes in access flags, etc.)
bool EnsureRedefinitionIsValid() {
+ LOG(WARNING) << "Redefinition is not checked for validity currently";
return true;
}
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 1283cf0..17f5513 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -1394,6 +1394,7 @@
os << " | group=\"" << group_name << "\""
<< " sCount=" << thread->tls32_.suspend_count
<< " dsCount=" << thread->tls32_.debug_suspend_count
+ << " flags=" << thread->tls32_.state_and_flags.as_struct.flags
<< " obj=" << reinterpret_cast<void*>(thread->tlsPtr_.opeer)
<< " self=" << reinterpret_cast<const void*>(thread) << "\n";
}
@@ -3175,4 +3176,8 @@
tlsPtr_.exception = new_exception.Ptr();
}
+bool Thread::IsAotCompiler() {
+ return Runtime::Current()->IsAotCompiler();
+}
+
} // namespace art
diff --git a/runtime/thread.h b/runtime/thread.h
index 35226f2..b80fdc7 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -33,13 +33,13 @@
#include "base/mutex.h"
#include "entrypoints/jni/jni_entrypoints.h"
#include "entrypoints/quick/quick_entrypoints.h"
+#include "gc_root.h"
#include "globals.h"
#include "handle_scope.h"
#include "instrumentation.h"
#include "jvalue.h"
#include "object_callbacks.h"
#include "offsets.h"
-#include "runtime.h"
#include "runtime_stats.h"
#include "stack.h"
#include "thread_state.h"
@@ -87,7 +87,6 @@
class JavaVMExt;
struct JNIEnvExt;
class Monitor;
-class Runtime;
class ScopedObjectAccessAlreadyRunnable;
class ShadowFrame;
class SingleStepControl;
@@ -949,17 +948,17 @@
}
std::vector<ArtMethod*>* GetStackTraceSample() const {
- DCHECK(!Runtime::Current()->IsAotCompiler());
+ DCHECK(!IsAotCompiler());
return tlsPtr_.deps_or_stack_trace_sample.stack_trace_sample;
}
void SetStackTraceSample(std::vector<ArtMethod*>* sample) {
- DCHECK(!Runtime::Current()->IsAotCompiler());
+ DCHECK(!IsAotCompiler());
tlsPtr_.deps_or_stack_trace_sample.stack_trace_sample = sample;
}
verifier::VerifierDeps* GetVerifierDeps() const {
- DCHECK(Runtime::Current()->IsAotCompiler());
+ DCHECK(IsAotCompiler());
return tlsPtr_.deps_or_stack_trace_sample.verifier_deps;
}
@@ -967,7 +966,7 @@
// entry in the thread is cleared before destruction of the actual VerifierDeps
// object, or the thread.
void SetVerifierDeps(verifier::VerifierDeps* verifier_deps) {
- DCHECK(Runtime::Current()->IsAotCompiler());
+ DCHECK(IsAotCompiler());
DCHECK(verifier_deps == nullptr || tlsPtr_.deps_or_stack_trace_sample.verifier_deps == nullptr);
tlsPtr_.deps_or_stack_trace_sample.verifier_deps = verifier_deps;
}
@@ -1246,6 +1245,8 @@
// Install the protected region for implicit stack checks.
void InstallImplicitProtection();
+ static bool IsAotCompiler();
+
// 32 bits of atomically changed state and flags. Keeping as 32 bits allows and atomic CAS to
// change from being Suspended to Runnable without a suspend request occurring.
union PACKED(4) StateAndFlags {
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index 27fb37a..a6bd83d 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -375,7 +375,7 @@
return count;
}
-size_t ThreadList::RunEmptyCheckpoint() {
+size_t ThreadList::RunEmptyCheckpoint(std::vector<uint32_t>& runnable_thread_ids) {
Thread* self = Thread::Current();
Locks::mutator_lock_->AssertNotExclusiveHeld(self);
Locks::thread_list_lock_->AssertNotHeld(self);
@@ -392,6 +392,9 @@
// This thread will run an empty checkpoint (decrement the empty checkpoint barrier)
// some time in the near future.
++count;
+ if (kIsDebugBuild) {
+ runnable_thread_ids.push_back(thread->GetThreadId());
+ }
break;
}
if (thread->GetState() != kRunnable) {
diff --git a/runtime/thread_list.h b/runtime/thread_list.h
index 133d430..1acabcb 100644
--- a/runtime/thread_list.h
+++ b/runtime/thread_list.h
@@ -27,6 +27,7 @@
#include <bitset>
#include <list>
+#include <vector>
namespace art {
namespace gc {
@@ -106,7 +107,9 @@
// in-flight mutator heap access (eg. a read barrier.) Runnable threads will respond by
// decrementing the empty checkpoint barrier count. This works even when the weak ref access is
// disabled. Only one concurrent use is currently supported.
- size_t RunEmptyCheckpoint()
+ // In debug build, runnable_thread_ids will be populated with the thread IDS of the runnable
+ // thread to wait for.
+ size_t RunEmptyCheckpoint(std::vector<uint32_t>& runnable_thread_ids)
REQUIRES(!Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_);
size_t RunCheckpointOnRunnableThreads(Closure* checkpoint_function)
diff --git a/test/422-type-conversion/src/Main.java b/test/422-type-conversion/src/Main.java
index 146f309..7754b75 100644
--- a/test/422-type-conversion/src/Main.java
+++ b/test/422-type-conversion/src/Main.java
@@ -390,6 +390,8 @@
assertLongEquals(9223372036854775807L, $opt$noinline$FloatToLong(9223372036854775807F)); // 2^63 - 1
assertLongEquals(-9223372036854775808L, $opt$noinline$FloatToLong(-9223372036854775807F)); // -(2^63 - 1)
assertLongEquals(-9223372036854775808L, $opt$noinline$FloatToLong(-9223372036854775808F)); // -(2^63)
+ assertLongEquals(9223371487098961920L, $opt$noinline$FloatToLong(9223371487098961920F)); // Math.nextAfter(2F^63, 0)
+ assertLongEquals(-9223371487098961920L, $opt$noinline$FloatToLong(-9223371487098961920F)); // Math.nextAfter(-2F^63, 0)
assertLongEquals(0L, $opt$noinline$FloatToLong(Float.NaN));
assertLongEquals(9223372036854775807L, $opt$noinline$FloatToLong(Float.POSITIVE_INFINITY));
assertLongEquals(-9223372036854775808L, $opt$noinline$FloatToLong(Float.NEGATIVE_INFINITY));
@@ -469,6 +471,8 @@
assertLongEquals(-9223372036854775808L, $opt$noinline$DoubleToLong(-9223372036854775807D)); // -(2^63 - 1)
assertLongEquals(-9223372036854775808L, $opt$noinline$DoubleToLong(-9223372036854775808D)); // -(2^63)
assertLongEquals(0L, $opt$noinline$DoubleToLong(Double.NaN));
+ assertLongEquals(9223372036854774784L, $opt$noinline$DoubleToLong(9223372036854774784D)); // Math.nextAfter(2D^63, 0)
+ assertLongEquals(-9223372036854774784L, $opt$noinline$DoubleToLong(-9223372036854774784D)); // Math.nextAfter(-2D^63, 0)
assertLongEquals(9223372036854775807L, $opt$noinline$DoubleToLong(Double.POSITIVE_INFINITY));
assertLongEquals(-9223372036854775808L, $opt$noinline$DoubleToLong(Double.NEGATIVE_INFINITY));
}
diff --git a/test/530-checker-loops1/info.txt b/test/530-checker-loops1/info.txt
index f5d334d..ecefa7e 100644
--- a/test/530-checker-loops1/info.txt
+++ b/test/530-checker-loops1/info.txt
@@ -1 +1 @@
-Test on loop optimizations.
+Test on loop optimizations, in particular around common induction.
diff --git a/test/530-checker-loops1/src/Main.java b/test/530-checker-loops1/src/Main.java
index dde4d62..383c28f 100644
--- a/test/530-checker-loops1/src/Main.java
+++ b/test/530-checker-loops1/src/Main.java
@@ -15,7 +15,7 @@
*/
//
-// Test on loop optimizations.
+// Test on loop optimizations, in particular around common induction.
//
public class Main {
diff --git a/test/530-checker-loops2/info.txt b/test/530-checker-loops2/info.txt
index f5d334d..3b5a7ad 100644
--- a/test/530-checker-loops2/info.txt
+++ b/test/530-checker-loops2/info.txt
@@ -1 +1 @@
-Test on loop optimizations.
+Test on loop optimizations, in particular around less common induction.
diff --git a/test/530-checker-loops2/src/Main.java b/test/530-checker-loops2/src/Main.java
index 47b6475..a94d69b 100644
--- a/test/530-checker-loops2/src/Main.java
+++ b/test/530-checker-loops2/src/Main.java
@@ -15,7 +15,7 @@
*/
//
-// Test on loop optimizations.
+// Test on loop optimizations, in particular around less common induction.
//
public class Main {
diff --git a/test/530-checker-loops3/info.txt b/test/530-checker-loops3/info.txt
index 07d99a3..e262f8e 100644
--- a/test/530-checker-loops3/info.txt
+++ b/test/530-checker-loops3/info.txt
@@ -1 +1 @@
-Test on loop optimizations, in particular loop-based dynamic bce.
+Test on loop optimizations, in particular around loop-based dynamic bce.
diff --git a/test/530-checker-loops4/expected.txt b/test/530-checker-loops4/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/530-checker-loops4/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/530-checker-loops4/info.txt b/test/530-checker-loops4/info.txt
new file mode 100644
index 0000000..10cf3b1
--- /dev/null
+++ b/test/530-checker-loops4/info.txt
@@ -0,0 +1 @@
+Test on loop optimizations, in particular with geometric induction.
diff --git a/test/530-checker-loops4/src/Main.java b/test/530-checker-loops4/src/Main.java
new file mode 100644
index 0000000..2e19c88
--- /dev/null
+++ b/test/530-checker-loops4/src/Main.java
@@ -0,0 +1,323 @@
+/*
+ * 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 geometric induction.
+//
+public class Main {
+
+ /// CHECK-START: int Main.geo1(int) loop_optimization (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+ /// CHECK-DAG: Mul loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.geo1(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 1410065408 loop:none
+ /// CHECK-DAG: <<Mul:i\d+>> Mul [<<Par>>,<<Int>>] loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Mul>>,<<Zer>>] loop:none
+ /// CHECK-DAG: Return [<<Add>>] loop:none
+ //
+ /// CHECK-START: int Main.geo1(int) loop_optimization (after)
+ /// CHECK-NOT: Phi
+ public static int geo1(int a) {
+ for (int i = 0; i < 10; i++) {
+ a *= 10;
+ }
+ return a;
+ }
+
+ /// CHECK-START: int Main.geo2(int) loop_optimization (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+ /// CHECK-DAG: Shl loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.geo2(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 1024 loop:none
+ /// CHECK-DAG: <<Mul:i\d+>> Mul [<<Par>>,<<Int>>] loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Mul>>,<<Zer>>] loop:none
+ /// CHECK-DAG: Return [<<Add>>] loop:none
+ //
+ /// CHECK-START: int Main.geo2(int) loop_optimization (after)
+ /// CHECK-NOT: Phi
+ public static int geo2(int a) {
+ for (int i = 0; i < 10; i++) {
+ a <<= 1;
+ }
+ return a;
+ }
+
+ /// CHECK-START: int Main.geo3(int) loop_optimization (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+ /// CHECK-DAG: Div loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.geo3(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 59049 loop:none
+ /// CHECK-DAG: <<Div:i\d+>> Div [<<Par>>,<<Int>>] loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Div>>,<<Zer>>] loop:none
+ /// CHECK-DAG: Return [<<Add>>] loop:none
+ //
+ /// CHECK-START: int Main.geo3(int) loop_optimization (after)
+ /// CHECK-NOT: Phi
+ public static int geo3(int a) {
+ for (int i = 0; i < 10; i++) {
+ a /= 3;
+ }
+ return a;
+ }
+
+ /// CHECK-START: int Main.geo4(int) loop_optimization (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+ /// CHECK-DAG: Rem loop:<<Loop>>
+ //
+ /// 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-START: int Main.geo4(int) loop_optimization (after)
+ /// CHECK-NOT: Phi
+ public static int geo4(int a) {
+ for (int i = 0; i < 10; i++) {
+ a %= 7;
+ }
+ return a;
+ }
+
+ // TODO: someday?
+ 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,
+ 21, 22, 23, 24, 25, 26 };
+ int a = 1;
+ int r = 0;
+ for (int i = 0; i < 3; i++) {
+ r += x[a];
+ a *= 5;
+ }
+ return r;
+ }
+
+ // TODO: someday?
+ 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,
+ 21, 22, 23, 24, 25, 26 };
+ int a = 1;
+ int r = 0;
+ for (int i = 0; i < 5; i++) {
+ r += x[a];
+ a <<= 1;
+ }
+ return r;
+ }
+
+ /// CHECK-START: int Main.geo3BCE() BCE (before)
+ /// CHECK-DAG: BoundsCheck loop:none
+ /// CHECK-DAG: BoundsCheck loop:{{B\d+}}
+ //
+ /// CHECK-START: int Main.geo3BCE() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ public static int geo3BCE() {
+ 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 };
+ int a = 25;
+ int r = 0;
+ for (int i = 0; i < 100; i++) { // a converges to 0
+ r += x[a];
+ a /= 5;
+ }
+ return r;
+ }
+
+ /// CHECK-START: int Main.geo4BCE() BCE (before)
+ /// CHECK-DAG: BoundsCheck loop:none
+ /// CHECK-DAG: BoundsCheck loop:{{B\d+}}
+ //
+ /// CHECK-START: int Main.geo4BCE() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ public static int geo4BCE() {
+ 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 };
+ int a = 25;
+ int r = 0;
+ for (int i = 0; i < 100; i++) { // a converges to 0
+ r += x[a];
+ a %= 5;
+ }
+ return r;
+ }
+
+ /// CHECK-START: int Main.geoMulBlackHole(int) loop_optimization (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+ /// CHECK-DAG: Mul loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.geoMulBlackHole(int) loop_optimization (after)
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+ /// CHECK-DAG: Return [<<Zero>>]
+ //
+ /// CHECK-START: int Main.geoMulBlackHole(int) loop_optimization (after)
+ /// CHECK-NOT: Phi
+ /// CHECK-NOT: Mul
+ public static int geoMulBlackHole(int a) {
+ for (int i = 0; i < 100; i++) {
+ a *= 10;
+ }
+ return a;
+ }
+
+ /// CHECK-START: int Main.geoShlBlackHole(int) loop_optimization (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+ /// CHECK-DAG: Shl loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.geoShlBlackHole(int) loop_optimization (after)
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+ /// CHECK-DAG: Return [<<Zero>>]
+ //
+ /// CHECK-START: int Main.geoShlBlackHole(int) loop_optimization (after)
+ /// CHECK-NOT: Phi
+ /// CHECK-NOT: Shl
+ public static int geoShlBlackHole(int a) {
+ for (int i = 0; i < 100; i++) {
+ a <<= 1;
+ }
+ return a;
+ }
+
+ /// CHECK-START: int Main.geoDivBlackHole(int) loop_optimization (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+ /// CHECK-DAG: Div loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.geoDivBlackHole(int) loop_optimization (after)
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+ /// CHECK-DAG: Return [<<Zero>>]
+ //
+ /// CHECK-START: int Main.geoDivBlackHole(int) loop_optimization (after)
+ /// CHECK-NOT: Phi
+ /// CHECK-NOT: Div
+ public static int geoDivBlackHole(int a) {
+ for (int i = 0; i < 100; i++) {
+ a /= 10;
+ }
+ 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!
+ //
+ /// 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>>
+ public static int geoRemBlackHole(int a) {
+ for (int i = 0; i < 100; i++) {
+ a %= 1;
+ }
+ return a;
+ }
+
+ //
+ // Verifier.
+ //
+
+ public static void main(String[] args) {
+ int m = 1410065408;
+ for (int i = -100; i <= 100; i++) {
+ expectEquals(m * i, geo1(i));
+ }
+ for (int i = 1; i <= 1000000000; i *= 10) {
+ expectEquals( m * i, geo1( i));
+ expectEquals(-m * i, geo1(-i));
+ }
+
+ for (int i = -100; i <= 100; i++) {
+ expectEquals(i << 10, geo2(i));
+ }
+ for (int i = 0; i < 22; i++) {
+ expectEquals(1 << (i + 10), geo2(1 << i));
+ }
+ expectEquals(0x80000400, geo2(0x00200001));
+ expectEquals(0x00000000, geo2(0x00400000));
+ expectEquals(0x00000400, geo2(0x00400001));
+
+ int d = 59049;
+ for (int i = -100; i <= 100; i++) {
+ expectEquals(0, geo3(i));
+ }
+ for (int i = 1; i <= 100; i++) {
+ expectEquals( i, geo3( i * d));
+ expectEquals( i, geo3( i * d + 1));
+ expectEquals(-i, geo3(-i * d));
+ expectEquals(-i, geo3(-i * d - 1));
+ }
+
+ for (int i = -100; i <= 100; i++) {
+ expectEquals(i % 7, geo4(i));
+ }
+
+ expectEquals(34, geo1BCE());
+ expectEquals(36, geo2BCE());
+ expectEquals(131, geo3BCE());
+ expectEquals(125, geo4BCE());
+
+ // Nothing escapes!
+ expectEquals(0, geoMulBlackHole(0));
+ expectEquals(0, geoShlBlackHole(0));
+ expectEquals(0, geoDivBlackHole(0));
+ expectEquals(0, geoRemBlackHole(0));
+ for (int i = -100; i <= 100; i++) {
+ expectEquals(0, geoMulBlackHole(i));
+ expectEquals(0, geoShlBlackHole(i));
+ expectEquals(0, geoDivBlackHole(i));
+ expectEquals(0, geoRemBlackHole(i));
+ }
+ for (int i = 0; i < 31; i++) {
+ expectEquals(0, geoMulBlackHole(1 << i));
+ expectEquals(0, geoShlBlackHole(1 << i));
+ expectEquals(0, geoDivBlackHole(1 << i));
+ expectEquals(0, geoRemBlackHole(1 << i));
+ }
+ expectEquals(0, geoMulBlackHole(0x7fffffff));
+ expectEquals(0, geoShlBlackHole(0x7fffffff));
+ expectEquals(0, geoDivBlackHole(0x7fffffff));
+ expectEquals(0, geoRemBlackHole(0x7fffffff));
+ expectEquals(0, geoMulBlackHole(0x80000000));
+ expectEquals(0, geoShlBlackHole(0x80000000));
+ expectEquals(0, geoDivBlackHole(0x80000000));
+ expectEquals(0, geoRemBlackHole(0x80000000));
+
+ 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/629-vdex-speed/expected.txt b/test/629-vdex-speed/expected.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/629-vdex-speed/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/629-vdex-speed/info.txt b/test/629-vdex-speed/info.txt
new file mode 100644
index 0000000..6d84cb5
--- /dev/null
+++ b/test/629-vdex-speed/info.txt
@@ -0,0 +1,2 @@
+Regression test for vdex that used to not AOT compile
+methods when the VerifierDeps were verified.
diff --git a/test/629-vdex-speed/run b/test/629-vdex-speed/run
new file mode 100644
index 0000000..f1b0a95
--- /dev/null
+++ b/test/629-vdex-speed/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+exec ${RUN} --vdex "${@}"
diff --git a/test/629-vdex-speed/src/Main.java b/test/629-vdex-speed/src/Main.java
new file mode 100644
index 0000000..470565a
--- /dev/null
+++ b/test/629-vdex-speed/src/Main.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+public class Main {
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
+ if (!isAotCompiled(Main.class, "main")) {
+ throw new Error("Expected Main.main to be AOT compiled");
+ }
+ }
+
+ private native static boolean isAotCompiled(Class<?> cls, String methodName);
+}
+
diff --git a/test/Android.arm_vixl.mk b/test/Android.arm_vixl.mk
index 5ae961a..c89eb4a 100644
--- a/test/Android.arm_vixl.mk
+++ b/test/Android.arm_vixl.mk
@@ -16,6 +16,5 @@
# Known broken tests for the ARM VIXL backend.
TEST_ART_BROKEN_OPTIMIZING_ARM_VIXL_RUN_TESTS := \
- 488-checker-inline-recursive-calls \
- 552-checker-sharpening \
562-checker-no-intermediate \
+ 624-checker-stringops \
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index d15d016..28e1e60 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -360,8 +360,10 @@
TEST_ART_BROKEN_NO_RELOCATE_TESTS :=
# Temporarily disable some broken tests when forcing access checks in interpreter b/22414682
+# 629 requires compilation.
TEST_ART_BROKEN_INTERPRETER_ACCESS_CHECK_TESTS := \
- 137-cfi
+ 137-cfi \
+ 629-vdex-speed
ifneq (,$(filter interp-ac,$(COMPILER_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -425,6 +427,7 @@
# explicitly test for them. These all also assume we have an image.
# 147-stripped-dex-fallback is disabled because it requires --prebuild.
# 554-jit-profile-file is disabled because it needs a primary oat file to know what it should save.
+# 629-vdex-speed requires compiled code.
TEST_ART_BROKEN_FALLBACK_RUN_TESTS := \
116-nodex2oat \
117-nopatchoat \
@@ -433,7 +436,8 @@
137-cfi \
138-duplicate-classes-check2 \
147-stripped-dex-fallback \
- 554-jit-profile-file
+ 554-jit-profile-file \
+ 629-vdex-speed
# This test fails without an image.
TEST_ART_BROKEN_NO_IMAGE_RUN_TESTS := \
@@ -470,12 +474,14 @@
# This test dynamically enables tracing to force a deoptimization. This makes the test meaningless
# when already tracing, and writes an error message that we do not want to check for.
# 130 occasional timeout b/32383962.
+# 629 requires compilation.
TEST_ART_BROKEN_TRACING_RUN_TESTS := \
087-gc-after-link \
130-hprof \
137-cfi \
141-class-unload \
570-checker-osr \
+ 629-vdex-speed \
802-deoptimization
ifneq (,$(filter trace stream,$(TRACE_TYPES)))
@@ -486,9 +492,11 @@
# Known broken tests for the interpreter.
# CFI unwinding expects managed frames.
+# 629 requires compilation.
TEST_ART_BROKEN_INTERPRETER_RUN_TESTS := \
137-cfi \
- 554-jit-profile-file
+ 554-jit-profile-file \
+ 629-vdex-speed
ifneq (,$(filter interpreter,$(COMPILER_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -504,8 +512,10 @@
# Test 906 iterates the heap filtering with different options. No instances should be created
# between those runs to be able to have precise checks.
# Test 902 hits races with the JIT compiler. b/32821077
+# Test 629 requires compilation.
TEST_ART_BROKEN_JIT_RUN_TESTS := \
137-cfi \
+ 629-vdex-speed \
902-hello-transformation \
904-object-allocation \
906-iterate-heap \
diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc
index 9cfa324..285f3aa 100644
--- a/test/common/runtime_state.cc
+++ b/test/common/runtime_state.cc
@@ -119,6 +119,24 @@
return JNI_TRUE;
}
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isAotCompiled(JNIEnv* env,
+ jclass,
+ jclass cls,
+ jstring method_name) {
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+ ScopedUtfChars chars(env, method_name);
+ CHECK(chars.c_str() != nullptr);
+ ArtMethod* method = soa.Decode<mirror::Class>(cls)->FindDeclaredDirectMethodByName(
+ chars.c_str(), kRuntimePointerSize);
+ const void* code = method->GetOatMethodQuickCode(kRuntimePointerSize);
+ jit::Jit* jit = Runtime::Current()->GetJit();
+ if (jit != nullptr && jit->GetCodeCache()->ContainsPc(code)) {
+ return true;
+ }
+ return code != nullptr;
+}
+
extern "C" JNIEXPORT void JNICALL Java_Main_ensureJitCompiled(JNIEnv* env,
jclass,
jclass cls,
diff --git a/test/etc/default-build b/test/etc/default-build
index e663496..f2b5078 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -71,7 +71,7 @@
declare -A JACK_EXPERIMENTAL_ARGS
JACK_EXPERIMENTAL_ARGS["default-methods"]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=24"
JACK_EXPERIMENTAL_ARGS["lambdas"]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=24"
-JACK_EXPERIMENTAL_ARGS["method-handles"]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=26"
+JACK_EXPERIMENTAL_ARGS["method-handles"]="-D jack.java.source.version=1.7 -D jack.android.min-api-level=o-b1"
declare -A SMALI_EXPERIMENTAL_ARGS
SMALI_EXPERIMENTAL_ARGS["default-methods"]="--api-level 24"