diff options
Diffstat (limited to 'compiler/optimizing')
20 files changed, 636 insertions, 231 deletions
diff --git a/compiler/optimizing/boolean_simplifier.cc b/compiler/optimizing/boolean_simplifier.cc index e9ca042f1d..be432c5a20 100644 --- a/compiler/optimizing/boolean_simplifier.cc +++ b/compiler/optimizing/boolean_simplifier.cc @@ -59,7 +59,8 @@ static HInstruction* GetOppositeCondition(HInstruction* cond) { return new (allocator) HGreaterThan(lhs, rhs); } else if (cond->IsGreaterThan()) { return new (allocator) HLessThanOrEqual(lhs, rhs); - } else if (cond->IsGreaterThanOrEqual()) { + } else { + DCHECK(cond->IsGreaterThanOrEqual()); return new (allocator) HLessThan(lhs, rhs); } } else if (cond->IsIntConstant()) { @@ -70,10 +71,11 @@ static HInstruction* GetOppositeCondition(HInstruction* cond) { DCHECK(int_const->IsOne()); return graph->GetIntConstant(0); } + } else { + // General case when 'cond' is another instruction of type boolean. + // Negate with 'cond == 0'. + return new (allocator) HEqual(cond, graph->GetIntConstant(0)); } - - LOG(FATAL) << "Instruction " << cond->DebugName() << " used as a condition"; - UNREACHABLE(); } void HBooleanSimplifier::Run() { diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 0f79d189be..1f95041a92 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -2087,16 +2087,32 @@ void InstructionCodeGeneratorARM::VisitMul(HMul* mul) { } void LocationsBuilderARM::VisitDiv(HDiv* div) { - LocationSummary::CallKind call_kind = div->GetResultType() == Primitive::kPrimLong - ? LocationSummary::kCall - : LocationSummary::kNoCall; + LocationSummary::CallKind call_kind = LocationSummary::kNoCall; + if (div->GetResultType() == Primitive::kPrimLong) { + // pLdiv runtime call. + call_kind = LocationSummary::kCall; + } else if (div->GetResultType() == Primitive::kPrimInt && + !codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { + // pIdivmod runtime call. + call_kind = LocationSummary::kCall; + } + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(div, call_kind); switch (div->GetResultType()) { case Primitive::kPrimInt: { - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + } else { + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + // Note: divrem will compute both the quotient and the remainder as the pair R0 and R1, but + // we only need the former. + locations->SetOut(Location::RegisterLocation(R0)); + } break; } case Primitive::kPrimLong: { @@ -2129,9 +2145,18 @@ void InstructionCodeGeneratorARM::VisitDiv(HDiv* div) { switch (div->GetResultType()) { case Primitive::kPrimInt: { - __ sdiv(out.AsRegister<Register>(), - first.AsRegister<Register>(), - second.AsRegister<Register>()); + if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { + __ sdiv(out.AsRegister<Register>(), + first.AsRegister<Register>(), + second.AsRegister<Register>()); + } else { + InvokeRuntimeCallingConvention calling_convention; + DCHECK_EQ(calling_convention.GetRegisterAt(0), first.AsRegister<Register>()); + DCHECK_EQ(calling_convention.GetRegisterAt(1), second.AsRegister<Register>()); + DCHECK_EQ(R0, out.AsRegister<Register>()); + + codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pIdivmod), div, div->GetDexPc(), nullptr); + } break; } @@ -2169,17 +2194,32 @@ void InstructionCodeGeneratorARM::VisitDiv(HDiv* div) { void LocationsBuilderARM::VisitRem(HRem* rem) { Primitive::Type type = rem->GetResultType(); - LocationSummary::CallKind call_kind = type == Primitive::kPrimInt - ? LocationSummary::kNoCall - : LocationSummary::kCall; + + // Most remainders are implemented in the runtime. + LocationSummary::CallKind call_kind = LocationSummary::kCall; + if (rem->GetResultType() == Primitive::kPrimInt && + codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { + // Have hardware divide instruction for int, do it with three instructions. + call_kind = LocationSummary::kNoCall; + } + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(rem, call_kind); switch (type) { case Primitive::kPrimInt: { - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); - locations->AddTemp(Location::RequiresRegister()); + if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + locations->AddTemp(Location::RequiresRegister()); + } else { + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + // Note: divrem will compute both the quotient and the remainder as the pair R0 and R1, but + // we only need the latter. + locations->SetOut(Location::RegisterLocation(R1)); + } break; } case Primitive::kPrimLong: { @@ -2224,16 +2264,25 @@ void InstructionCodeGeneratorARM::VisitRem(HRem* rem) { Primitive::Type type = rem->GetResultType(); switch (type) { case Primitive::kPrimInt: { - Register reg1 = first.AsRegister<Register>(); - Register reg2 = second.AsRegister<Register>(); - Register temp = locations->GetTemp(0).AsRegister<Register>(); + if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { + Register reg1 = first.AsRegister<Register>(); + Register reg2 = second.AsRegister<Register>(); + Register temp = locations->GetTemp(0).AsRegister<Register>(); + + // temp = reg1 / reg2 (integer division) + // temp = temp * reg2 + // dest = reg1 - temp + __ sdiv(temp, reg1, reg2); + __ mul(temp, temp, reg2); + __ sub(out.AsRegister<Register>(), reg1, ShifterOperand(temp)); + } else { + InvokeRuntimeCallingConvention calling_convention; + DCHECK_EQ(calling_convention.GetRegisterAt(0), first.AsRegister<Register>()); + DCHECK_EQ(calling_convention.GetRegisterAt(1), second.AsRegister<Register>()); + DCHECK_EQ(R1, out.AsRegister<Register>()); - // temp = reg1 / reg2 (integer division) - // temp = temp * reg2 - // dest = reg1 - temp - __ sdiv(temp, reg1, reg2); - __ mul(temp, temp, reg2); - __ sub(out.AsRegister<Register>(), reg1, ShifterOperand(temp)); + codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pIdivmod), rem, rem->GetDexPc(), nullptr); + } break; } diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 8d0ca0beb9..0d5fe49c1d 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -3146,7 +3146,7 @@ void InstructionCodeGeneratorX86::GenerateExplicitNullCheck(HNullCheck* instruct Location obj = locations->InAt(0); if (obj.IsRegister()) { - __ cmpl(obj.AsRegister<Register>(), Immediate(0)); + __ testl(obj.AsRegister<Register>(), obj.AsRegister<Register>()); } else if (obj.IsStackSlot()) { __ cmpl(Address(ESP, obj.GetStackIndex()), Immediate(0)); } else { diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 4b990f1ddd..2c17a67867 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -49,7 +49,8 @@ void HInliner::Run() { for (HInstruction* instruction = block->GetFirstInstruction(); instruction != nullptr;) { HInstruction* next = instruction->GetNext(); HInvokeStaticOrDirect* call = instruction->AsInvokeStaticOrDirect(); - if (call != nullptr) { + // As long as the call is not intrinsified, it is worth trying to inline. + if (call != nullptr && call->GetIntrinsic() == Intrinsics::kNone) { // We use the original invoke type to ensure the resolution of the called method // works properly. if (!TryInline(call, call->GetDexMethodIndex(), call->GetOriginalInvokeType())) { diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc index 36cf8568e5..628a844cc7 100644 --- a/compiler/optimizing/intrinsics.cc +++ b/compiler/optimizing/intrinsics.cc @@ -191,8 +191,10 @@ static Intrinsics GetIntrinsic(InlineMethod method) { case kIntrinsicCompareTo: return Intrinsics::kStringCompareTo; case kIntrinsicIsEmptyOrLength: - return ((method.d.data & kIntrinsicFlagIsEmpty) == 0) ? - Intrinsics::kStringLength : Intrinsics::kStringIsEmpty; + // The inliner can handle these two cases - and this is the preferred approach + // since after inlining the call is no longer visible (as opposed to waiting + // until codegen to handle intrinsic). + return Intrinsics::kNone; case kIntrinsicIndexOf: return ((method.d.data & kIntrinsicFlagBase0) == 0) ? Intrinsics::kStringIndexOfAfter : Intrinsics::kStringIndexOf; diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc index 3fb6a7d2d0..33176f009c 100644 --- a/compiler/optimizing/intrinsics_arm.cc +++ b/compiler/optimizing/intrinsics_arm.cc @@ -862,7 +862,7 @@ void IntrinsicCodeGeneratorARM::VisitStringCompareTo(HInvoke* invoke) { ArmAssembler* assembler = GetAssembler(); LocationSummary* locations = invoke->GetLocations(); - // Note that the null check must have be done earlier. + // Note that the null check must have been done earlier. DCHECK(!invoke->CanDoImplicitNullCheck()); Register argument = locations->InAt(1).AsRegister<Register>(); @@ -903,8 +903,6 @@ UNIMPLEMENTED_INTRINSIC(MathRoundDouble) // Could be done by changing rounding UNIMPLEMENTED_INTRINSIC(MathRoundFloat) // Could be done by changing rounding mode, maybe? UNIMPLEMENTED_INTRINSIC(UnsafeCASLong) // High register pressure. UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) -UNIMPLEMENTED_INTRINSIC(StringIsEmpty) // Might not want to do these two anyways, inlining should -UNIMPLEMENTED_INTRINSIC(StringLength) // be good enough here. UNIMPLEMENTED_INTRINSIC(StringIndexOf) UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter) UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index 04e8fdcbe4..72d303c870 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -1006,7 +1006,7 @@ void IntrinsicCodeGeneratorARM64::VisitStringCompareTo(HInvoke* invoke) { vixl::MacroAssembler* masm = GetVIXLAssembler(); LocationSummary* locations = invoke->GetLocations(); - // Note that the null check must have be done earlier. + // Note that the null check must have been done earlier. DCHECK(!invoke->CanDoImplicitNullCheck()); Register argument = WRegisterFrom(locations->InAt(1)); @@ -1030,8 +1030,6 @@ void IntrinsicCodeGeneratorARM64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED } UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) -UNIMPLEMENTED_INTRINSIC(StringIsEmpty) // Might not want to do these two anyways, inlining should -UNIMPLEMENTED_INTRINSIC(StringLength) // be good enough here. UNIMPLEMENTED_INTRINSIC(StringIndexOf) UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter) UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) diff --git a/compiler/optimizing/intrinsics_list.h b/compiler/optimizing/intrinsics_list.h index 9cc77c6251..10f6e1d6c7 100644 --- a/compiler/optimizing/intrinsics_list.h +++ b/compiler/optimizing/intrinsics_list.h @@ -60,10 +60,8 @@ V(MemoryPokeShortNative, kStatic) \ V(StringCharAt, kDirect) \ V(StringCompareTo, kDirect) \ - V(StringIsEmpty, kDirect) \ V(StringIndexOf, kDirect) \ V(StringIndexOfAfter, kDirect) \ - V(StringLength, kDirect) \ V(UnsafeCASInt, kDirect) \ V(UnsafeCASLong, kDirect) \ V(UnsafeCASObject, kDirect) \ diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index 74edf50971..384737f55a 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -790,7 +790,7 @@ void IntrinsicCodeGeneratorX86::VisitStringCompareTo(HInvoke* invoke) { X86Assembler* assembler = GetAssembler(); LocationSummary* locations = invoke->GetLocations(); - // Note that the null check must have be done earlier. + // Note that the null check must have been done earlier. DCHECK(!invoke->CanDoImplicitNullCheck()); Register argument = locations->InAt(1).AsRegister<Register>(); @@ -1196,8 +1196,6 @@ UNIMPLEMENTED_INTRINSIC(MathCeil) UNIMPLEMENTED_INTRINSIC(MathRint) UNIMPLEMENTED_INTRINSIC(MathRoundDouble) UNIMPLEMENTED_INTRINSIC(MathRoundFloat) -UNIMPLEMENTED_INTRINSIC(StringIsEmpty) // Might not want to do these two anyways, inlining should -UNIMPLEMENTED_INTRINSIC(StringLength) // be good enough here. UNIMPLEMENTED_INTRINSIC(StringIndexOf) UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter) UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index e4f2518a16..736cea88cb 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -681,7 +681,7 @@ void IntrinsicCodeGeneratorX86_64::VisitStringCompareTo(HInvoke* invoke) { X86_64Assembler* assembler = GetAssembler(); LocationSummary* locations = invoke->GetLocations(); - // Note that the null check must have be done earlier. + // Note that the null check must have been done earlier. DCHECK(!invoke->CanDoImplicitNullCheck()); CpuRegister argument = locations->InAt(1).AsRegister<CpuRegister>(); @@ -1014,8 +1014,6 @@ UNIMPLEMENTED_INTRINSIC(MathCeil) UNIMPLEMENTED_INTRINSIC(MathRint) UNIMPLEMENTED_INTRINSIC(MathRoundDouble) UNIMPLEMENTED_INTRINSIC(MathRoundFloat) -UNIMPLEMENTED_INTRINSIC(StringIsEmpty) // Might not want to do these two anyways, inlining should -UNIMPLEMENTED_INTRINSIC(StringLength) // be good enough here. UNIMPLEMENTED_INTRINSIC(StringIndexOf) UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter) UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index c0df02b5cc..e474c49121 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -585,8 +585,13 @@ CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item, if (method != nullptr) { return method; } - return delegate_->Compile(code_item, access_flags, invoke_type, class_def_idx, method_idx, - class_loader, dex_file); + method = delegate_->Compile(code_item, access_flags, invoke_type, class_def_idx, method_idx, + class_loader, dex_file); + + if (method != nullptr) { + compilation_stats_.RecordStat(MethodCompilationStat::kCompiledQuick); + } + return method; } Compiler* CreateOptimizingCompiler(CompilerDriver* driver) { diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h index 22ec2a5167..b97a66719d 100644 --- a/compiler/optimizing/optimizing_compiler_stats.h +++ b/compiler/optimizing/optimizing_compiler_stats.h @@ -28,6 +28,7 @@ enum MethodCompilationStat { kAttemptCompilation = 0, kCompiledBaseline, kCompiledOptimized, + kCompiledQuick, kInlinedInvoke, kNotCompiledUnsupportedIsa, kNotCompiledPathological, @@ -65,16 +66,22 @@ class OptimizingCompilerStats { compile_stats_[kCompiledBaseline] * 100 / compile_stats_[kAttemptCompilation]; size_t optimized_percent = compile_stats_[kCompiledOptimized] * 100 / compile_stats_[kAttemptCompilation]; + size_t quick_percent = + compile_stats_[kCompiledQuick] * 100 / compile_stats_[kAttemptCompilation]; std::ostringstream oss; - oss << "Attempted compilation of " << compile_stats_[kAttemptCompilation] << " methods: " - << unoptimized_percent << "% (" << compile_stats_[kCompiledBaseline] << ") unoptimized, " - << optimized_percent << "% (" << compile_stats_[kCompiledOptimized] << ") optimized."; + oss << "Attempted compilation of " << compile_stats_[kAttemptCompilation] << " methods: "; + + oss << unoptimized_percent << "% (" << compile_stats_[kCompiledBaseline] << ") unoptimized, "; + oss << optimized_percent << "% (" << compile_stats_[kCompiledOptimized] << ") optimized, "; + oss << quick_percent << "% (" << compile_stats_[kCompiledQuick] << ") quick."; + + LOG(INFO) << oss.str(); + for (int i = 0; i < kLastStat; i++) { if (compile_stats_[i] != 0) { - oss << "\n" << PrintMethodCompilationStat(i) << ": " << compile_stats_[i]; + VLOG(compiler) << PrintMethodCompilationStat(i) << ": " << compile_stats_[i]; } } - LOG(INFO) << oss.str(); } } @@ -84,6 +91,7 @@ class OptimizingCompilerStats { case kAttemptCompilation : return "kAttemptCompilation"; case kCompiledBaseline : return "kCompiledBaseline"; case kCompiledOptimized : return "kCompiledOptimized"; + case kCompiledQuick : return "kCompiledQuick"; case kInlinedInvoke : return "kInlinedInvoke"; case kNotCompiledUnsupportedIsa : return "kNotCompiledUnsupportedIsa"; case kNotCompiledPathological : return "kNotCompiledPathological"; diff --git a/compiler/optimizing/parallel_move_resolver.cc b/compiler/optimizing/parallel_move_resolver.cc index 7d0641ec13..9df8f5640d 100644 --- a/compiler/optimizing/parallel_move_resolver.cc +++ b/compiler/optimizing/parallel_move_resolver.cc @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include <iostream> #include "parallel_move_resolver.h" #include "nodes.h" @@ -63,39 +64,42 @@ void ParallelMoveResolver::BuildInitialMoveList(HParallelMove* parallel_move) { } } +Location LowOf(Location location) { + if (location.IsRegisterPair()) { + return Location::RegisterLocation(location.low()); + } else if (location.IsFpuRegisterPair()) { + return Location::FpuRegisterLocation(location.low()); + } else if (location.IsDoubleStackSlot()) { + return Location::StackSlot(location.GetStackIndex()); + } else { + return Location::NoLocation(); + } +} + +Location HighOf(Location location) { + if (location.IsRegisterPair()) { + return Location::RegisterLocation(location.high()); + } else if (location.IsFpuRegisterPair()) { + return Location::FpuRegisterLocation(location.high()); + } else if (location.IsDoubleStackSlot()) { + return Location::StackSlot(location.GetHighStackIndex(4)); + } else { + return Location::NoLocation(); + } +} + // Update the source of `move`, knowing that `updated_location` has been swapped // with `new_source`. Note that `updated_location` can be a pair, therefore if // `move` is non-pair, we need to extract which register to use. static void UpdateSourceOf(MoveOperands* move, Location updated_location, Location new_source) { Location source = move->GetSource(); - if (new_source.GetKind() == source.GetKind()) { - DCHECK(updated_location.Equals(source)); - move->SetSource(new_source); - } else if (new_source.IsStackSlot() - || new_source.IsDoubleStackSlot() - || source.IsStackSlot() - || source.IsDoubleStackSlot()) { - // Stack slots never take part of a pair/non-pair swap. - DCHECK(updated_location.Equals(source)); + if (LowOf(updated_location).Equals(source)) { + move->SetSource(LowOf(new_source)); + } else if (HighOf(updated_location).Equals(source)) { + move->SetSource(HighOf(new_source)); + } else { + DCHECK(updated_location.Equals(source)) << updated_location << " " << source; move->SetSource(new_source); - } else if (source.IsRegister()) { - DCHECK(new_source.IsRegisterPair()) << new_source; - DCHECK(updated_location.IsRegisterPair()) << updated_location; - if (updated_location.low() == source.reg()) { - move->SetSource(Location::RegisterLocation(new_source.low())); - } else { - DCHECK_EQ(updated_location.high(), source.reg()); - move->SetSource(Location::RegisterLocation(new_source.high())); - } - } else if (source.IsFpuRegister()) { - DCHECK(new_source.IsFpuRegisterPair()) << new_source; - DCHECK(updated_location.IsFpuRegisterPair()) << updated_location; - if (updated_location.low() == source.reg()) { - move->SetSource(Location::FpuRegisterLocation(new_source.low())); - } else { - DCHECK_EQ(updated_location.high(), source.reg()); - move->SetSource(Location::FpuRegisterLocation(new_source.high())); - } } } diff --git a/compiler/optimizing/parallel_move_test.cc b/compiler/optimizing/parallel_move_test.cc index 817a44b184..5c502f7ef4 100644 --- a/compiler/optimizing/parallel_move_test.cc +++ b/compiler/optimizing/parallel_move_test.cc @@ -31,8 +31,13 @@ class TestParallelMoveResolver : public ParallelMoveResolver { message_ << "C"; } else if (location.IsPair()) { message_ << location.low() << "," << location.high(); - } else { + } else if (location.IsRegister()) { message_ << location.reg(); + } else if (location.IsStackSlot()) { + message_ << location.GetStackIndex() << "(sp)"; + } else { + message_ << "2x" << location.GetStackIndex() << "(sp)"; + DCHECK(location.IsDoubleStackSlot()) << location; } } @@ -279,6 +284,26 @@ TEST(ParallelMoveTest, Pairs) { resolver.EmitNativeCode(moves); ASSERT_STREQ("(0,1 <-> 2,3)", resolver.GetMessage().c_str()); } + + { + // Test involving registers used in single context and pair context. + TestParallelMoveResolver resolver(&allocator); + HParallelMove* moves = new (&allocator) HParallelMove(&allocator); + moves->AddMove( + Location::RegisterLocation(10), + Location::RegisterLocation(5), + nullptr); + moves->AddMove( + Location::RegisterPairLocation(4, 5), + Location::DoubleStackSlot(32), + nullptr); + moves->AddMove( + Location::DoubleStackSlot(32), + Location::RegisterPairLocation(10, 11), + nullptr); + resolver.EmitNativeCode(moves); + ASSERT_STREQ("(2x32(sp) <-> 10,11) (4,5 <-> 2x32(sp)) (4 -> 5)", resolver.GetMessage().c_str()); + } } } // namespace art diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc index cecc210cbf..cf38bd3f8c 100644 --- a/compiler/optimizing/register_allocator.cc +++ b/compiler/optimizing/register_allocator.cc @@ -213,7 +213,7 @@ void RegisterAllocator::ProcessInstruction(HInstruction* instruction) { LiveInterval* interval = LiveInterval::MakeTempInterval(allocator_, Primitive::kPrimInt); temp_intervals_.Add(interval); - interval->AddRange(position, position + 1); + interval->AddTempUse(instruction, i); unhandled_core_intervals_.Add(interval); break; } @@ -222,7 +222,7 @@ void RegisterAllocator::ProcessInstruction(HInstruction* instruction) { LiveInterval* interval = LiveInterval::MakeTempInterval(allocator_, Primitive::kPrimDouble); temp_intervals_.Add(interval); - interval->AddRange(position, position + 1); + interval->AddTempUse(instruction, i); if (codegen_->NeedsTwoRegisters(Primitive::kPrimDouble)) { interval->AddHighInterval(true); LiveInterval* high = interval->GetHighInterval(); @@ -851,6 +851,23 @@ bool RegisterAllocator::TrySplitNonPairOrUnalignedPairIntervalAt(size_t position return false; } +bool RegisterAllocator::PotentiallyRemoveOtherHalf(LiveInterval* interval, + GrowableArray<LiveInterval*>* intervals, + size_t index) { + if (interval->IsLowInterval()) { + DCHECK_EQ(intervals->Get(index), interval->GetHighInterval()); + intervals->DeleteAt(index); + return true; + } else if (interval->IsHighInterval()) { + DCHECK_GT(index, 0u); + DCHECK_EQ(intervals->Get(index - 1), interval->GetLowInterval()); + intervals->DeleteAt(index - 1); + return true; + } else { + return false; + } +} + // Find the register that is used the last, and spill the interval // that holds it. If the first use of `current` is after that register // we spill `current` instead. @@ -974,33 +991,17 @@ bool RegisterAllocator::AllocateBlockedReg(LiveInterval* current) { if (active->GetRegister() == reg) { DCHECK(!active->IsFixed()); LiveInterval* split = Split(active, current->GetStart()); - active_.DeleteAt(i); if (split != active) { handled_.Add(active); } + active_.DeleteAt(i); + PotentiallyRemoveOtherHalf(active, &active_, i); AddSorted(unhandled_, split); - - if (active->IsLowInterval() || active->IsHighInterval()) { - LiveInterval* other_half = active->IsLowInterval() - ? active->GetHighInterval() - : active->GetLowInterval(); - // We also need to remove the other half from the list of actives. - bool found = false; - for (size_t j = 0; j < active_.Size(); ++j) { - if (active_.Get(j) == other_half) { - found = true; - active_.DeleteAt(j); - handled_.Add(other_half); - break; - } - } - DCHECK(found); - } break; } } - for (size_t i = 0, e = inactive_.Size(); i < e; ++i) { + for (size_t i = 0; i < inactive_.Size(); ++i) { LiveInterval* inactive = inactive_.Get(i); if (inactive->GetRegister() == reg) { if (!current->IsSplit() && !inactive->IsFixed()) { @@ -1024,29 +1025,14 @@ bool RegisterAllocator::AllocateBlockedReg(LiveInterval* current) { // If it's inactive, it must start before the current interval. DCHECK_NE(split, inactive); inactive_.DeleteAt(i); + if (PotentiallyRemoveOtherHalf(inactive, &inactive_, i) && inactive->IsHighInterval()) { + // We have removed an entry prior to `inactive`. So we need to decrement. + --i; + } + // Decrement because we have removed `inactive` from the list. --i; - --e; handled_.Add(inactive); AddSorted(unhandled_, split); - - if (inactive->IsLowInterval() || inactive->IsHighInterval()) { - LiveInterval* other_half = inactive->IsLowInterval() - ? inactive->GetHighInterval() - : inactive->GetLowInterval(); - - // We also need to remove the other half from the list of inactives. - bool found = false; - for (size_t j = 0; j < inactive_.Size(); ++j) { - if (inactive_.Get(j) == other_half) { - found = true; - inactive_.DeleteAt(j); - --e; - handled_.Add(other_half); - break; - } - } - DCHECK(found); - } } } } @@ -1695,8 +1681,6 @@ void RegisterAllocator::Resolve() { } // Assign temp locations. - HInstruction* current = nullptr; - size_t temp_index = 0; for (size_t i = 0; i < temp_intervals_.Size(); ++i) { LiveInterval* temp = temp_intervals_.Get(i); if (temp->IsHighInterval()) { @@ -1704,25 +1688,20 @@ void RegisterAllocator::Resolve() { continue; } HInstruction* at = liveness_.GetTempUser(temp); - if (at != current) { - temp_index = 0; - current = at; - } + size_t temp_index = liveness_.GetTempIndex(temp); LocationSummary* locations = at->GetLocations(); switch (temp->GetType()) { case Primitive::kPrimInt: - locations->SetTempAt( - temp_index++, Location::RegisterLocation(temp->GetRegister())); + locations->SetTempAt(temp_index, Location::RegisterLocation(temp->GetRegister())); break; case Primitive::kPrimDouble: if (codegen_->NeedsTwoRegisters(Primitive::kPrimDouble)) { Location location = Location::FpuRegisterPairLocation( temp->GetRegister(), temp->GetHighInterval()->GetRegister()); - locations->SetTempAt(temp_index++, location); + locations->SetTempAt(temp_index, location); } else { - locations->SetTempAt( - temp_index++, Location::FpuRegisterLocation(temp->GetRegister())); + locations->SetTempAt(temp_index, Location::FpuRegisterLocation(temp->GetRegister())); } break; diff --git a/compiler/optimizing/register_allocator.h b/compiler/optimizing/register_allocator.h index fcc61128a6..717be75533 100644 --- a/compiler/optimizing/register_allocator.h +++ b/compiler/optimizing/register_allocator.h @@ -144,6 +144,13 @@ class RegisterAllocator { size_t first_register_use, size_t* next_use); + // If `interval` has another half, remove it from the list of `intervals`. + // `index` holds the index at which `interval` is in `intervals`. + // Returns whether there is another half. + bool PotentiallyRemoveOtherHalf(LiveInterval* interval, + GrowableArray<LiveInterval*>* intervals, + size_t index); + ArenaAllocator* const allocator_; CodeGenerator* const codegen_; const SsaLivenessAnalysis& liveness_; diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc index 56ccd717cf..0f3973e5fb 100644 --- a/compiler/optimizing/ssa_liveness_analysis.cc +++ b/compiler/optimizing/ssa_liveness_analysis.cc @@ -318,6 +318,8 @@ static int RegisterOrLowRegister(Location location) { int LiveInterval::FindFirstRegisterHint(size_t* free_until) const { DCHECK(!IsHighInterval()); + if (IsTemp()) return kNoRegister; + if (GetParent() == this && defined_by_ != nullptr) { // This is the first interval for the instruction. Try to find // a register based on its definition. diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h index b57029d1a7..bc78dc2e76 100644 --- a/compiler/optimizing/ssa_liveness_analysis.h +++ b/compiler/optimizing/ssa_liveness_analysis.h @@ -180,6 +180,15 @@ class LiveInterval : public ArenaObject<kArenaAllocMisc> { // This interval is the result of a split. bool IsSplit() const { return parent_ != this; } + void AddTempUse(HInstruction* instruction, size_t temp_index) { + DCHECK(IsTemp()); + DCHECK(first_use_ == nullptr) << "A temporary can only have one user"; + size_t position = instruction->GetLifetimePosition(); + first_use_ = new (allocator_) UsePosition( + instruction, temp_index, /* is_environment */ false, position, first_use_); + AddRange(position, position + 1); + } + void AddUse(HInstruction* instruction, size_t input_index, bool is_environment) { // Set the use within the instruction. size_t position = instruction->GetLifetimePosition() + 1; @@ -856,7 +865,15 @@ class SsaLivenessAnalysis : public ValueObject { HInstruction* GetTempUser(LiveInterval* temp) const { // A temporary shares the same lifetime start as the instruction that requires it. DCHECK(temp->IsTemp()); - return GetInstructionFromPosition(temp->GetStart() / 2); + HInstruction* user = GetInstructionFromPosition(temp->GetStart() / 2); + DCHECK_EQ(user, temp->GetFirstUse()->GetUser()); + return user; + } + + size_t GetTempIndex(LiveInterval* temp) const { + // We use the input index to store the index of the temporary in the user's temporary list. + DCHECK(temp->IsTemp()); + return temp->GetFirstUse()->GetInputIndex(); } size_t GetMaxLifetimePosition() const { diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h index 5818a37a46..a73c8d77f3 100644 --- a/compiler/optimizing/stack_map_stream.h +++ b/compiler/optimizing/stack_map_stream.h @@ -27,6 +27,32 @@ namespace art { +// Helper to build art::StackMapStream::LocationCatalogEntriesIndices. +class LocationCatalogEntriesIndicesEmptyFn { + public: + void MakeEmpty(std::pair<DexRegisterLocation, size_t>& item) const { + item.first = DexRegisterLocation::None(); + } + bool IsEmpty(const std::pair<DexRegisterLocation, size_t>& item) const { + return item.first == DexRegisterLocation::None(); + } +}; + +// Hash function for art::StackMapStream::LocationCatalogEntriesIndices. +// This hash function does not create collisions. +class DexRegisterLocationHashFn { + public: + size_t operator()(DexRegisterLocation key) const { + // Concatenate `key`s fields to create a 64-bit value to be hashed. + int64_t kind_and_value = + (static_cast<int64_t>(key.kind_) << 32) | static_cast<int64_t>(key.value_); + return inner_hash_fn_(kind_and_value); + } + private: + std::hash<int64_t> inner_hash_fn_; +}; + + /** * Collects and builds stack maps for a method. All the stack maps * for a method are placed in a CodeInfo object. @@ -36,11 +62,13 @@ class StackMapStream : public ValueObject { explicit StackMapStream(ArenaAllocator* allocator) : allocator_(allocator), stack_maps_(allocator, 10), + location_catalog_entries_(allocator, 4), dex_register_locations_(allocator, 10 * 4), inline_infos_(allocator, 2), stack_mask_max_(-1), dex_pc_max_(0), native_pc_offset_max_(0), + register_mask_max_(0), number_of_stack_maps_with_inline_info_(0), dex_map_hash_to_stack_map_indices_(std::less<uint32_t>(), allocator->Adapter()) {} @@ -101,6 +129,7 @@ class StackMapStream : public ValueObject { dex_pc_max_ = std::max(dex_pc_max_, dex_pc); native_pc_offset_max_ = std::max(native_pc_offset_max_, native_pc_offset); + register_mask_max_ = std::max(register_mask_max_, register_mask); } void AddInlineInfoEntry(uint32_t method_index) { @@ -111,6 +140,7 @@ class StackMapStream : public ValueObject { size_t ComputeNeededSize() { size_t size = CodeInfo::kFixedSize + + ComputeDexRegisterLocationCatalogSize() + ComputeStackMapsSize() + ComputeDexRegisterMapsSize() + ComputeInlineInfoSize(); @@ -128,24 +158,43 @@ class StackMapStream : public ValueObject { ComputeInlineInfoSize(), ComputeDexRegisterMapsSize(), dex_pc_max_, - native_pc_offset_max_); + native_pc_offset_max_, + register_mask_max_); + } + + // Compute the size of the Dex register location catalog of `entry`. + size_t ComputeDexRegisterLocationCatalogSize() const { + size_t size = DexRegisterLocationCatalog::kFixedSize; + for (size_t location_catalog_entry_index = 0; + location_catalog_entry_index < location_catalog_entries_.Size(); + ++location_catalog_entry_index) { + DexRegisterLocation dex_register_location = + location_catalog_entries_.Get(location_catalog_entry_index); + size += DexRegisterLocationCatalog::EntrySize(dex_register_location); + } + return size; } - // Compute the size of the Dex register map of `entry`. size_t ComputeDexRegisterMapSize(const StackMapEntry& entry) const { + // Size of the map in bytes. size_t size = DexRegisterMap::kFixedSize; - // Add the bit mask for the dex register liveness. - size += DexRegisterMap::LiveBitMaskSize(entry.num_dex_registers); - for (size_t dex_register_number = 0, index_in_dex_register_locations = 0; + // Add the live bit mask for the Dex register liveness. + size += DexRegisterMap::GetLiveBitMaskSize(entry.num_dex_registers); + // Compute the size of the set of live Dex register entries. + size_t number_of_live_dex_registers = 0; + for (size_t dex_register_number = 0; dex_register_number < entry.num_dex_registers; ++dex_register_number) { if (entry.live_dex_registers_mask->IsBitSet(dex_register_number)) { - DexRegisterLocation dex_register_location = dex_register_locations_.Get( - entry.dex_register_locations_start_index + index_in_dex_register_locations); - size += DexRegisterMap::EntrySize(dex_register_location); - index_in_dex_register_locations++; + ++number_of_live_dex_registers; } } + size_t map_entries_size_in_bits = + DexRegisterMap::SingleEntrySizeInBits(location_catalog_entries_.Size()) + * number_of_live_dex_registers; + size_t map_entries_size_in_bytes = + RoundUp(map_entries_size_in_bits, kBitsPerByte) / kBitsPerByte; + size += map_entries_size_in_bytes; return size; } @@ -168,8 +217,16 @@ class StackMapStream : public ValueObject { + (number_of_stack_maps_with_inline_info_ * InlineInfo::kFixedSize); } + size_t ComputeDexRegisterLocationCatalogStart() const { + return CodeInfo::kFixedSize; + } + + size_t ComputeStackMapsStart() const { + return ComputeDexRegisterLocationCatalogStart() + ComputeDexRegisterLocationCatalogSize(); + } + size_t ComputeDexRegisterMapsStart() { - return CodeInfo::kFixedSize + ComputeStackMapsSize(); + return ComputeStackMapsStart() + ComputeStackMapsSize(); } size_t ComputeInlineInfoStart() { @@ -194,11 +251,32 @@ class StackMapStream : public ValueObject { ComputeInlineInfoStart(), inline_info_size); - code_info.SetEncoding( - inline_info_size, dex_register_map_size, dex_pc_max_, native_pc_offset_max_); + code_info.SetEncoding(inline_info_size, + dex_register_map_size, + dex_pc_max_, + native_pc_offset_max_, + register_mask_max_); code_info.SetNumberOfStackMaps(stack_maps_.Size()); code_info.SetStackMaskSize(stack_mask_size); - DCHECK_EQ(code_info.StackMapsSize(), ComputeStackMapsSize()); + DCHECK_EQ(code_info.GetStackMapsSize(), ComputeStackMapsSize()); + + // Set the Dex register location catalog. + code_info.SetNumberOfDexRegisterLocationCatalogEntries( + location_catalog_entries_.Size()); + MemoryRegion dex_register_location_catalog_region = region.Subregion( + ComputeDexRegisterLocationCatalogStart(), + ComputeDexRegisterLocationCatalogSize()); + DexRegisterLocationCatalog dex_register_location_catalog(dex_register_location_catalog_region); + // Offset in `dex_register_location_catalog` where to store the next + // register location. + size_t location_catalog_offset = DexRegisterLocationCatalog::kFixedSize; + for (size_t i = 0, e = location_catalog_entries_.Size(); i < e; ++i) { + DexRegisterLocation dex_register_location = location_catalog_entries_.Get(i); + dex_register_location_catalog.SetRegisterInfo(location_catalog_offset, dex_register_location); + location_catalog_offset += DexRegisterLocationCatalog::EntrySize(dex_register_location); + } + // Ensure we reached the end of the Dex registers location_catalog. + DCHECK_EQ(location_catalog_offset, dex_register_location_catalog_region.size()); uintptr_t next_dex_register_map_offset = 0; uintptr_t next_inline_info_offset = 0; @@ -234,25 +312,25 @@ class StackMapStream : public ValueObject { stack_map.SetDexRegisterMapOffset( code_info, register_region.start() - dex_register_locations_region.start()); - // Offset in `dex_register_map` where to store the next register entry. - size_t offset = DexRegisterMap::kFixedSize; - dex_register_map.SetLiveBitMask(offset, - entry.num_dex_registers, - *entry.live_dex_registers_mask); - offset += DexRegisterMap::LiveBitMaskSize(entry.num_dex_registers); + // Set the live bit mask. + dex_register_map.SetLiveBitMask(entry.num_dex_registers, *entry.live_dex_registers_mask); + + // Set the dex register location mapping data. for (size_t dex_register_number = 0, index_in_dex_register_locations = 0; dex_register_number < entry.num_dex_registers; ++dex_register_number) { if (entry.live_dex_registers_mask->IsBitSet(dex_register_number)) { - DexRegisterLocation dex_register_location = dex_register_locations_.Get( - entry.dex_register_locations_start_index + index_in_dex_register_locations); - dex_register_map.SetRegisterInfo(offset, dex_register_location); - offset += DexRegisterMap::EntrySize(dex_register_location); + size_t location_catalog_entry_index = + dex_register_locations_.Get(entry.dex_register_locations_start_index + + index_in_dex_register_locations); + dex_register_map.SetLocationCatalogEntryIndex( + index_in_dex_register_locations, + location_catalog_entry_index, + entry.num_dex_registers, + location_catalog_entries_.Size()); ++index_in_dex_register_locations; } } - // Ensure we reached the end of the Dex registers region. - DCHECK_EQ(offset, register_region.size()); } } @@ -282,12 +360,31 @@ class StackMapStream : public ValueObject { } void AddDexRegisterEntry(uint16_t dex_register, DexRegisterLocation::Kind kind, int32_t value) { + StackMapEntry entry = stack_maps_.Get(stack_maps_.Size() - 1); + DCHECK_LT(dex_register, entry.num_dex_registers); + if (kind != DexRegisterLocation::Kind::kNone) { // Ensure we only use non-compressed location kind at this stage. DCHECK(DexRegisterLocation::IsShortLocationKind(kind)) << DexRegisterLocation::PrettyDescriptor(kind); - dex_register_locations_.Add(DexRegisterLocation(kind, value)); - StackMapEntry entry = stack_maps_.Get(stack_maps_.Size() - 1); + DexRegisterLocation location(kind, value); + + // Look for Dex register `location` in the location catalog (using the + // companion hash map of locations to indices). Use its index if it + // is already in the location catalog. If not, insert it (in the + // location catalog and the hash map) and use the newly created index. + auto it = location_catalog_entries_indices_.Find(location); + if (it != location_catalog_entries_indices_.end()) { + // Retrieve the index from the hash map. + dex_register_locations_.Add(it->second); + } else { + // Create a new entry in the location catalog and the hash map. + size_t index = location_catalog_entries_.Size(); + location_catalog_entries_.Add(location); + dex_register_locations_.Add(index); + location_catalog_entries_indices_.Insert(std::make_pair(location, index)); + } + entry.live_dex_registers_mask->SetBit(dex_register); entry.dex_register_map_hash += (1 << dex_register); entry.dex_register_map_hash += static_cast<uint32_t>(value); @@ -354,9 +451,9 @@ class StackMapStream : public ValueObject { return false; } if (a.live_dex_registers_mask->IsBitSet(i)) { - DexRegisterLocation a_loc = dex_register_locations_.Get( + size_t a_loc = dex_register_locations_.Get( a.dex_register_locations_start_index + index_in_dex_register_locations); - DexRegisterLocation b_loc = dex_register_locations_.Get( + size_t b_loc = dex_register_locations_.Get( b.dex_register_locations_start_index + index_in_dex_register_locations); if (a_loc != b_loc) { return false; @@ -369,21 +466,29 @@ class StackMapStream : public ValueObject { ArenaAllocator* allocator_; GrowableArray<StackMapEntry> stack_maps_; - GrowableArray<DexRegisterLocation> dex_register_locations_; + + // A catalog of unique [location_kind, register_value] pairs (per method). + GrowableArray<DexRegisterLocation> location_catalog_entries_; + // Map from Dex register location catalog entries to their indices in the + // location catalog. + typedef HashMap<DexRegisterLocation, size_t, LocationCatalogEntriesIndicesEmptyFn, + DexRegisterLocationHashFn> LocationCatalogEntriesIndices; + LocationCatalogEntriesIndices location_catalog_entries_indices_; + + // A set of concatenated maps of Dex register locations indices to + // `location_catalog_entries_`. + GrowableArray<size_t> dex_register_locations_; GrowableArray<InlineInfoEntry> inline_infos_; int stack_mask_max_; uint32_t dex_pc_max_; uint32_t native_pc_offset_max_; + uint32_t register_mask_max_; size_t number_of_stack_maps_with_inline_info_; ArenaSafeMap<uint32_t, GrowableArray<uint32_t>> dex_map_hash_to_stack_map_indices_; static constexpr uint32_t kNoSameDexMapFound = -1; - ART_FRIEND_TEST(StackMapTest, Test1); - ART_FRIEND_TEST(StackMapTest, Test2); - ART_FRIEND_TEST(StackMapTest, TestNonLiveDexRegisters); - DISALLOW_COPY_AND_ASSIGN(StackMapStream); }; diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc index e5a9790254..8d160bc81e 100644 --- a/compiler/optimizing/stack_map_test.cc +++ b/compiler/optimizing/stack_map_test.cc @@ -31,6 +31,8 @@ static bool SameBits(MemoryRegion region, const BitVector& bit_vector) { return true; } +using Kind = DexRegisterLocation::Kind; + TEST(StackMapTest, Test1) { ArenaPool pool; ArenaAllocator arena(&pool); @@ -39,8 +41,8 @@ TEST(StackMapTest, Test1) { ArenaBitVector sp_mask(&arena, 0, false); size_t number_of_dex_registers = 2; stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); - stream.AddDexRegisterEntry(0, DexRegisterLocation::Kind::kInStack, 0); - stream.AddDexRegisterEntry(1, DexRegisterLocation::Kind::kConstant, -2); + stream.AddDexRegisterEntry(0, Kind::kInStack, 0); // Short location. + stream.AddDexRegisterEntry(1, Kind::kConstant, -2); // Short location. size_t size = stream.ComputeNeededSize(); void* memory = arena.Alloc(size, kArenaAllocMisc); @@ -51,6 +53,16 @@ TEST(StackMapTest, Test1) { ASSERT_EQ(0u, code_info.GetStackMaskSize()); ASSERT_EQ(1u, code_info.GetNumberOfStackMaps()); + uint32_t number_of_location_catalog_entries = + code_info.GetNumberOfDexRegisterLocationCatalogEntries(); + ASSERT_EQ(2u, number_of_location_catalog_entries); + DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(); + // The Dex register location catalog contains: + // - one 1-byte short Dex register location, and + // - one 5-byte large Dex register location. + size_t expected_location_catalog_size = 1u + 5u; + ASSERT_EQ(expected_location_catalog_size, location_catalog.Size()); + StackMap stack_map = code_info.GetStackMapAt(0); ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0))); ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64))); @@ -62,14 +74,40 @@ TEST(StackMapTest, Test1) { ASSERT_TRUE(SameBits(stack_mask, sp_mask)); ASSERT_TRUE(stack_map.HasDexRegisterMap(code_info)); - DexRegisterMap dex_registers = code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); - ASSERT_EQ(7u, dex_registers.Size()); - DexRegisterLocation location0 = dex_registers.GetLocationKindAndValue(0, number_of_dex_registers); - DexRegisterLocation location1 = dex_registers.GetLocationKindAndValue(1, number_of_dex_registers); - ASSERT_EQ(DexRegisterLocation::Kind::kInStack, location0.GetKind()); - ASSERT_EQ(DexRegisterLocation::Kind::kConstant, location1.GetKind()); - ASSERT_EQ(DexRegisterLocation::Kind::kInStack, location0.GetInternalKind()); - ASSERT_EQ(DexRegisterLocation::Kind::kConstantLargeValue, location1.GetInternalKind()); + DexRegisterMap dex_register_map = + code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); + ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0)); + ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1)); + ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers)); + // The Dex register map contains: + // - one 1-byte live bit mask, and + // - one 1-byte set of location catalog entry indices composed of two 2-bit values. + size_t expected_dex_register_map_size = 1u + 1u; + ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size()); + + ASSERT_EQ(Kind::kInStack, + dex_register_map.GetLocationKind(0, number_of_dex_registers, code_info)); + ASSERT_EQ(Kind::kConstant, + dex_register_map.GetLocationKind(1, number_of_dex_registers, code_info)); + ASSERT_EQ(Kind::kInStack, + dex_register_map.GetLocationInternalKind(0, number_of_dex_registers, code_info)); + ASSERT_EQ(Kind::kConstantLargeValue, + dex_register_map.GetLocationInternalKind(1, number_of_dex_registers, code_info)); + ASSERT_EQ(0, dex_register_map.GetStackOffsetInBytes(0, number_of_dex_registers, code_info)); + ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info)); + + size_t index0 = dex_register_map.GetLocationCatalogEntryIndex( + 0, number_of_dex_registers, number_of_location_catalog_entries); + size_t index1 = dex_register_map.GetLocationCatalogEntryIndex( + 1, number_of_dex_registers, number_of_location_catalog_entries); + ASSERT_EQ(0u, index0); + ASSERT_EQ(1u, index1); + DexRegisterLocation location0 = location_catalog.GetDexRegisterLocation(index0); + DexRegisterLocation location1 = location_catalog.GetDexRegisterLocation(index1); + ASSERT_EQ(Kind::kInStack, location0.GetKind()); + ASSERT_EQ(Kind::kConstant, location1.GetKind()); + ASSERT_EQ(Kind::kInStack, location0.GetInternalKind()); + ASSERT_EQ(Kind::kConstantLargeValue, location1.GetInternalKind()); ASSERT_EQ(0, location0.GetValue()); ASSERT_EQ(-2, location1.GetValue()); @@ -86,8 +124,8 @@ TEST(StackMapTest, Test2) { sp_mask1.SetBit(4); size_t number_of_dex_registers = 2; stream.AddStackMapEntry(0, 64, 0x3, &sp_mask1, number_of_dex_registers, 2); - stream.AddDexRegisterEntry(0, DexRegisterLocation::Kind::kInStack, 0); - stream.AddDexRegisterEntry(1, DexRegisterLocation::Kind::kConstant, -2); + stream.AddDexRegisterEntry(0, Kind::kInStack, 0); // Short location. + stream.AddDexRegisterEntry(1, Kind::kConstant, -2); // Large location. stream.AddInlineInfoEntry(42); stream.AddInlineInfoEntry(82); @@ -95,8 +133,8 @@ TEST(StackMapTest, Test2) { sp_mask2.SetBit(3); sp_mask1.SetBit(8); stream.AddStackMapEntry(1, 128, 0xFF, &sp_mask2, number_of_dex_registers, 0); - stream.AddDexRegisterEntry(0, DexRegisterLocation::Kind::kInRegister, 18); - stream.AddDexRegisterEntry(1, DexRegisterLocation::Kind::kInFpuRegister, 3); + stream.AddDexRegisterEntry(0, Kind::kInRegister, 18); // Short location. + stream.AddDexRegisterEntry(1, Kind::kInFpuRegister, 3); // Short location. size_t size = stream.ComputeNeededSize(); void* memory = arena.Alloc(size, kArenaAllocMisc); @@ -107,6 +145,16 @@ TEST(StackMapTest, Test2) { ASSERT_EQ(1u, code_info.GetStackMaskSize()); ASSERT_EQ(2u, code_info.GetNumberOfStackMaps()); + uint32_t number_of_location_catalog_entries = + code_info.GetNumberOfDexRegisterLocationCatalogEntries(); + ASSERT_EQ(4u, number_of_location_catalog_entries); + DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(); + // The Dex register location catalog contains: + // - three 1-byte short Dex register locations, and + // - one 5-byte large Dex register location. + size_t expected_location_catalog_size = 3u * 1u + 5u; + ASSERT_EQ(expected_location_catalog_size, location_catalog.Size()); + // First stack map. { StackMap stack_map = code_info.GetStackMapAt(0); @@ -120,17 +168,40 @@ TEST(StackMapTest, Test2) { ASSERT_TRUE(SameBits(stack_mask, sp_mask1)); ASSERT_TRUE(stack_map.HasDexRegisterMap(code_info)); - DexRegisterMap dex_registers = + DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); - ASSERT_EQ(7u, dex_registers.Size()); - DexRegisterLocation location0 = - dex_registers.GetLocationKindAndValue(0, number_of_dex_registers); - DexRegisterLocation location1 = - dex_registers.GetLocationKindAndValue(1, number_of_dex_registers); - ASSERT_EQ(DexRegisterLocation::Kind::kInStack, location0.GetKind()); - ASSERT_EQ(DexRegisterLocation::Kind::kConstant, location1.GetKind()); - ASSERT_EQ(DexRegisterLocation::Kind::kInStack, location0.GetInternalKind()); - ASSERT_EQ(DexRegisterLocation::Kind::kConstantLargeValue, location1.GetInternalKind()); + ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0)); + ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1)); + ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers)); + // The Dex register map contains: + // - one 1-byte live bit mask, and + // - one 1-byte set of location catalog entry indices composed of two 2-bit values. + size_t expected_dex_register_map_size = 1u + 1u; + ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size()); + + ASSERT_EQ(Kind::kInStack, + dex_register_map.GetLocationKind(0, number_of_dex_registers, code_info)); + ASSERT_EQ(Kind::kConstant, + dex_register_map.GetLocationKind(1, number_of_dex_registers, code_info)); + ASSERT_EQ(Kind::kInStack, + dex_register_map.GetLocationInternalKind(0, number_of_dex_registers, code_info)); + ASSERT_EQ(Kind::kConstantLargeValue, + dex_register_map.GetLocationInternalKind(1, number_of_dex_registers, code_info)); + ASSERT_EQ(0, dex_register_map.GetStackOffsetInBytes(0, number_of_dex_registers, code_info)); + ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info)); + + size_t index0 = dex_register_map.GetLocationCatalogEntryIndex( + 0, number_of_dex_registers, number_of_location_catalog_entries); + size_t index1 = dex_register_map.GetLocationCatalogEntryIndex( + 1, number_of_dex_registers, number_of_location_catalog_entries); + ASSERT_EQ(0u, index0); + ASSERT_EQ(1u, index1); + DexRegisterLocation location0 = location_catalog.GetDexRegisterLocation(index0); + DexRegisterLocation location1 = location_catalog.GetDexRegisterLocation(index1); + ASSERT_EQ(Kind::kInStack, location0.GetKind()); + ASSERT_EQ(Kind::kConstant, location1.GetKind()); + ASSERT_EQ(Kind::kInStack, location0.GetInternalKind()); + ASSERT_EQ(Kind::kConstantLargeValue, location1.GetInternalKind()); ASSERT_EQ(0, location0.GetValue()); ASSERT_EQ(-2, location1.GetValue()); @@ -154,17 +225,40 @@ TEST(StackMapTest, Test2) { ASSERT_TRUE(SameBits(stack_mask, sp_mask2)); ASSERT_TRUE(stack_map.HasDexRegisterMap(code_info)); - DexRegisterMap dex_registers = + DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); - ASSERT_EQ(3u, dex_registers.Size()); - DexRegisterLocation location0 = - dex_registers.GetLocationKindAndValue(0, number_of_dex_registers); - DexRegisterLocation location1 = - dex_registers.GetLocationKindAndValue(1, number_of_dex_registers); - ASSERT_EQ(DexRegisterLocation::Kind::kInRegister, location0.GetKind()); - ASSERT_EQ(DexRegisterLocation::Kind::kInFpuRegister, location1.GetKind()); - ASSERT_EQ(DexRegisterLocation::Kind::kInRegister, location0.GetInternalKind()); - ASSERT_EQ(DexRegisterLocation::Kind::kInFpuRegister, location1.GetInternalKind()); + ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0)); + ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1)); + ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers)); + // The Dex register map contains: + // - one 1-byte live bit mask, and + // - one 1-byte set of location catalog entry indices composed of two 2-bit values. + size_t expected_dex_register_map_size = 1u + 1u; + ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size()); + + ASSERT_EQ(Kind::kInRegister, + dex_register_map.GetLocationKind(0, number_of_dex_registers, code_info)); + ASSERT_EQ(Kind::kInFpuRegister, + dex_register_map.GetLocationKind(1, number_of_dex_registers, code_info)); + ASSERT_EQ(Kind::kInRegister, + dex_register_map.GetLocationInternalKind(0, number_of_dex_registers, code_info)); + ASSERT_EQ(Kind::kInFpuRegister, + dex_register_map.GetLocationInternalKind(1, number_of_dex_registers, code_info)); + ASSERT_EQ(18, dex_register_map.GetMachineRegister(0, number_of_dex_registers, code_info)); + ASSERT_EQ(3, dex_register_map.GetMachineRegister(1, number_of_dex_registers, code_info)); + + size_t index0 = dex_register_map.GetLocationCatalogEntryIndex( + 0, number_of_dex_registers, number_of_location_catalog_entries); + size_t index1 = dex_register_map.GetLocationCatalogEntryIndex( + 1, number_of_dex_registers, number_of_location_catalog_entries); + ASSERT_EQ(2u, index0); + ASSERT_EQ(3u, index1); + DexRegisterLocation location0 = location_catalog.GetDexRegisterLocation(index0); + DexRegisterLocation location1 = location_catalog.GetDexRegisterLocation(index1); + ASSERT_EQ(Kind::kInRegister, location0.GetKind()); + ASSERT_EQ(Kind::kInFpuRegister, location1.GetKind()); + ASSERT_EQ(Kind::kInRegister, location0.GetInternalKind()); + ASSERT_EQ(Kind::kInFpuRegister, location1.GetInternalKind()); ASSERT_EQ(18, location0.GetValue()); ASSERT_EQ(3, location1.GetValue()); @@ -180,8 +274,8 @@ TEST(StackMapTest, TestNonLiveDexRegisters) { ArenaBitVector sp_mask(&arena, 0, false); uint32_t number_of_dex_registers = 2; stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); - stream.AddDexRegisterEntry(0, DexRegisterLocation::Kind::kNone, 0); - stream.AddDexRegisterEntry(1, DexRegisterLocation::Kind::kConstant, -2); + stream.AddDexRegisterEntry(0, Kind::kNone, 0); // No location. + stream.AddDexRegisterEntry(1, Kind::kConstant, -2); // Large location. size_t size = stream.ComputeNeededSize(); void* memory = arena.Alloc(size, kArenaAllocMisc); @@ -189,14 +283,62 @@ TEST(StackMapTest, TestNonLiveDexRegisters) { stream.FillIn(region); CodeInfo code_info(region); + ASSERT_EQ(0u, code_info.GetStackMaskSize()); + ASSERT_EQ(1u, code_info.GetNumberOfStackMaps()); + + uint32_t number_of_location_catalog_entries = + code_info.GetNumberOfDexRegisterLocationCatalogEntries(); + ASSERT_EQ(1u, number_of_location_catalog_entries); + DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(); + // The Dex register location catalog contains: + // - one 5-byte large Dex register location. + size_t expected_location_catalog_size = 5u; + ASSERT_EQ(expected_location_catalog_size, location_catalog.Size()); + StackMap stack_map = code_info.GetStackMapAt(0); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64))); + ASSERT_EQ(0u, stack_map.GetDexPc(code_info)); + ASSERT_EQ(64u, stack_map.GetNativePcOffset(code_info)); + ASSERT_EQ(0x3u, stack_map.GetRegisterMask(code_info)); + ASSERT_TRUE(stack_map.HasDexRegisterMap(code_info)); - DexRegisterMap dex_registers = code_info.GetDexRegisterMapOf(stack_map, 2); - ASSERT_EQ(DexRegisterLocation::Kind::kNone, - dex_registers.GetLocationKind(0, number_of_dex_registers)); - ASSERT_EQ(DexRegisterLocation::Kind::kConstant, - dex_registers.GetLocationKind(1, number_of_dex_registers)); - ASSERT_EQ(-2, dex_registers.GetConstant(1, number_of_dex_registers)); + DexRegisterMap dex_register_map = + code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); + ASSERT_FALSE(dex_register_map.IsDexRegisterLive(0)); + ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1)); + ASSERT_EQ(1u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers)); + // The Dex register map contains: + // - one 1-byte live bit mask. + // No space is allocated for the sole location catalog entry index, as it is useless. + size_t expected_dex_register_map_size = 1u + 0u; + ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size()); + + ASSERT_EQ(Kind::kNone, + dex_register_map.GetLocationKind(0, number_of_dex_registers, code_info)); + ASSERT_EQ(Kind::kConstant, + dex_register_map.GetLocationKind(1, number_of_dex_registers, code_info)); + ASSERT_EQ(Kind::kNone, + dex_register_map.GetLocationInternalKind(0, number_of_dex_registers, code_info)); + ASSERT_EQ(Kind::kConstantLargeValue, + dex_register_map.GetLocationInternalKind(1, number_of_dex_registers, code_info)); + ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info)); + + size_t index0 = dex_register_map.GetLocationCatalogEntryIndex( + 0, number_of_dex_registers, number_of_location_catalog_entries); + size_t index1 = dex_register_map.GetLocationCatalogEntryIndex( + 1, number_of_dex_registers, number_of_location_catalog_entries); + ASSERT_EQ(DexRegisterLocationCatalog::kNoLocationEntryIndex, index0); + ASSERT_EQ(0u, index1); + DexRegisterLocation location0 = location_catalog.GetDexRegisterLocation(index0); + DexRegisterLocation location1 = location_catalog.GetDexRegisterLocation(index1); + ASSERT_EQ(Kind::kNone, location0.GetKind()); + ASSERT_EQ(Kind::kConstant, location1.GetKind()); + ASSERT_EQ(Kind::kNone, location0.GetInternalKind()); + ASSERT_EQ(Kind::kConstantLargeValue, location1.GetInternalKind()); + ASSERT_EQ(0, location0.GetValue()); + ASSERT_EQ(-2, location1.GetValue()); + ASSERT_FALSE(stack_map.HasInlineInfo(code_info)); } @@ -209,14 +351,21 @@ TEST(StackMapTest, DexRegisterMapOffsetOverflow) { StackMapStream stream(&arena); ArenaBitVector sp_mask(&arena, 0, false); - uint32_t number_of_dex_registers = 0xEA; + uint32_t number_of_dex_registers = 1024; + // Create the first stack map (and its Dex register map). stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); - for (uint32_t i = 0; i < number_of_dex_registers - 9; ++i) { - stream.AddDexRegisterEntry(i, DexRegisterLocation::Kind::kConstant, 0); + uint32_t number_of_dex_live_registers_in_dex_register_map_0 = number_of_dex_registers - 8; + for (uint32_t i = 0; i < number_of_dex_live_registers_in_dex_register_map_0; ++i) { + // Use two different Dex register locations to populate this map, + // as using a single value (in the whole CodeInfo object) would + // make this Dex register mapping data empty (see + // art::DexRegisterMap::SingleEntrySizeInBits). + stream.AddDexRegisterEntry(i, Kind::kConstant, i % 2); // Short location. } + // Create the second stack map (and its Dex register map). stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); for (uint32_t i = 0; i < number_of_dex_registers; ++i) { - stream.AddDexRegisterEntry(i, DexRegisterLocation::Kind::kConstant, 0); + stream.AddDexRegisterEntry(i, Kind::kConstant, 0); // Short location. } size_t size = stream.ComputeNeededSize(); @@ -225,10 +374,35 @@ TEST(StackMapTest, DexRegisterMapOffsetOverflow) { stream.FillIn(region); CodeInfo code_info(region); - StackMap stack_map = code_info.GetStackMapAt(1); - ASSERT_TRUE(stack_map.HasDexRegisterMap(code_info)); - ASSERT_NE(stack_map.GetDexRegisterMapOffset(code_info), StackMap::kNoDexRegisterMap); - ASSERT_EQ(stack_map.GetDexRegisterMapOffset(code_info), StackMap::kNoDexRegisterMapSmallEncoding); + // The location catalog contains two entries (DexRegisterLocation(kConstant, 0) + // and DexRegisterLocation(kConstant, 1)), therefore the location catalog index + // has a size of 1 bit. + uint32_t number_of_location_catalog_entries = + code_info.GetNumberOfDexRegisterLocationCatalogEntries(); + ASSERT_EQ(2u, number_of_location_catalog_entries); + ASSERT_EQ(1u, DexRegisterMap::SingleEntrySizeInBits(number_of_location_catalog_entries)); + + // The first Dex register map contains: + // - a live register bit mask for 1024 registers (that is, 128 bytes of + // data); and + // - Dex register mapping information for 1016 1-bit Dex (live) register + // locations (that is, 127 bytes of data). + // Hence it has a size of 255 bytes, and therefore... + ASSERT_EQ(128u, DexRegisterMap::GetLiveBitMaskSize(number_of_dex_registers)); + StackMap stack_map0 = code_info.GetStackMapAt(0); + DexRegisterMap dex_register_map0 = + code_info.GetDexRegisterMapOf(stack_map0, number_of_dex_registers); + ASSERT_EQ(127u, dex_register_map0.GetLocationMappingDataSize(number_of_dex_registers, + number_of_location_catalog_entries)); + ASSERT_EQ(255u, dex_register_map0.Size()); + + StackMap stack_map1 = code_info.GetStackMapAt(1); + ASSERT_TRUE(stack_map1.HasDexRegisterMap(code_info)); + // ...the offset of the second Dex register map (relative to the + // beginning of the Dex register maps region) is 255 (i.e., + // kNoDexRegisterMapSmallEncoding). + ASSERT_NE(stack_map1.GetDexRegisterMapOffset(code_info), StackMap::kNoDexRegisterMap); + ASSERT_EQ(stack_map1.GetDexRegisterMapOffset(code_info), 0xFFu); } TEST(StackMapTest, TestShareDexRegisterMap) { @@ -240,16 +414,16 @@ TEST(StackMapTest, TestShareDexRegisterMap) { uint32_t number_of_dex_registers = 2; // First stack map. stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); - stream.AddDexRegisterEntry(0, DexRegisterLocation::Kind::kInRegister, 0); - stream.AddDexRegisterEntry(1, DexRegisterLocation::Kind::kConstant, -2); + stream.AddDexRegisterEntry(0, Kind::kInRegister, 0); // Short location. + stream.AddDexRegisterEntry(1, Kind::kConstant, -2); // Large location. // Second stack map, which should share the same dex register map. stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); - stream.AddDexRegisterEntry(0, DexRegisterLocation::Kind::kInRegister, 0); - stream.AddDexRegisterEntry(1, DexRegisterLocation::Kind::kConstant, -2); + stream.AddDexRegisterEntry(0, Kind::kInRegister, 0); // Short location. + stream.AddDexRegisterEntry(1, Kind::kConstant, -2); // Large location. // Third stack map (doesn't share the dex register map). stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); - stream.AddDexRegisterEntry(0, DexRegisterLocation::Kind::kInRegister, 2); - stream.AddDexRegisterEntry(1, DexRegisterLocation::Kind::kConstant, -2); + stream.AddDexRegisterEntry(0, Kind::kInRegister, 2); // Short location. + stream.AddDexRegisterEntry(1, Kind::kConstant, -2); // Large location. size_t size = stream.ComputeNeededSize(); void* memory = arena.Alloc(size, kArenaAllocMisc); @@ -260,20 +434,20 @@ TEST(StackMapTest, TestShareDexRegisterMap) { // Verify first stack map. StackMap sm0 = ci.GetStackMapAt(0); DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm0, number_of_dex_registers); - ASSERT_EQ(0, dex_registers0.GetMachineRegister(0, number_of_dex_registers)); - ASSERT_EQ(-2, dex_registers0.GetConstant(1, number_of_dex_registers)); + ASSERT_EQ(0, dex_registers0.GetMachineRegister(0, number_of_dex_registers, ci)); + ASSERT_EQ(-2, dex_registers0.GetConstant(1, number_of_dex_registers, ci)); // Verify second stack map. StackMap sm1 = ci.GetStackMapAt(1); DexRegisterMap dex_registers1 = ci.GetDexRegisterMapOf(sm1, number_of_dex_registers); - ASSERT_EQ(0, dex_registers1.GetMachineRegister(0, number_of_dex_registers)); - ASSERT_EQ(-2, dex_registers1.GetConstant(1, number_of_dex_registers)); + ASSERT_EQ(0, dex_registers1.GetMachineRegister(0, number_of_dex_registers, ci)); + ASSERT_EQ(-2, dex_registers1.GetConstant(1, number_of_dex_registers, ci)); // Verify third stack map. StackMap sm2 = ci.GetStackMapAt(2); DexRegisterMap dex_registers2 = ci.GetDexRegisterMapOf(sm2, number_of_dex_registers); - ASSERT_EQ(2, dex_registers2.GetMachineRegister(0, number_of_dex_registers)); - ASSERT_EQ(-2, dex_registers2.GetConstant(1, number_of_dex_registers)); + ASSERT_EQ(2, dex_registers2.GetMachineRegister(0, number_of_dex_registers, ci)); + ASSERT_EQ(-2, dex_registers2.GetConstant(1, number_of_dex_registers, ci)); // Verify dex register map offsets. ASSERT_EQ(sm0.GetDexRegisterMapOffset(ci), sm1.GetDexRegisterMapOffset(ci)); @@ -281,4 +455,39 @@ TEST(StackMapTest, TestShareDexRegisterMap) { ASSERT_NE(sm1.GetDexRegisterMapOffset(ci), sm2.GetDexRegisterMapOffset(ci)); } +TEST(StackMapTest, TestNoDexRegisterMap) { + ArenaPool pool; + ArenaAllocator arena(&pool); + StackMapStream stream(&arena); + + ArenaBitVector sp_mask(&arena, 0, false); + uint32_t number_of_dex_registers = 0; + stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); + + size_t size = stream.ComputeNeededSize(); + void* memory = arena.Alloc(size, kArenaAllocMisc); + MemoryRegion region(memory, size); + stream.FillIn(region); + + CodeInfo code_info(region); + ASSERT_EQ(0u, code_info.GetStackMaskSize()); + ASSERT_EQ(1u, code_info.GetNumberOfStackMaps()); + + uint32_t number_of_location_catalog_entries = + code_info.GetNumberOfDexRegisterLocationCatalogEntries(); + ASSERT_EQ(0u, number_of_location_catalog_entries); + DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(); + ASSERT_EQ(0u, location_catalog.Size()); + + StackMap stack_map = code_info.GetStackMapAt(0); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64))); + ASSERT_EQ(0u, stack_map.GetDexPc(code_info)); + ASSERT_EQ(64u, stack_map.GetNativePcOffset(code_info)); + ASSERT_EQ(0x3u, stack_map.GetRegisterMask(code_info)); + + ASSERT_FALSE(stack_map.HasDexRegisterMap(code_info)); + ASSERT_FALSE(stack_map.HasInlineInfo(code_info)); +} + } // namespace art |