summaryrefslogtreecommitdiffstats
path: root/compiler/optimizing
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/optimizing')
-rw-r--r--compiler/optimizing/boolean_simplifier.cc10
-rw-r--r--compiler/optimizing/code_generator_arm.cc99
-rw-r--r--compiler/optimizing/code_generator_x86.cc2
-rw-r--r--compiler/optimizing/inliner.cc3
-rw-r--r--compiler/optimizing/intrinsics.cc6
-rw-r--r--compiler/optimizing/intrinsics_arm.cc4
-rw-r--r--compiler/optimizing/intrinsics_arm64.cc4
-rw-r--r--compiler/optimizing/intrinsics_list.h2
-rw-r--r--compiler/optimizing/intrinsics_x86.cc4
-rw-r--r--compiler/optimizing/intrinsics_x86_64.cc4
-rw-r--r--compiler/optimizing/optimizing_compiler.cc9
-rw-r--r--compiler/optimizing/optimizing_compiler_stats.h18
-rw-r--r--compiler/optimizing/parallel_move_resolver.cc58
-rw-r--r--compiler/optimizing/parallel_move_test.cc27
-rw-r--r--compiler/optimizing/register_allocator.cc83
-rw-r--r--compiler/optimizing/register_allocator.h7
-rw-r--r--compiler/optimizing/ssa_liveness_analysis.cc2
-rw-r--r--compiler/optimizing/ssa_liveness_analysis.h19
-rw-r--r--compiler/optimizing/stack_map_stream.h173
-rw-r--r--compiler/optimizing/stack_map_test.cc333
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