Merge "Move some helper methods to DexRegisterLocation."
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index eea146e..2589869 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -1055,7 +1055,8 @@
void CodeGenerator::RecordPcInfo(HInstruction* instruction,
uint32_t dex_pc,
- SlowPathCode* slow_path) {
+ SlowPathCode* slow_path,
+ bool native_debug_info) {
if (instruction != nullptr) {
// The code generated for some type conversions
// may call the runtime, thus normally requiring a subsequent
@@ -1120,12 +1121,23 @@
outer_dex_pc = outer_environment->GetDexPc();
outer_environment_size = outer_environment->Size();
}
+
+ HLoopInformation* info = instruction->GetBlock()->GetLoopInformation();
+ bool osr =
+ instruction->IsSuspendCheck() &&
+ (info != nullptr) &&
+ graph_->IsCompilingOsr() &&
+ (inlining_depth == 0);
+ StackMap::Kind kind = native_debug_info
+ ? StackMap::Kind::Debug
+ : (osr ? StackMap::Kind::OSR : StackMap::Kind::Default);
stack_map_stream->BeginStackMapEntry(outer_dex_pc,
native_pc,
register_mask,
locations->GetStackMask(),
outer_environment_size,
- inlining_depth);
+ inlining_depth,
+ kind);
EmitEnvironment(environment, slow_path);
// Record invoke info, the common case for the trampoline is super and static invokes. Only
// record these to reduce oat file size.
@@ -1138,19 +1150,9 @@
}
stack_map_stream->EndStackMapEntry();
- HLoopInformation* info = instruction->GetBlock()->GetLoopInformation();
- if (instruction->IsSuspendCheck() &&
- (info != nullptr) &&
- graph_->IsCompilingOsr() &&
- (inlining_depth == 0)) {
+ if (osr) {
DCHECK_EQ(info->GetSuspendCheck(), instruction);
- // We duplicate the stack map as a marker that this stack map can be an OSR entry.
- // Duplicating it avoids having the runtime recognize and skip an OSR stack map.
DCHECK(info->IsIrreducible());
- stack_map_stream->BeginStackMapEntry(
- dex_pc, native_pc, register_mask, locations->GetStackMask(), outer_environment_size, 0);
- EmitEnvironment(instruction->GetEnvironment(), slow_path);
- stack_map_stream->EndStackMapEntry();
if (kIsDebugBuild) {
for (size_t i = 0, environment_size = environment->Size(); i < environment_size; ++i) {
HInstruction* in_environment = environment->GetInstructionAt(i);
@@ -1167,14 +1169,6 @@
}
}
}
- } else if (kIsDebugBuild) {
- // Ensure stack maps are unique, by checking that the native pc in the stack map
- // last emitted is different than the native pc of the stack map just emitted.
- size_t number_of_stack_maps = stack_map_stream->GetNumberOfStackMaps();
- if (number_of_stack_maps > 1) {
- DCHECK_NE(stack_map_stream->GetStackMapNativePcOffset(number_of_stack_maps - 1),
- stack_map_stream->GetStackMapNativePcOffset(number_of_stack_maps - 2));
- }
}
}
@@ -1196,12 +1190,11 @@
// Ensure that we do not collide with the stack map of the previous instruction.
GenerateNop();
}
- RecordPcInfo(instruction, dex_pc, slow_path);
+ RecordPcInfo(instruction, dex_pc, slow_path, /* native_debug_info */ true);
}
}
void CodeGenerator::RecordCatchBlockInfo() {
- ArenaAllocator* allocator = graph_->GetAllocator();
StackMapStream* stack_map_stream = GetStackMapStream();
for (HBasicBlock* block : *block_order_) {
@@ -1213,28 +1206,24 @@
uint32_t num_vregs = graph_->GetNumberOfVRegs();
uint32_t inlining_depth = 0; // Inlining of catch blocks is not supported at the moment.
uint32_t native_pc = GetAddressOf(block);
- uint32_t register_mask = 0; // Not used.
-
- // The stack mask is not used, so we leave it empty.
- ArenaBitVector* stack_mask =
- ArenaBitVector::Create(allocator, 0, /* expandable */ true, kArenaAllocCodeGenerator);
stack_map_stream->BeginStackMapEntry(dex_pc,
native_pc,
- register_mask,
- stack_mask,
+ /* register_mask */ 0,
+ /* stack_mask */ nullptr,
num_vregs,
- inlining_depth);
+ inlining_depth,
+ StackMap::Kind::Catch);
HInstruction* current_phi = block->GetFirstPhi();
for (size_t vreg = 0; vreg < num_vregs; ++vreg) {
- while (current_phi != nullptr && current_phi->AsPhi()->GetRegNumber() < vreg) {
- HInstruction* next_phi = current_phi->GetNext();
- DCHECK(next_phi == nullptr ||
- current_phi->AsPhi()->GetRegNumber() <= next_phi->AsPhi()->GetRegNumber())
- << "Phis need to be sorted by vreg number to keep this a linear-time loop.";
- current_phi = next_phi;
- }
+ while (current_phi != nullptr && current_phi->AsPhi()->GetRegNumber() < vreg) {
+ HInstruction* next_phi = current_phi->GetNext();
+ DCHECK(next_phi == nullptr ||
+ current_phi->AsPhi()->GetRegNumber() <= next_phi->AsPhi()->GetRegNumber())
+ << "Phis need to be sorted by vreg number to keep this a linear-time loop.";
+ current_phi = next_phi;
+ }
if (current_phi == nullptr || current_phi->AsPhi()->GetRegNumber() != vreg) {
stack_map_stream->AddDexRegisterEntry(DexRegisterLocation::Kind::kNone, 0);
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index b3c29aa..03ae498 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -323,7 +323,10 @@
}
// Record native to dex mapping for a suspend point. Required by runtime.
- void RecordPcInfo(HInstruction* instruction, uint32_t dex_pc, SlowPathCode* slow_path = nullptr);
+ void RecordPcInfo(HInstruction* instruction,
+ uint32_t dex_pc,
+ SlowPathCode* slow_path = nullptr,
+ bool native_debug_info = false);
// Check whether we have already recorded mapping at this PC.
bool HasStackMapAtCurrentPc();
// Record extra stack maps if we support native debugging.
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 5f0533c..8aa790db 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -2459,6 +2459,9 @@
// all & reg_bits - 1.
__ Ror(dst, lhs, RegisterFrom(instr->GetLocations()->InAt(1), type));
}
+ } else if (instr->IsMin() || instr->IsMax()) {
+ __ Cmp(lhs, rhs);
+ __ Csel(dst, lhs, rhs, instr->IsMin() ? lt : gt);
} else {
DCHECK(instr->IsXor());
__ Eor(dst, lhs, rhs);
@@ -2474,6 +2477,10 @@
__ Fadd(dst, lhs, rhs);
} else if (instr->IsSub()) {
__ Fsub(dst, lhs, rhs);
+ } else if (instr->IsMin()) {
+ __ Fmin(dst, lhs, rhs);
+ } else if (instr->IsMax()) {
+ __ Fmax(dst, lhs, rhs);
} else {
LOG(FATAL) << "Unexpected floating-point binary operation";
}
@@ -5671,111 +5678,20 @@
}
}
-// TODO: integrate with HandleBinaryOp?
-static void CreateMinMaxLocations(ArenaAllocator* allocator, HBinaryOperation* minmax) {
- LocationSummary* locations = new (allocator) LocationSummary(minmax);
- switch (minmax->GetResultType()) {
- case DataType::Type::kInt32:
- case DataType::Type::kInt64:
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
- break;
- case DataType::Type::kFloat32:
- case DataType::Type::kFloat64:
- locations->SetInAt(0, Location::RequiresFpuRegister());
- locations->SetInAt(1, Location::RequiresFpuRegister());
- locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
- break;
- default:
- LOG(FATAL) << "Unexpected type for HMinMax " << minmax->GetResultType();
- }
-}
-
-void InstructionCodeGeneratorARM64::GenerateMinMaxInt(LocationSummary* locations,
- bool is_min,
- DataType::Type type) {
- Location op1 = locations->InAt(0);
- Location op2 = locations->InAt(1);
- Location out = locations->Out();
-
- Register op1_reg;
- Register op2_reg;
- Register out_reg;
- if (type == DataType::Type::kInt64) {
- op1_reg = XRegisterFrom(op1);
- op2_reg = XRegisterFrom(op2);
- out_reg = XRegisterFrom(out);
- } else {
- DCHECK_EQ(type, DataType::Type::kInt32);
- op1_reg = WRegisterFrom(op1);
- op2_reg = WRegisterFrom(op2);
- out_reg = WRegisterFrom(out);
- }
-
- __ Cmp(op1_reg, op2_reg);
- __ Csel(out_reg, op1_reg, op2_reg, is_min ? lt : gt);
-}
-
-void InstructionCodeGeneratorARM64::GenerateMinMaxFP(LocationSummary* locations,
- bool is_min,
- DataType::Type type) {
- Location op1 = locations->InAt(0);
- Location op2 = locations->InAt(1);
- Location out = locations->Out();
-
- FPRegister op1_reg;
- FPRegister op2_reg;
- FPRegister out_reg;
- if (type == DataType::Type::kFloat64) {
- op1_reg = DRegisterFrom(op1);
- op2_reg = DRegisterFrom(op2);
- out_reg = DRegisterFrom(out);
- } else {
- DCHECK_EQ(type, DataType::Type::kFloat32);
- op1_reg = SRegisterFrom(op1);
- op2_reg = SRegisterFrom(op2);
- out_reg = SRegisterFrom(out);
- }
-
- if (is_min) {
- __ Fmin(out_reg, op1_reg, op2_reg);
- } else {
- __ Fmax(out_reg, op1_reg, op2_reg);
- }
-}
-
-// TODO: integrate with HandleBinaryOp?
-void InstructionCodeGeneratorARM64::GenerateMinMax(HBinaryOperation* minmax, bool is_min) {
- DataType::Type type = minmax->GetResultType();
- switch (type) {
- case DataType::Type::kInt32:
- case DataType::Type::kInt64:
- GenerateMinMaxInt(minmax->GetLocations(), is_min, type);
- break;
- case DataType::Type::kFloat32:
- case DataType::Type::kFloat64:
- GenerateMinMaxFP(minmax->GetLocations(), is_min, type);
- break;
- default:
- LOG(FATAL) << "Unexpected type for HMinMax " << type;
- }
-}
-
void LocationsBuilderARM64::VisitMin(HMin* min) {
- CreateMinMaxLocations(GetGraph()->GetAllocator(), min);
+ HandleBinaryOp(min);
}
void InstructionCodeGeneratorARM64::VisitMin(HMin* min) {
- GenerateMinMax(min, /*is_min*/ true);
+ HandleBinaryOp(min);
}
void LocationsBuilderARM64::VisitMax(HMax* max) {
- CreateMinMaxLocations(GetGraph()->GetAllocator(), max);
+ HandleBinaryOp(max);
}
void InstructionCodeGeneratorARM64::VisitMax(HMax* max) {
- GenerateMinMax(max, /*is_min*/ false);
+ HandleBinaryOp(max);
}
void LocationsBuilderARM64::VisitAbs(HAbs* abs) {
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index e7fe5b7..5afb712 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -280,10 +280,6 @@
void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
void HandleCondition(HCondition* instruction);
- void GenerateMinMaxInt(LocationSummary* locations, bool is_min, DataType::Type type);
- void GenerateMinMaxFP(LocationSummary* locations, bool is_min, DataType::Type type);
- void GenerateMinMax(HBinaryOperation* minmax, bool is_min);
-
// Generate a heap reference load using one register `out`:
//
// out <- *(out + offset)
diff --git a/compiler/optimizing/common_arm64.h b/compiler/optimizing/common_arm64.h
index ed2f8e9..5191ee2 100644
--- a/compiler/optimizing/common_arm64.h
+++ b/compiler/optimizing/common_arm64.h
@@ -234,6 +234,13 @@
}
}
+inline bool AddSubCanEncodeAsImmediate(int64_t value) {
+ // If `value` does not fit but `-value` does, VIXL will automatically use
+ // the 'opposite' instruction.
+ return vixl::aarch64::Assembler::IsImmAddSub(value)
+ || vixl::aarch64::Assembler::IsImmAddSub(-value);
+}
+
inline bool Arm64CanEncodeConstantAsImmediate(HConstant* constant, HInstruction* instr) {
int64_t value = CodeGenerator::GetInt64ValueOf(constant);
@@ -249,6 +256,20 @@
return IsUint<8>(value);
}
+ // Code generation for Min/Max:
+ // Cmp left_op, right_op
+ // Csel dst, left_op, right_op, cond
+ if (instr->IsMin() || instr->IsMax()) {
+ if (constant->GetUses().HasExactlyOneElement()) {
+ // If value can be encoded as immediate for the Cmp, then let VIXL handle
+ // the constant generation for the Csel.
+ return AddSubCanEncodeAsImmediate(value);
+ }
+ // These values are encodable as immediates for Cmp and VIXL will use csinc and csinv
+ // with the zr register as right_op, hence no constant generation is required.
+ return constant->IsZeroBitPattern() || constant->IsOne() || constant->IsMinusOne();
+ }
+
// For single uses we let VIXL handle the constant generation since it will
// use registers that are not managed by the register allocator (wip0, wip1).
if (constant->GetUses().HasExactlyOneElement()) {
@@ -275,10 +296,7 @@
instr->IsSub())
<< instr->DebugName();
// Uses aliases of ADD/SUB instructions.
- // If `value` does not fit but `-value` does, VIXL will automatically use
- // the 'opposite' instruction.
- return vixl::aarch64::Assembler::IsImmAddSub(value)
- || vixl::aarch64::Assembler::IsImmAddSub(-value);
+ return AddSubCanEncodeAsImmediate(value);
}
}
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index d532eee..c979a5a 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -117,6 +117,7 @@
void SimplifyFP2Int(HInvoke* invoke);
void SimplifyStringCharAt(HInvoke* invoke);
void SimplifyStringIsEmptyOrLength(HInvoke* invoke);
+ void SimplifyStringIndexOf(HInvoke* invoke);
void SimplifyNPEOnArgN(HInvoke* invoke, size_t);
void SimplifyReturnThis(HInvoke* invoke);
void SimplifyAllocationIntrinsic(HInvoke* invoke);
@@ -2417,6 +2418,43 @@
invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, replacement);
}
+void InstructionSimplifierVisitor::SimplifyStringIndexOf(HInvoke* invoke) {
+ DCHECK(invoke->GetIntrinsic() == Intrinsics::kStringIndexOf ||
+ invoke->GetIntrinsic() == Intrinsics::kStringIndexOfAfter);
+ if (invoke->InputAt(0)->IsLoadString()) {
+ HLoadString* load_string = invoke->InputAt(0)->AsLoadString();
+ const DexFile& dex_file = load_string->GetDexFile();
+ uint32_t utf16_length;
+ const char* data =
+ dex_file.StringDataAndUtf16LengthByIdx(load_string->GetStringIndex(), &utf16_length);
+ if (utf16_length == 0) {
+ invoke->ReplaceWith(GetGraph()->GetIntConstant(-1));
+ invoke->GetBlock()->RemoveInstruction(invoke);
+ RecordSimplification();
+ return;
+ }
+ if (utf16_length == 1 && invoke->GetIntrinsic() == Intrinsics::kStringIndexOf) {
+ // Simplify to HSelect(HEquals(., load_string.charAt(0)), 0, -1).
+ // If the sought character is supplementary, this gives the correct result, i.e. -1.
+ uint32_t c = GetUtf16FromUtf8(&data);
+ DCHECK_EQ(GetTrailingUtf16Char(c), 0u);
+ DCHECK_EQ(GetLeadingUtf16Char(c), c);
+ uint32_t dex_pc = invoke->GetDexPc();
+ ArenaAllocator* allocator = GetGraph()->GetAllocator();
+ HEqual* equal =
+ new (allocator) HEqual(invoke->InputAt(1), GetGraph()->GetIntConstant(c), dex_pc);
+ invoke->GetBlock()->InsertInstructionBefore(equal, invoke);
+ HSelect* result = new (allocator) HSelect(equal,
+ GetGraph()->GetIntConstant(0),
+ GetGraph()->GetIntConstant(-1),
+ dex_pc);
+ invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, result);
+ RecordSimplification();
+ return;
+ }
+ }
+}
+
// This method should only be used on intrinsics whose sole way of throwing an
// exception is raising a NPE when the nth argument is null. If that argument
// is provably non-null, we can clear the flag.
@@ -2554,6 +2592,10 @@
case Intrinsics::kStringLength:
SimplifyStringIsEmptyOrLength(instruction);
break;
+ case Intrinsics::kStringIndexOf:
+ case Intrinsics::kStringIndexOfAfter:
+ SimplifyStringIndexOf(instruction);
+ break;
case Intrinsics::kStringStringIndexOf:
case Intrinsics::kStringStringIndexOfAfter:
SimplifyNPEOnArgN(instruction, 1); // 0th has own NullCheck
diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc
index 2bfa430..58a35dd 100644
--- a/compiler/optimizing/stack_map_stream.cc
+++ b/compiler/optimizing/stack_map_stream.cc
@@ -31,11 +31,12 @@
constexpr static bool kVerifyStackMaps = kIsDebugBuild;
uint32_t StackMapStream::GetStackMapNativePcOffset(size_t i) {
- return StackMap::UnpackNativePc(stack_maps_[i].packed_native_pc, instruction_set_);
+ return StackMap::UnpackNativePc(stack_maps_[i][StackMap::kPackedNativePc], instruction_set_);
}
void StackMapStream::SetStackMapNativePcOffset(size_t i, uint32_t native_pc_offset) {
- stack_maps_[i].packed_native_pc = StackMap::PackNativePc(native_pc_offset, instruction_set_);
+ stack_maps_[i][StackMap::kPackedNativePc] =
+ StackMap::PackNativePc(native_pc_offset, instruction_set_);
}
void StackMapStream::BeginStackMapEntry(uint32_t dex_pc,
@@ -43,7 +44,8 @@
uint32_t register_mask,
BitVector* stack_mask,
uint32_t num_dex_registers,
- uint8_t inlining_depth) {
+ uint8_t inlining_depth,
+ StackMap::Kind kind) {
DCHECK(!in_stack_map_) << "Mismatched Begin/End calls";
in_stack_map_ = true;
// num_dex_registers_ is the constant per-method number of registers.
@@ -54,19 +56,17 @@
DCHECK_EQ(num_dex_registers_, num_dex_registers) << "Inconsistent register count";
}
- current_stack_map_ = StackMapEntry {
- .packed_native_pc = StackMap::PackNativePc(native_pc_offset, instruction_set_),
- .dex_pc = dex_pc,
- .register_mask_index = kNoValue,
- .stack_mask_index = kNoValue,
- .inline_info_index = kNoValue,
- .dex_register_mask_index = kNoValue,
- .dex_register_map_index = kNoValue,
- };
+ current_stack_map_ = BitTableBuilder<StackMap::kCount>::Entry();
+ current_stack_map_[StackMap::kKind] = static_cast<uint32_t>(kind);
+ current_stack_map_[StackMap::kPackedNativePc] =
+ StackMap::PackNativePc(native_pc_offset, instruction_set_);
+ current_stack_map_[StackMap::kDexPc] = dex_pc;
if (register_mask != 0) {
uint32_t shift = LeastSignificantBit(register_mask);
- RegisterMaskEntry entry = { register_mask >> shift, shift };
- current_stack_map_.register_mask_index = register_masks_.Dedup(&entry);
+ BitTableBuilder<RegisterMask::kCount>::Entry entry;
+ entry[RegisterMask::kValue] = register_mask >> shift;
+ entry[RegisterMask::kShift] = shift;
+ current_stack_map_[StackMap::kRegisterMaskIndex] = register_masks_.Dedup(&entry);
}
// The compiler assumes the bit vector will be read during PrepareForFillIn(),
// and it might modify the data before that. Therefore, just store the pointer.
@@ -81,8 +81,17 @@
// Create lambda method, which will be executed at the very end to verify data.
// Parameters and local variables will be captured(stored) by the lambda "[=]".
dchecks_.emplace_back([=](const CodeInfo& code_info) {
+ if (kind == StackMap::Kind::Default || kind == StackMap::Kind::OSR) {
+ StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset,
+ instruction_set_);
+ CHECK_EQ(stack_map.Row(), stack_map_index);
+ } else if (kind == StackMap::Kind::Catch) {
+ StackMap stack_map = code_info.GetCatchStackMapForDexPc(dex_pc);
+ CHECK_EQ(stack_map.Row(), stack_map_index);
+ }
StackMap stack_map = code_info.GetStackMapAt(stack_map_index);
CHECK_EQ(stack_map.GetNativePcOffset(instruction_set_), native_pc_offset);
+ CHECK_EQ(stack_map.GetKind(), static_cast<uint32_t>(kind));
CHECK_EQ(stack_map.GetDexPc(), dex_pc);
CHECK_EQ(code_info.GetRegisterMaskOf(stack_map), register_mask);
BitMemoryRegion seen_stack_mask = code_info.GetStackMaskOf(stack_map);
@@ -103,8 +112,8 @@
// Generate index into the InlineInfo table.
if (!current_inline_infos_.empty()) {
- current_inline_infos_.back().is_last = InlineInfo::kLast;
- current_stack_map_.inline_info_index =
+ current_inline_infos_.back()[InlineInfo::kIsLast] = InlineInfo::kLast;
+ current_stack_map_[StackMap::kInlineInfoIndex] =
inline_infos_.Dedup(current_inline_infos_.data(), current_inline_infos_.size());
}
@@ -115,13 +124,13 @@
}
void StackMapStream::AddInvoke(InvokeType invoke_type, uint32_t dex_method_index) {
- uint32_t packed_native_pc = current_stack_map_.packed_native_pc;
+ uint32_t packed_native_pc = current_stack_map_[StackMap::kPackedNativePc];
size_t invoke_info_index = invoke_infos_.size();
- invoke_infos_.Add(InvokeInfoEntry {
- .packed_native_pc = packed_native_pc,
- .invoke_type = invoke_type,
- .method_info_index = method_infos_.Dedup(&dex_method_index),
- });
+ BitTableBuilder<InvokeInfo::kCount>::Entry entry;
+ entry[InvokeInfo::kPackedNativePc] = packed_native_pc;
+ entry[InvokeInfo::kInvokeType] = invoke_type;
+ entry[InvokeInfo::kMethodInfoIndex] = method_infos_.Dedup({dex_method_index});
+ invoke_infos_.Add(entry);
if (kVerifyStackMaps) {
dchecks_.emplace_back([=](const CodeInfo& code_info) {
@@ -129,7 +138,7 @@
CHECK_EQ(invoke_info.GetNativePcOffset(instruction_set_),
StackMap::UnpackNativePc(packed_native_pc, instruction_set_));
CHECK_EQ(invoke_info.GetInvokeType(), invoke_type);
- CHECK_EQ(method_infos_[invoke_info.GetMethodInfoIndex()], dex_method_index);
+ CHECK_EQ(method_infos_[invoke_info.GetMethodInfoIndex()][0], dex_method_index);
});
}
}
@@ -144,24 +153,20 @@
expected_num_dex_registers_ += num_dex_registers;
- InlineInfoEntry entry = {
- .is_last = InlineInfo::kMore,
- .dex_pc = dex_pc,
- .method_info_index = kNoValue,
- .art_method_hi = kNoValue,
- .art_method_lo = kNoValue,
- .num_dex_registers = static_cast<uint32_t>(expected_num_dex_registers_),
- };
+ BitTableBuilder<InlineInfo::kCount>::Entry entry;
+ entry[InlineInfo::kIsLast] = InlineInfo::kMore;
+ entry[InlineInfo::kDexPc] = dex_pc;
+ entry[InlineInfo::kNumberOfDexRegisters] = static_cast<uint32_t>(expected_num_dex_registers_);
if (EncodeArtMethodInInlineInfo(method)) {
- entry.art_method_hi = High32Bits(reinterpret_cast<uintptr_t>(method));
- entry.art_method_lo = Low32Bits(reinterpret_cast<uintptr_t>(method));
+ entry[InlineInfo::kArtMethodHi] = High32Bits(reinterpret_cast<uintptr_t>(method));
+ entry[InlineInfo::kArtMethodLo] = Low32Bits(reinterpret_cast<uintptr_t>(method));
} else {
if (dex_pc != static_cast<uint32_t>(-1) && kIsDebugBuild) {
ScopedObjectAccess soa(Thread::Current());
DCHECK(IsSameDexFile(*outer_dex_file, *method->GetDexFile()));
}
uint32_t dex_method_index = method->GetDexMethodIndexUnchecked();
- entry.method_info_index = method_infos_.Dedup(&dex_method_index);
+ entry[InlineInfo::kMethodInfoIndex] = method_infos_.Dedup({dex_method_index});
}
current_inline_infos_.push_back(entry);
@@ -177,7 +182,7 @@
if (encode_art_method) {
CHECK_EQ(inline_info.GetArtMethod(), method);
} else {
- CHECK_EQ(method_infos_[inline_info.GetMethodInfoIndex()],
+ CHECK_EQ(method_infos_[inline_info.GetMethodInfoIndex()][0],
method->GetDexMethodIndexUnchecked());
}
});
@@ -210,13 +215,13 @@
// Distance is difference between this index and the index of last modification.
uint32_t distance = stack_maps_.size() - dex_register_timestamp_[i];
if (previous_dex_registers_[i] != reg || distance > kMaxDexRegisterMapSearchDistance) {
- DexRegisterEntry entry = DexRegisterEntry{
- .kind = static_cast<uint32_t>(reg.GetKind()),
- .packed_value = DexRegisterInfo::PackValue(reg.GetKind(), reg.GetValue()),
- };
+ BitTableBuilder<DexRegisterInfo::kCount>::Entry entry;
+ entry[DexRegisterInfo::kKind] = static_cast<uint32_t>(reg.GetKind());
+ entry[DexRegisterInfo::kPackedValue] =
+ DexRegisterInfo::PackValue(reg.GetKind(), reg.GetValue());
uint32_t index = reg.IsLive() ? dex_register_catalog_.Dedup(&entry) : kNoValue;
temp_dex_register_mask_.SetBit(i);
- temp_dex_register_map_.push_back(index);
+ temp_dex_register_map_.push_back({index});
previous_dex_registers_[i] = reg;
dex_register_timestamp_[i] = stack_maps_.size();
}
@@ -224,12 +229,12 @@
// Set the mask and map for the current StackMap (which includes inlined registers).
if (temp_dex_register_mask_.GetNumberOfBits() != 0) {
- current_stack_map_.dex_register_mask_index =
+ current_stack_map_[StackMap::kDexRegisterMaskIndex] =
dex_register_masks_.Dedup(temp_dex_register_mask_.GetRawStorage(),
temp_dex_register_mask_.GetNumberOfBits());
}
if (!current_dex_registers_.empty()) {
- current_stack_map_.dex_register_map_index =
+ current_stack_map_[StackMap::kDexRegisterMapIndex] =
dex_register_maps_.Dedup(temp_dex_register_map_.data(),
temp_dex_register_map_.size());
}
@@ -260,7 +265,7 @@
{
MethodInfo info(region.begin(), method_infos_.size());
for (size_t i = 0; i < method_infos_.size(); ++i) {
- info.SetMethodIndex(i, method_infos_[i]);
+ info.SetMethodIndex(i, method_infos_[i][0]);
}
}
if (kVerifyStackMaps) {
@@ -269,23 +274,19 @@
const size_t count = info.NumMethodIndices();
DCHECK_EQ(count, method_infos_.size());
for (size_t i = 0; i < count; ++i) {
- DCHECK_EQ(info.GetMethodIndex(i), method_infos_[i]);
+ DCHECK_EQ(info.GetMethodIndex(i), method_infos_[i][0]);
}
}
}
size_t StackMapStream::PrepareForFillIn() {
- static_assert(sizeof(StackMapEntry) == StackMap::kCount * sizeof(uint32_t), "Layout");
- static_assert(sizeof(InvokeInfoEntry) == InvokeInfo::kCount * sizeof(uint32_t), "Layout");
- static_assert(sizeof(InlineInfoEntry) == InlineInfo::kCount * sizeof(uint32_t), "Layout");
- static_assert(sizeof(DexRegisterEntry) == DexRegisterInfo::kCount * sizeof(uint32_t), "Layout");
DCHECK_EQ(out_.size(), 0u);
// Read the stack masks now. The compiler might have updated them.
for (size_t i = 0; i < lazy_stack_masks_.size(); i++) {
BitVector* stack_mask = lazy_stack_masks_[i];
if (stack_mask != nullptr && stack_mask->GetNumberOfBits() != 0) {
- stack_maps_[i].stack_mask_index =
+ stack_maps_[i][StackMap::kStackMaskIndex] =
stack_masks_.Dedup(stack_mask->GetRawStorage(), stack_mask->GetNumberOfBits());
}
}
diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h
index e3ae8a2..6842d9f 100644
--- a/compiler/optimizing/stack_map_stream.h
+++ b/compiler/optimizing/stack_map_stream.h
@@ -27,11 +27,10 @@
#include "dex_register_location.h"
#include "method_info.h"
#include "nodes.h"
+#include "stack_map.h"
namespace art {
-class CodeInfo;
-
/**
* Collects and builds stack maps for a method. All the stack maps
* for a method are placed in a CodeInfo object.
@@ -53,6 +52,7 @@
lazy_stack_masks_(allocator->Adapter(kArenaAllocStackMapStream)),
in_stack_map_(false),
in_inline_info_(false),
+ current_stack_map_(),
current_inline_infos_(allocator->Adapter(kArenaAllocStackMapStream)),
current_dex_registers_(allocator->Adapter(kArenaAllocStackMapStream)),
previous_dex_registers_(allocator->Adapter(kArenaAllocStackMapStream)),
@@ -66,7 +66,8 @@
uint32_t register_mask,
BitVector* sp_mask,
uint32_t num_dex_registers,
- uint8_t inlining_depth);
+ uint8_t inlining_depth,
+ StackMap::Kind kind = StackMap::Kind::Default);
void EndStackMapEntry();
void AddDexRegisterEntry(DexRegisterLocation::Kind kind, int32_t value) {
@@ -99,69 +100,29 @@
private:
static constexpr uint32_t kNoValue = -1;
- // The fields must be uint32_t and mirror the StackMap accessor in stack_map.h!
- struct StackMapEntry {
- uint32_t packed_native_pc;
- uint32_t dex_pc;
- uint32_t register_mask_index;
- uint32_t stack_mask_index;
- uint32_t inline_info_index;
- uint32_t dex_register_mask_index;
- uint32_t dex_register_map_index;
- };
-
- // The fields must be uint32_t and mirror the InlineInfo accessor in stack_map.h!
- struct InlineInfoEntry {
- uint32_t is_last;
- uint32_t dex_pc;
- uint32_t method_info_index;
- uint32_t art_method_hi;
- uint32_t art_method_lo;
- uint32_t num_dex_registers;
- };
-
- // The fields must be uint32_t and mirror the InvokeInfo accessor in stack_map.h!
- struct InvokeInfoEntry {
- uint32_t packed_native_pc;
- uint32_t invoke_type;
- uint32_t method_info_index;
- };
-
- // The fields must be uint32_t and mirror the DexRegisterInfo accessor in stack_map.h!
- struct DexRegisterEntry {
- uint32_t kind;
- uint32_t packed_value;
- };
-
- // The fields must be uint32_t and mirror the RegisterMask accessor in stack_map.h!
- struct RegisterMaskEntry {
- uint32_t value;
- uint32_t shift;
- };
-
void CreateDexRegisterMap();
const InstructionSet instruction_set_;
- BitTableBuilder<StackMapEntry> stack_maps_;
- BitTableBuilder<RegisterMaskEntry> register_masks_;
+ BitTableBuilder<StackMap::kCount> stack_maps_;
+ BitTableBuilder<RegisterMask::kCount> register_masks_;
BitmapTableBuilder stack_masks_;
- BitTableBuilder<InvokeInfoEntry> invoke_infos_;
- BitTableBuilder<InlineInfoEntry> inline_infos_;
+ BitTableBuilder<InvokeInfo::kCount> invoke_infos_;
+ BitTableBuilder<InlineInfo::kCount> inline_infos_;
BitmapTableBuilder dex_register_masks_;
- BitTableBuilder<uint32_t> dex_register_maps_;
- BitTableBuilder<DexRegisterEntry> dex_register_catalog_;
+ BitTableBuilder<MaskInfo::kCount> dex_register_maps_;
+ BitTableBuilder<DexRegisterInfo::kCount> dex_register_catalog_;
uint32_t num_dex_registers_ = 0; // TODO: Make this const and get the value in constructor.
ScopedArenaVector<uint8_t> out_;
- BitTableBuilder<uint32_t> method_infos_;
+ BitTableBuilder<1> method_infos_;
ScopedArenaVector<BitVector*> lazy_stack_masks_;
// Variables which track the current state between Begin/End calls;
bool in_stack_map_;
bool in_inline_info_;
- StackMapEntry current_stack_map_;
- ScopedArenaVector<InlineInfoEntry> current_inline_infos_;
+ BitTableBuilder<StackMap::kCount>::Entry current_stack_map_;
+ ScopedArenaVector<BitTableBuilder<InlineInfo::kCount>::Entry> current_inline_infos_;
ScopedArenaVector<DexRegisterLocation> current_dex_registers_;
ScopedArenaVector<DexRegisterLocation> previous_dex_registers_;
ScopedArenaVector<uint32_t> dex_register_timestamp_; // Stack map index of last change.
@@ -170,7 +131,7 @@
// Temporary variables used in CreateDexRegisterMap.
// They are here so that we can reuse the reserved memory.
ArenaBitVector temp_dex_register_mask_;
- ScopedArenaVector<uint32_t> temp_dex_register_map_;
+ ScopedArenaVector<BitTableBuilder<DexRegisterMapInfo::kCount>::Entry> temp_dex_register_map_;
// A set of lambda functions to be executed at the end to verify
// the encoded data. It is generally only used in debug builds.
diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc
index 56717b5..fd85667 100644
--- a/compiler/optimizing/stack_map_test.cc
+++ b/compiler/optimizing/stack_map_test.cc
@@ -425,12 +425,12 @@
stream.AddDexRegisterEntry(Kind::kConstant, -2); // Large location.
stream.EndStackMapEntry();
// Second stack map, which should share the same dex register map.
- stream.BeginStackMapEntry(0, 64 * kPcAlign, 0x3, &sp_mask, number_of_dex_registers, 0);
+ stream.BeginStackMapEntry(0, 65 * kPcAlign, 0x3, &sp_mask, number_of_dex_registers, 0);
stream.AddDexRegisterEntry(Kind::kInRegister, 0); // Short location.
stream.AddDexRegisterEntry(Kind::kConstant, -2); // Large location.
stream.EndStackMapEntry();
// Third stack map (doesn't share the dex register map).
- stream.BeginStackMapEntry(0, 64 * kPcAlign, 0x3, &sp_mask, number_of_dex_registers, 0);
+ stream.BeginStackMapEntry(0, 66 * kPcAlign, 0x3, &sp_mask, number_of_dex_registers, 0);
stream.AddDexRegisterEntry(Kind::kInRegister, 2); // Short location.
stream.AddDexRegisterEntry(Kind::kConstant, -2); // Large location.
stream.EndStackMapEntry();
@@ -528,7 +528,7 @@
sp_mask1.SetBit(4);
// First stack map.
- stream.BeginStackMapEntry(0, 64 * kPcAlign, 0x3, &sp_mask1, 2, 2);
+ stream.BeginStackMapEntry(0, 10 * kPcAlign, 0x3, &sp_mask1, 2, 2);
stream.AddDexRegisterEntry(Kind::kInStack, 0);
stream.AddDexRegisterEntry(Kind::kConstant, 4);
diff --git a/dexlayout/compact_dex_writer.cc b/dexlayout/compact_dex_writer.cc
index 2b4144c..3f5dbcf 100644
--- a/dexlayout/compact_dex_writer.cc
+++ b/dexlayout/compact_dex_writer.cc
@@ -59,8 +59,8 @@
for (auto& method : *(invoke_type == InvokeType::kDirect
? class_data->DirectMethods()
: class_data->VirtualMethods())) {
- const dex_ir::MethodId* method_id = method->GetMethodId();
- dex_ir::CodeItem* code_item = method->GetCodeItem();
+ const dex_ir::MethodId* method_id = method.GetMethodId();
+ dex_ir::CodeItem* code_item = method.GetCodeItem();
if (code_item != nullptr && code_item->DebugInfo() != nullptr) {
const uint32_t debug_info_offset = code_item->DebugInfo()->GetOffset();
const uint32_t method_idx = method_id->GetIndex();
@@ -248,8 +248,8 @@
for (auto& method : *(invoke_type == InvokeType::kDirect
? class_data->DirectMethods()
: class_data->VirtualMethods())) {
- const dex_ir::MethodId* method_id = method->GetMethodId();
- dex_ir::CodeItem* code_item = method->GetCodeItem();
+ const dex_ir::MethodId* method_id = method.GetMethodId();
+ dex_ir::CodeItem* code_item = method.GetCodeItem();
if (code_item != nullptr && code_item->DebugInfo() != nullptr) {
const dex_ir::DebugInfoItem* debug_item = code_item->DebugInfo();
method_idx_map.insert(std::make_pair(debug_item, method_id->GetIndex()));
@@ -350,8 +350,8 @@
for (auto& method : *(invoke_type == InvokeType::kDirect
? class_data->DirectMethods()
: class_data->VirtualMethods())) {
- const uint32_t idx = method->GetMethodId()->GetIndex();
- dex_ir::CodeItem* code_item = method->GetCodeItem();
+ const uint32_t idx = method.GetMethodId()->GetIndex();
+ dex_ir::CodeItem* code_item = method.GetCodeItem();
dex_ir:: DebugInfoItem* debug_info_item = nullptr;
if (code_item != nullptr) {
debug_info_item = code_item->DebugInfo();
diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc
index b7d9db6..15e3baf 100644
--- a/dexlayout/dex_ir.cc
+++ b/dexlayout/dex_ir.cc
@@ -318,17 +318,22 @@
void Collections::CreateStringId(const DexFile& dex_file, uint32_t i) {
const DexFile::StringId& disk_string_id = dex_file.GetStringId(dex::StringIndex(i));
- StringData* string_data = new StringData(dex_file.GetStringData(disk_string_id));
- AddItem(string_datas_map_, string_datas_, string_data, disk_string_id.string_data_off_);
-
- StringId* string_id = new StringId(string_data);
- AddIndexedItem(string_ids_, string_id, StringIdsOffset() + i * StringId::ItemSize(), i);
+ StringData* string_data = CreateAndAddItem(string_datas_map_,
+ string_datas_,
+ disk_string_id.string_data_off_,
+ dex_file.GetStringData(disk_string_id));
+ CreateAndAddIndexedItem(string_ids_,
+ StringIdsOffset() + i * StringId::ItemSize(),
+ i,
+ string_data);
}
void Collections::CreateTypeId(const DexFile& dex_file, uint32_t i) {
const DexFile::TypeId& disk_type_id = dex_file.GetTypeId(dex::TypeIndex(i));
- TypeId* type_id = new TypeId(GetStringId(disk_type_id.descriptor_idx_.index_));
- AddIndexedItem(type_ids_, type_id, TypeIdsOffset() + i * TypeId::ItemSize(), i);
+ CreateAndAddIndexedItem(type_ids_,
+ TypeIdsOffset() + i * TypeId::ItemSize(),
+ i,
+ GetStringId(disk_type_id.descriptor_idx_.index_));
}
void Collections::CreateProtoId(const DexFile& dex_file, uint32_t i) {
@@ -336,26 +341,32 @@
const DexFile::TypeList* type_list = dex_file.GetProtoParameters(disk_proto_id);
TypeList* parameter_type_list = CreateTypeList(type_list, disk_proto_id.parameters_off_);
- ProtoId* proto_id = new ProtoId(GetStringId(disk_proto_id.shorty_idx_.index_),
- GetTypeId(disk_proto_id.return_type_idx_.index_),
- parameter_type_list);
- AddIndexedItem(proto_ids_, proto_id, ProtoIdsOffset() + i * ProtoId::ItemSize(), i);
+ CreateAndAddIndexedItem(proto_ids_,
+ ProtoIdsOffset() + i * ProtoId::ItemSize(),
+ i,
+ GetStringId(disk_proto_id.shorty_idx_.index_),
+ GetTypeId(disk_proto_id.return_type_idx_.index_),
+ parameter_type_list);
}
void Collections::CreateFieldId(const DexFile& dex_file, uint32_t i) {
const DexFile::FieldId& disk_field_id = dex_file.GetFieldId(i);
- FieldId* field_id = new FieldId(GetTypeId(disk_field_id.class_idx_.index_),
- GetTypeId(disk_field_id.type_idx_.index_),
- GetStringId(disk_field_id.name_idx_.index_));
- AddIndexedItem(field_ids_, field_id, FieldIdsOffset() + i * FieldId::ItemSize(), i);
+ CreateAndAddIndexedItem(field_ids_,
+ FieldIdsOffset() + i * FieldId::ItemSize(),
+ i,
+ GetTypeId(disk_field_id.class_idx_.index_),
+ GetTypeId(disk_field_id.type_idx_.index_),
+ GetStringId(disk_field_id.name_idx_.index_));
}
void Collections::CreateMethodId(const DexFile& dex_file, uint32_t i) {
const DexFile::MethodId& disk_method_id = dex_file.GetMethodId(i);
- MethodId* method_id = new MethodId(GetTypeId(disk_method_id.class_idx_.index_),
- GetProtoId(disk_method_id.proto_idx_.index_),
- GetStringId(disk_method_id.name_idx_.index_));
- AddIndexedItem(method_ids_, method_id, MethodIdsOffset() + i * MethodId::ItemSize(), i);
+ CreateAndAddIndexedItem(method_ids_,
+ MethodIdsOffset() + i * MethodId::ItemSize(),
+ i,
+ GetTypeId(disk_method_id.class_idx_.index_),
+ GetProtoId(disk_method_id.proto_idx_.index_),
+ GetStringId(disk_method_id.name_idx_.index_));
}
void Collections::CreateClassDef(const DexFile& dex_file, uint32_t i) {
@@ -382,9 +393,17 @@
CreateEncodedArrayItem(dex_file, static_data, disk_class_def.static_values_off_);
ClassData* class_data = CreateClassData(
dex_file, dex_file.GetClassData(disk_class_def), disk_class_def.class_data_off_);
- ClassDef* class_def = new ClassDef(class_type, access_flags, superclass, interfaces_type_list,
- source_file, annotations, static_values, class_data);
- AddIndexedItem(class_defs_, class_def, ClassDefsOffset() + i * ClassDef::ItemSize(), i);
+ CreateAndAddIndexedItem(class_defs_,
+ ClassDefsOffset() + i * ClassDef::ItemSize(),
+ i,
+ class_type,
+ access_flags,
+ superclass,
+ interfaces_type_list,
+ source_file,
+ annotations,
+ static_values,
+ class_data);
}
TypeList* Collections::CreateTypeList(const DexFile::TypeList* dex_type_list, uint32_t offset) {
@@ -398,8 +417,7 @@
for (uint32_t index = 0; index < size; ++index) {
type_vector->push_back(GetTypeId(dex_type_list->GetTypeItem(index).type_idx_.index_));
}
- type_list = new TypeList(type_vector);
- AddItem(type_lists_map_, type_lists_, type_list, offset);
+ type_list = CreateAndAddItem(type_lists_map_, type_lists_, offset, type_vector);
}
return type_list;
}
@@ -418,8 +436,10 @@
values->push_back(std::unique_ptr<EncodedValue>(ReadEncodedValue(dex_file, &static_data)));
}
// TODO: Calculate the size of the encoded array.
- encoded_array_item = new EncodedArrayItem(values);
- AddItem(encoded_array_items_map_, encoded_array_items_, encoded_array_item, offset);
+ encoded_array_item = CreateAndAddItem(encoded_array_items_map_,
+ encoded_array_items_,
+ offset,
+ values);
}
return encoded_array_item;
}
@@ -447,9 +467,12 @@
const uint8_t* annotation_data = annotation->annotation_;
std::unique_ptr<EncodedValue> encoded_value(
ReadEncodedValue(dex_file, &annotation_data, DexFile::kDexAnnotationAnnotation, 0));
- annotation_item = new AnnotationItem(visibility, encoded_value->ReleaseEncodedAnnotation());
+ annotation_item = CreateAndAddItem(annotation_items_map_,
+ annotation_items_,
+ offset,
+ visibility,
+ encoded_value->ReleaseEncodedAnnotation());
annotation_item->SetSize(annotation_data - start_data);
- AddItem(annotation_items_map_, annotation_items_, annotation_item, offset);
}
return annotation_item;
}
@@ -472,8 +495,10 @@
AnnotationItem* annotation_item = CreateAnnotationItem(dex_file, annotation);
items->push_back(annotation_item);
}
- annotation_set_item = new AnnotationSetItem(items);
- AddItem(annotation_set_items_map_, annotation_set_items_, annotation_set_item, offset);
+ annotation_set_item = CreateAndAddItem(annotation_set_items_map_,
+ annotation_set_items_,
+ offset,
+ items);
}
return annotation_set_item;
}
@@ -538,13 +563,13 @@
}
}
// TODO: Calculate the size of the annotations directory.
-annotations_directory_item = new AnnotationsDirectoryItem(
- class_annotation, field_annotations, method_annotations, parameter_annotations);
- AddItem(annotations_directory_items_map_,
- annotations_directory_items_,
- annotations_directory_item,
- offset);
- return annotations_directory_item;
+ return CreateAndAddItem(annotations_directory_items_map_,
+ annotations_directory_items_,
+ offset,
+ class_annotation,
+ field_annotations,
+ method_annotations,
+ parameter_annotations);
}
ParameterAnnotation* Collections::GenerateParameterAnnotation(
@@ -559,8 +584,10 @@
uint32_t set_offset = annotation_set_ref_list->list_[i].annotations_off_;
annotations->push_back(CreateAnnotationSetItem(dex_file, annotation_set_item, set_offset));
}
- set_ref_list = new AnnotationSetRefList(annotations);
- AddItem(annotation_set_ref_lists_map_, annotation_set_ref_lists_, set_ref_list, offset);
+ set_ref_list = CreateAndAddItem(annotation_set_ref_lists_map_,
+ annotation_set_ref_lists_,
+ offset,
+ annotations);
}
return new ParameterAnnotation(method_id, set_ref_list);
}
@@ -590,8 +617,11 @@
uint32_t debug_info_size = GetDebugInfoStreamSize(debug_info_stream);
uint8_t* debug_info_buffer = new uint8_t[debug_info_size];
memcpy(debug_info_buffer, debug_info_stream, debug_info_size);
- debug_info = new DebugInfoItem(debug_info_size, debug_info_buffer);
- AddItem(debug_info_items_map_, debug_info_items_, debug_info, debug_info_offset);
+ debug_info = CreateAndAddItem(debug_info_items_map_,
+ debug_info_items_,
+ debug_info_offset,
+ debug_info_size,
+ debug_info_buffer);
}
}
@@ -677,14 +707,14 @@
}
uint32_t size = dex_file.GetCodeItemSize(*disk_code_item);
- CodeItem* code_item = new CodeItem(accessor.RegistersSize(),
- accessor.InsSize(),
- accessor.OutsSize(),
- debug_info,
- insns_size,
- insns,
- tries,
- handler_list);
+ CodeItem* code_item = code_items_.CreateAndAddItem(accessor.RegistersSize(),
+ accessor.InsSize(),
+ accessor.OutsSize(),
+ debug_info,
+ insns_size,
+ insns,
+ tries,
+ handler_list);
code_item->SetSize(size);
// Add the code item to the map.
@@ -693,7 +723,6 @@
code_item->SetOffset(offset);
}
code_items_map_.emplace(offsets_pair, code_item);
- code_items_.AddItem(code_item);
// Add "fixup" references to types, strings, methods, and fields.
// This is temporary, as we will probably want more detailed parsing of the
@@ -718,7 +747,7 @@
return code_item;
}
-MethodItem* Collections::GenerateMethodItem(const DexFile& dex_file, ClassDataItemIterator& cdii) {
+MethodItem Collections::GenerateMethodItem(const DexFile& dex_file, ClassDataItemIterator& cdii) {
MethodId* method_id = GetMethodId(cdii.GetMemberIndex());
uint32_t access_flags = cdii.GetRawMemberAccessFlags();
const DexFile::CodeItem* disk_code_item = cdii.GetMethodCodeItem();
@@ -728,7 +757,7 @@
disk_code_item,
cdii.GetMethodCodeItemOffset(),
cdii.GetMemberIndex());
- return new MethodItem(access_flags, method_id, code_item);
+ return MethodItem(access_flags, method_id, code_item);
}
ClassData* Collections::CreateClassData(
@@ -743,29 +772,33 @@
for (; cdii.HasNextStaticField(); cdii.Next()) {
FieldId* field_item = GetFieldId(cdii.GetMemberIndex());
uint32_t access_flags = cdii.GetRawMemberAccessFlags();
- static_fields->push_back(std::unique_ptr<FieldItem>(new FieldItem(access_flags, field_item)));
+ static_fields->emplace_back(access_flags, field_item);
}
// Instance fields.
FieldItemVector* instance_fields = new FieldItemVector();
for (; cdii.HasNextInstanceField(); cdii.Next()) {
FieldId* field_item = GetFieldId(cdii.GetMemberIndex());
uint32_t access_flags = cdii.GetRawMemberAccessFlags();
- instance_fields->push_back(
- std::unique_ptr<FieldItem>(new FieldItem(access_flags, field_item)));
+ instance_fields->emplace_back(access_flags, field_item);
}
// Direct methods.
MethodItemVector* direct_methods = new MethodItemVector();
for (; cdii.HasNextDirectMethod(); cdii.Next()) {
- direct_methods->push_back(std::unique_ptr<MethodItem>(GenerateMethodItem(dex_file, cdii)));
+ direct_methods->push_back(GenerateMethodItem(dex_file, cdii));
}
// Virtual methods.
MethodItemVector* virtual_methods = new MethodItemVector();
for (; cdii.HasNextVirtualMethod(); cdii.Next()) {
- virtual_methods->push_back(std::unique_ptr<MethodItem>(GenerateMethodItem(dex_file, cdii)));
+ virtual_methods->push_back(GenerateMethodItem(dex_file, cdii));
}
- class_data = new ClassData(static_fields, instance_fields, direct_methods, virtual_methods);
+ class_data = CreateAndAddItem(class_datas_map_,
+ class_datas_,
+ offset,
+ static_fields,
+ instance_fields,
+ direct_methods,
+ virtual_methods);
class_data->SetSize(cdii.EndDataPointer() - encoded_data);
- AddItem(class_datas_map_, class_datas_, class_data, offset);
}
return class_data;
}
@@ -802,8 +835,10 @@
EncodedArrayItem* call_site_item =
CreateEncodedArrayItem(dex_file, disk_call_item_ptr, disk_call_site_id.data_off_);
- CallSiteId* call_site_id = new CallSiteId(call_site_item);
- AddIndexedItem(call_site_ids_, call_site_id, CallSiteIdsOffset() + i * CallSiteId::ItemSize(), i);
+ CreateAndAddIndexedItem(call_site_ids_,
+ CallSiteIdsOffset() + i * CallSiteId::ItemSize(),
+ i,
+ call_site_item);
}
void Collections::CreateMethodHandleItem(const DexFile& dex_file, uint32_t i) {
@@ -824,11 +859,11 @@
} else {
field_or_method_id = GetFieldId(index);
}
- MethodHandleItem* method_handle = new MethodHandleItem(type, field_or_method_id);
- AddIndexedItem(method_handle_items_,
- method_handle,
- MethodHandleItemsOffset() + i * MethodHandleItem::ItemSize(),
- i);
+ CreateAndAddIndexedItem(method_handle_items_,
+ MethodHandleItemsOffset() + i * MethodHandleItem::ItemSize(),
+ i,
+ type,
+ field_or_method_id);
}
void Collections::SortVectorsByMapOrder() {
@@ -844,6 +879,19 @@
class_datas_.SortByMapOrder(class_datas_map_.Collection());
}
+void Collections::ClearMaps() {
+ string_datas_map_.Collection().clear();
+ type_lists_map_.Collection().clear();
+ encoded_array_items_map_.Collection().clear();
+ annotation_items_map_.Collection().clear();
+ annotation_set_items_map_.Collection().clear();
+ annotation_set_ref_lists_map_.Collection().clear();
+ annotations_directory_items_map_.Collection().clear();
+ debug_info_items_map_.Collection().clear();
+ code_items_map_.clear();
+ class_datas_map_.Collection().clear();
+}
+
static uint32_t HeaderOffset(const dex_ir::Collections& collections ATTRIBUTE_UNUSED) {
return 0;
}
diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h
index 5ecad2b..54ff105 100644
--- a/dexlayout/dex_ir.h
+++ b/dexlayout/dex_ir.h
@@ -129,7 +129,11 @@
template<class T> class CollectionVector : public CollectionBase<T> {
public:
using Vector = std::vector<std::unique_ptr<T>>;
- CollectionVector() = default;
+ CollectionVector() { }
+ explicit CollectionVector(size_t size) {
+ // Preallocate so that assignment does not invalidate pointers into the vector.
+ collection_.reserve(size);
+ }
uint32_t Size() const { return collection_.size(); }
Vector& Collection() { return collection_; }
@@ -152,8 +156,11 @@
protected:
Vector collection_;
- void AddItem(T* object) {
+ template<class... Args>
+ T* CreateAndAddItem(Args&&... args) {
+ T* object = new T(std::forward<Args>(args)...);
collection_.push_back(std::unique_ptr<T>(object));
+ return object;
}
private:
@@ -165,11 +172,20 @@
public:
using Vector = std::vector<std::unique_ptr<T>>;
IndexedCollectionVector() = default;
+ explicit IndexedCollectionVector(size_t size) : CollectionVector<T>(size) { }
private:
- void AddIndexedItem(T* object, uint32_t index) {
+ template <class... Args>
+ T* CreateAndAddIndexedItem(uint32_t index, Args&&... args) {
+ T* object = CollectionVector<T>::CreateAndAddItem(std::forward<Args>(args)...);
object->SetIndex(index);
- CollectionVector<T>::collection_.push_back(std::unique_ptr<T>(object));
+ return object;
+ }
+
+ T* GetElement(uint32_t index) {
+ DCHECK_LT(index, CollectionVector<T>::Size());
+ DCHECK_NE(CollectionVector<T>::collection_[index].get(), static_cast<T*>(nullptr));
+ return CollectionVector<T>::collection_[index].get();
}
friend class Collections;
@@ -193,6 +209,8 @@
private:
std::map<uint32_t, T*> collection_;
+ // CollectionMaps do not own the objects they contain, therefore AddItem is supported
+ // rather than CreateAndAddItem.
void AddItem(T* object, uint32_t offset) {
auto it = collection_.emplace(offset, object);
CHECK(it.second) << "CollectionMap already has an object with offset " << offset << " "
@@ -206,13 +224,25 @@
class Collections {
public:
Collections() = default;
+ Collections(uint32_t num_string_ids,
+ uint32_t num_type_ids,
+ uint32_t num_proto_ids,
+ uint32_t num_field_ids,
+ uint32_t num_method_ids,
+ uint32_t num_class_defs)
+ : string_ids_(num_string_ids),
+ type_ids_(num_type_ids),
+ proto_ids_(num_proto_ids),
+ field_ids_(num_field_ids),
+ method_ids_(num_method_ids),
+ class_defs_(num_class_defs) { }
- CollectionVector<StringId>::Vector& StringIds() { return string_ids_.Collection(); }
- CollectionVector<TypeId>::Vector& TypeIds() { return type_ids_.Collection(); }
- CollectionVector<ProtoId>::Vector& ProtoIds() { return proto_ids_.Collection(); }
- CollectionVector<FieldId>::Vector& FieldIds() { return field_ids_.Collection(); }
- CollectionVector<MethodId>::Vector& MethodIds() { return method_ids_.Collection(); }
- CollectionVector<ClassDef>::Vector& ClassDefs() { return class_defs_.Collection(); }
+ IndexedCollectionVector<StringId>::Vector& StringIds() { return string_ids_.Collection(); }
+ IndexedCollectionVector<TypeId>::Vector& TypeIds() { return type_ids_.Collection(); }
+ IndexedCollectionVector<ProtoId>::Vector& ProtoIds() { return proto_ids_.Collection(); }
+ IndexedCollectionVector<FieldId>::Vector& FieldIds() { return field_ids_.Collection(); }
+ IndexedCollectionVector<MethodId>::Vector& MethodIds() { return method_ids_.Collection(); }
+ IndexedCollectionVector<ClassDef>::Vector& ClassDefs() { return class_defs_.Collection(); }
CollectionVector<CallSiteId>::Vector& CallSiteIds() { return call_site_ids_.Collection(); }
CollectionVector<MethodHandleItem>::Vector& MethodHandleItems()
{ return method_handle_items_.Collection(); }
@@ -266,28 +296,22 @@
uint32_t count);
StringId* GetStringId(uint32_t index) {
- CHECK_LT(index, StringIdsSize());
- return StringIds()[index].get();
+ return string_ids_.GetElement(index);
}
TypeId* GetTypeId(uint32_t index) {
- CHECK_LT(index, TypeIdsSize());
- return TypeIds()[index].get();
+ return type_ids_.GetElement(index);
}
ProtoId* GetProtoId(uint32_t index) {
- CHECK_LT(index, ProtoIdsSize());
- return ProtoIds()[index].get();
+ return proto_ids_.GetElement(index);
}
FieldId* GetFieldId(uint32_t index) {
- CHECK_LT(index, FieldIdsSize());
- return FieldIds()[index].get();
+ return field_ids_.GetElement(index);
}
MethodId* GetMethodId(uint32_t index) {
- CHECK_LT(index, MethodIdsSize());
- return MethodIds()[index].get();
+ return method_ids_.GetElement(index);
}
ClassDef* GetClassDef(uint32_t index) {
- CHECK_LT(index, ClassDefsSize());
- return ClassDefs()[index].get();
+ return class_defs_.GetElement(index);
}
CallSiteId* GetCallSiteId(uint32_t index) {
CHECK_LT(index, CallSiteIdsSize());
@@ -372,31 +396,35 @@
// Sort the vectors buy map order (same order that was used in the input file).
void SortVectorsByMapOrder();
+ // Empty the maps, which are only used for IR construction.
+ void ClearMaps();
- template <typename Type>
- void AddItem(CollectionMap<Type>& map,
- CollectionVector<Type>& vector,
- Type* item,
- uint32_t offset) {
+ template <typename Type, class... Args>
+ Type* CreateAndAddItem(CollectionMap<Type>& map,
+ CollectionVector<Type>& vector,
+ uint32_t offset,
+ Args&&... args) {
+ Type* item = vector.CreateAndAddItem(std::forward<Args>(args)...);
DCHECK(!map.GetExistingObject(offset));
DCHECK(!item->OffsetAssigned());
if (eagerly_assign_offsets_) {
item->SetOffset(offset);
}
map.AddItem(item, offset);
- vector.AddItem(item);
+ return item;
}
- template <typename Type>
- void AddIndexedItem(IndexedCollectionVector<Type>& vector,
- Type* item,
- uint32_t offset,
- uint32_t index) {
+ template <typename Type, class... Args>
+ Type* CreateAndAddIndexedItem(IndexedCollectionVector<Type>& vector,
+ uint32_t offset,
+ uint32_t index,
+ Args&&... args) {
+ Type* item = vector.CreateAndAddIndexedItem(index, std::forward<Args>(args)...);
DCHECK(!item->OffsetAssigned());
if (eagerly_assign_offsets_) {
item->SetOffset(offset);
}
- vector.AddIndexedItem(item, index);
+ return item;
}
void SetEagerlyAssignOffsets(bool eagerly_assign_offsets) {
@@ -425,7 +453,7 @@
ParameterAnnotation* GenerateParameterAnnotation(const DexFile& dex_file, MethodId* method_id,
const DexFile::AnnotationSetRefList* annotation_set_ref_list, uint32_t offset);
- MethodItem* GenerateMethodItem(const DexFile& dex_file, ClassDataItemIterator& cdii);
+ MethodItem GenerateMethodItem(const DexFile& dex_file, ClassDataItemIterator& cdii);
// Collection vectors own the IR data.
IndexedCollectionVector<StringId> string_ids_;
@@ -433,6 +461,7 @@
IndexedCollectionVector<ProtoId> proto_ids_;
IndexedCollectionVector<FieldId> field_ids_;
IndexedCollectionVector<MethodId> method_ids_;
+ IndexedCollectionVector<ClassDef> class_defs_;
IndexedCollectionVector<CallSiteId> call_site_ids_;
IndexedCollectionVector<MethodHandleItem> method_handle_items_;
IndexedCollectionVector<StringData> string_datas_;
@@ -442,7 +471,6 @@
IndexedCollectionVector<AnnotationSetItem> annotation_set_items_;
IndexedCollectionVector<AnnotationSetRefList> annotation_set_ref_lists_;
IndexedCollectionVector<AnnotationsDirectoryItem> annotations_directory_items_;
- IndexedCollectionVector<ClassDef> class_defs_;
// The order of the vectors controls the layout of the output file by index order, to change the
// layout just sort the vector. Note that you may only change the order of the non indexed vectors
// below. Indexed vectors are accessed by indices in other places, changing the sorting order will
@@ -485,6 +513,8 @@
Item() { }
virtual ~Item() { }
+ Item(Item&&) = default;
+
// Return the assigned offset.
uint32_t GetOffset() const WARN_UNUSED {
CHECK(OffsetAssigned());
@@ -536,18 +566,54 @@
uint32_t data_size,
uint32_t data_offset,
bool support_default_methods)
+ : Item(0, kHeaderItemSize), support_default_methods_(support_default_methods) {
+ ConstructorHelper(magic,
+ checksum,
+ signature,
+ endian_tag,
+ file_size,
+ header_size,
+ link_size,
+ link_offset,
+ data_size,
+ data_offset);
+ }
+
+ Header(const uint8_t* magic,
+ uint32_t checksum,
+ const uint8_t* signature,
+ uint32_t endian_tag,
+ uint32_t file_size,
+ uint32_t header_size,
+ uint32_t link_size,
+ uint32_t link_offset,
+ uint32_t data_size,
+ uint32_t data_offset,
+ bool support_default_methods,
+ uint32_t num_string_ids,
+ uint32_t num_type_ids,
+ uint32_t num_proto_ids,
+ uint32_t num_field_ids,
+ uint32_t num_method_ids,
+ uint32_t num_class_defs)
: Item(0, kHeaderItemSize),
- checksum_(checksum),
- endian_tag_(endian_tag),
- file_size_(file_size),
- header_size_(header_size),
- link_size_(link_size),
- link_offset_(link_offset),
- data_size_(data_size),
- data_offset_(data_offset),
- support_default_methods_(support_default_methods) {
- memcpy(magic_, magic, sizeof(magic_));
- memcpy(signature_, signature, sizeof(signature_));
+ support_default_methods_(support_default_methods),
+ collections_(num_string_ids,
+ num_type_ids,
+ num_proto_ids,
+ num_field_ids,
+ num_method_ids,
+ num_class_defs) {
+ ConstructorHelper(magic,
+ checksum,
+ signature,
+ endian_tag,
+ file_size,
+ header_size,
+ link_size,
+ link_offset,
+ data_size,
+ data_offset);
}
~Header() OVERRIDE { }
@@ -596,6 +662,27 @@
uint32_t data_offset_;
const bool support_default_methods_;
+ void ConstructorHelper(const uint8_t* magic,
+ uint32_t checksum,
+ const uint8_t* signature,
+ uint32_t endian_tag,
+ uint32_t file_size,
+ uint32_t header_size,
+ uint32_t link_size,
+ uint32_t link_offset,
+ uint32_t data_size,
+ uint32_t data_offset) {
+ checksum_ = checksum;
+ endian_tag_ = endian_tag;
+ file_size_ = file_size;
+ header_size_ = header_size;
+ link_size_ = link_size;
+ link_offset_ = link_offset;
+ data_size_ = data_size;
+ data_offset_ = data_offset;
+ memcpy(magic_, magic, sizeof(magic_));
+ memcpy(signature_, signature, sizeof(signature_));
+ }
Collections collections_;
DISALLOW_COPY_AND_ASSIGN(Header);
@@ -744,6 +831,8 @@
: access_flags_(access_flags), field_id_(field_id) { }
~FieldItem() OVERRIDE { }
+ FieldItem(FieldItem&&) = default;
+
uint32_t GetAccessFlags() const { return access_flags_; }
const FieldId* GetFieldId() const { return field_id_; }
@@ -756,7 +845,7 @@
DISALLOW_COPY_AND_ASSIGN(FieldItem);
};
-using FieldItemVector = std::vector<std::unique_ptr<FieldItem>>;
+using FieldItemVector = std::vector<FieldItem>;
class MethodItem : public Item {
public:
@@ -764,6 +853,8 @@
: access_flags_(access_flags), method_id_(method_id), code_(code) { }
~MethodItem() OVERRIDE { }
+ MethodItem(MethodItem&&) = default;
+
uint32_t GetAccessFlags() const { return access_flags_; }
const MethodId* GetMethodId() const { return method_id_; }
CodeItem* GetCodeItem() { return code_; }
@@ -778,7 +869,7 @@
DISALLOW_COPY_AND_ASSIGN(MethodItem);
};
-using MethodItemVector = std::vector<std::unique_ptr<MethodItem>>;
+using MethodItemVector = std::vector<MethodItem>;
class EncodedValue {
public:
diff --git a/dexlayout/dex_ir_builder.cc b/dexlayout/dex_ir_builder.cc
index 4f9bcdd..9468f76 100644
--- a/dexlayout/dex_ir_builder.cc
+++ b/dexlayout/dex_ir_builder.cc
@@ -43,7 +43,13 @@
disk_header.link_off_,
disk_header.data_size_,
disk_header.data_off_,
- dex_file.SupportsDefaultMethods());
+ dex_file.SupportsDefaultMethods(),
+ dex_file.NumStringIds(),
+ dex_file.NumTypeIds(),
+ dex_file.NumProtoIds(),
+ dex_file.NumFieldIds(),
+ dex_file.NumMethodIds(),
+ dex_file.NumClassDefs());
Collections& collections = header->GetCollections();
collections.SetEagerlyAssignOffsets(eagerly_assign_offsets);
// Walk the rest of the header fields.
@@ -94,6 +100,7 @@
// Sort the vectors by the map order (same order as the file).
collections.SortVectorsByMapOrder();
+ collections.ClearMaps();
// Load the link data if it exists.
collections.SetLinkData(std::vector<uint8_t>(
diff --git a/dexlayout/dex_verify.cc b/dexlayout/dex_verify.cc
index 18ddc86..2e4756b 100644
--- a/dexlayout/dex_verify.cc
+++ b/dexlayout/dex_verify.cc
@@ -769,8 +769,8 @@
return false;
}
for (size_t i = 0; i < orig->size(); ++i) {
- dex_ir::FieldItem* orig_field = (*orig)[i].get();
- dex_ir::FieldItem* output_field = (*output)[i].get();
+ dex_ir::FieldItem* orig_field = &(*orig)[i];
+ dex_ir::FieldItem* output_field = &(*output)[i];
if (orig_field->GetFieldId()->GetIndex() != output_field->GetFieldId()->GetIndex()) {
*error_msg = StringPrintf("Mismatched field index for class data at offset %x: %u vs %u.",
orig_offset,
@@ -802,8 +802,8 @@
return false;
}
for (size_t i = 0; i < orig->size(); ++i) {
- dex_ir::MethodItem* orig_method = (*orig)[i].get();
- dex_ir::MethodItem* output_method = (*output)[i].get();
+ dex_ir::MethodItem* orig_method = &(*orig)[i];
+ dex_ir::MethodItem* output_method = &(*output)[i];
if (orig_method->GetMethodId()->GetIndex() != output_method->GetMethodId()->GetIndex()) {
*error_msg = StringPrintf("Mismatched method index for class data at offset %x: %u vs %u.",
orig_offset,
diff --git a/dexlayout/dex_visualize.cc b/dexlayout/dex_visualize.cc
index c8aac94..0e04c58 100644
--- a/dexlayout/dex_visualize.cc
+++ b/dexlayout/dex_visualize.cc
@@ -279,22 +279,22 @@
dumper->DumpAddressRange(class_data, class_index);
if (class_data->StaticFields()) {
for (auto& field_item : *class_data->StaticFields()) {
- dumper->DumpFieldItem(field_item.get(), class_index);
+ dumper->DumpFieldItem(&field_item, class_index);
}
}
if (class_data->InstanceFields()) {
for (auto& field_item : *class_data->InstanceFields()) {
- dumper->DumpFieldItem(field_item.get(), class_index);
+ dumper->DumpFieldItem(&field_item, class_index);
}
}
if (class_data->DirectMethods()) {
for (auto& method_item : *class_data->DirectMethods()) {
- dumper->DumpMethodItem(method_item.get(), dex_file, class_index, profile_info);
+ dumper->DumpMethodItem(&method_item, dex_file, class_index, profile_info);
}
}
if (class_data->VirtualMethods()) {
for (auto& method_item : *class_data->VirtualMethods()) {
- dumper->DumpMethodItem(method_item.get(), dex_file, class_index, profile_info);
+ dumper->DumpMethodItem(&method_item, dex_file, class_index, profile_info);
}
}
}
diff --git a/dexlayout/dex_writer.cc b/dexlayout/dex_writer.cc
index eead13f..9ed1312 100644
--- a/dexlayout/dex_writer.cc
+++ b/dexlayout/dex_writer.cc
@@ -207,21 +207,21 @@
void DexWriter::WriteEncodedFields(Stream* stream, dex_ir::FieldItemVector* fields) {
uint32_t prev_index = 0;
- for (std::unique_ptr<dex_ir::FieldItem>& field : *fields) {
- uint32_t index = field->GetFieldId()->GetIndex();
+ for (auto& field : *fields) {
+ uint32_t index = field.GetFieldId()->GetIndex();
stream->WriteUleb128(index - prev_index);
- stream->WriteUleb128(field->GetAccessFlags());
+ stream->WriteUleb128(field.GetAccessFlags());
prev_index = index;
}
}
void DexWriter::WriteEncodedMethods(Stream* stream, dex_ir::MethodItemVector* methods) {
uint32_t prev_index = 0;
- for (std::unique_ptr<dex_ir::MethodItem>& method : *methods) {
- uint32_t index = method->GetMethodId()->GetIndex();
- uint32_t code_off = method->GetCodeItem() == nullptr ? 0 : method->GetCodeItem()->GetOffset();
+ for (auto& method : *methods) {
+ uint32_t index = method.GetMethodId()->GetIndex();
+ uint32_t code_off = method.GetCodeItem() == nullptr ? 0 : method.GetCodeItem()->GetOffset();
stream->WriteUleb128(index - prev_index);
- stream->WriteUleb128(method->GetAccessFlags());
+ stream->WriteUleb128(method.GetAccessFlags());
stream->WriteUleb128(code_off);
prev_index = index;
}
diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc
index 62dd1a9..39d93bf 100644
--- a/dexlayout/dexlayout.cc
+++ b/dexlayout/dexlayout.cc
@@ -1459,8 +1459,8 @@
dex_ir::FieldItemVector* static_fields = class_data->StaticFields();
if (static_fields != nullptr) {
for (uint32_t i = 0; i < static_fields->size(); i++) {
- DumpSField((*static_fields)[i]->GetFieldId()->GetIndex(),
- (*static_fields)[i]->GetAccessFlags(),
+ DumpSField((*static_fields)[i].GetFieldId()->GetIndex(),
+ (*static_fields)[i].GetAccessFlags(),
i,
i < encoded_values_size ? (*encoded_values)[i].get() : nullptr);
} // for
@@ -1475,8 +1475,8 @@
dex_ir::FieldItemVector* instance_fields = class_data->InstanceFields();
if (instance_fields != nullptr) {
for (uint32_t i = 0; i < instance_fields->size(); i++) {
- DumpIField((*instance_fields)[i]->GetFieldId()->GetIndex(),
- (*instance_fields)[i]->GetAccessFlags(),
+ DumpIField((*instance_fields)[i].GetFieldId()->GetIndex(),
+ (*instance_fields)[i].GetAccessFlags(),
i);
} // for
}
@@ -1490,9 +1490,9 @@
dex_ir::MethodItemVector* direct_methods = class_data->DirectMethods();
if (direct_methods != nullptr) {
for (uint32_t i = 0; i < direct_methods->size(); i++) {
- DumpMethod((*direct_methods)[i]->GetMethodId()->GetIndex(),
- (*direct_methods)[i]->GetAccessFlags(),
- (*direct_methods)[i]->GetCodeItem(),
+ DumpMethod((*direct_methods)[i].GetMethodId()->GetIndex(),
+ (*direct_methods)[i].GetAccessFlags(),
+ (*direct_methods)[i].GetCodeItem(),
i);
} // for
}
@@ -1506,9 +1506,9 @@
dex_ir::MethodItemVector* virtual_methods = class_data->VirtualMethods();
if (virtual_methods != nullptr) {
for (uint32_t i = 0; i < virtual_methods->size(); i++) {
- DumpMethod((*virtual_methods)[i]->GetMethodId()->GetIndex(),
- (*virtual_methods)[i]->GetAccessFlags(),
- (*virtual_methods)[i]->GetCodeItem(),
+ DumpMethod((*virtual_methods)[i].GetMethodId()->GetIndex(),
+ (*virtual_methods)[i].GetAccessFlags(),
+ (*virtual_methods)[i].GetCodeItem(),
i);
} // for
}
@@ -1636,14 +1636,14 @@
}
for (size_t i = 0; i < 2; ++i) {
for (auto& method : *(i == 0 ? data->DirectMethods() : data->VirtualMethods())) {
- const dex_ir::MethodId* method_id = method->GetMethodId();
- dex_ir::CodeItem* code_item = method->GetCodeItem();
+ const dex_ir::MethodId* method_id = method.GetMethodId();
+ dex_ir::CodeItem* code_item = method.GetCodeItem();
if (code_item == nullptr) {
continue;
}
const bool is_clinit = is_profile_class &&
- (method->GetAccessFlags() & kAccConstructor) != 0 &&
- (method->GetAccessFlags() & kAccStatic) != 0;
+ (method.GetAccessFlags() & kAccConstructor) != 0 &&
+ (method.GetAccessFlags() & kAccStatic) != 0;
const bool method_executed = is_clinit ||
info_->GetMethodHotness(MethodReference(dex_file, method_id->GetIndex())).IsInProfile();
if (!method_executed) {
@@ -1744,14 +1744,14 @@
for (auto& method : *(invoke_type == InvokeType::kDirect
? class_data->DirectMethods()
: class_data->VirtualMethods())) {
- const dex_ir::MethodId *method_id = method->GetMethodId();
- dex_ir::CodeItem *code_item = method->GetCodeItem();
+ const dex_ir::MethodId *method_id = method.GetMethodId();
+ dex_ir::CodeItem *code_item = method.GetCodeItem();
if (code_item == nullptr) {
continue;
}
// Separate executed methods (clinits and profiled methods) from unexecuted methods.
- const bool is_clinit = (method->GetAccessFlags() & kAccConstructor) != 0 &&
- (method->GetAccessFlags() & kAccStatic) != 0;
+ const bool is_clinit = (method.GetAccessFlags() & kAccConstructor) != 0 &&
+ (method.GetAccessFlags() & kAccStatic) != 0;
const bool is_startup_clinit = is_profile_class && is_clinit;
using Hotness = ProfileCompilationInfo::MethodHotness;
Hotness hotness = info_->GetMethodHotness(MethodReference(dex_file, method_id->GetIndex()));
diff --git a/libartbase/base/bit_table.h b/libartbase/base/bit_table.h
index 6a714e6..2cc1a31 100644
--- a/libartbase/base/bit_table.h
+++ b/libartbase/base/bit_table.h
@@ -18,6 +18,7 @@
#define ART_LIBARTBASE_BASE_BIT_TABLE_H_
#include <array>
+#include <initializer_list>
#include <numeric>
#include <string.h>
#include <type_traits>
@@ -184,33 +185,54 @@
}
// Helper class for encoding BitTable. It can optionally de-duplicate the inputs.
-// Type 'T' must be POD type consisting of uint32_t fields (one for each column).
-template<typename T>
+template<uint32_t kNumColumns>
class BitTableBuilder {
public:
- static_assert(std::is_pod<T>::value, "Type 'T' must be POD");
- static constexpr size_t kNumColumns = sizeof(T) / sizeof(uint32_t);
+ class Entry {
+ public:
+ Entry() {
+ std::fill_n(data_, kNumColumns, BitTable<kNumColumns>::Accessor::kNoValue);
+ }
+
+ Entry(std::initializer_list<uint32_t> values) {
+ DCHECK_EQ(values.size(), kNumColumns);
+ std::copy(values.begin(), values.end(), data_);
+ }
+
+ uint32_t& operator[](size_t column) {
+ DCHECK_LT(column, kNumColumns);
+ return data_[column];
+ }
+
+ uint32_t operator[](size_t column) const {
+ DCHECK_LT(column, kNumColumns);
+ return data_[column];
+ }
+
+ private:
+ uint32_t data_[kNumColumns];
+ };
explicit BitTableBuilder(ScopedArenaAllocator* allocator)
: rows_(allocator->Adapter(kArenaAllocBitTableBuilder)),
dedup_(8, allocator->Adapter(kArenaAllocBitTableBuilder)) {
}
- T& operator[](size_t row) { return rows_[row]; }
- const T& operator[](size_t row) const { return rows_[row]; }
+ Entry& operator[](size_t row) { return rows_[row]; }
+ const Entry& operator[](size_t row) const { return rows_[row]; }
size_t size() const { return rows_.size(); }
// Append given value to the vector without de-duplication.
// This will not add the element to the dedup map to avoid its associated costs.
- void Add(T value) {
+ void Add(Entry value) {
rows_.push_back(value);
}
// Append given list of values and return the index of the first value.
// If the exact same set of values was already added, return the old index.
- uint32_t Dedup(T* values, size_t count = 1) {
+ uint32_t Dedup(Entry* values, size_t count = 1) {
FNVHash<MemoryRegion> hasher;
- uint32_t hash = hasher(MemoryRegion(values, sizeof(T) * count));
+ uint32_t hash = hasher(MemoryRegion(values, sizeof(Entry) * count));
// Check if we have already added identical set of values.
auto range = dedup_.equal_range(hash);
@@ -220,8 +242,8 @@
std::equal(values,
values + count,
rows_.begin() + index,
- [](const T& lhs, const T& rhs) {
- return memcmp(&lhs, &rhs, sizeof(T)) == 0;
+ [](const Entry& lhs, const Entry& rhs) {
+ return memcmp(&lhs, &rhs, sizeof(Entry)) == 0;
})) {
return index;
}
@@ -234,11 +256,8 @@
return index;
}
- ALWAYS_INLINE uint32_t Get(uint32_t row, uint32_t column) const {
- DCHECK_LT(row, size());
- DCHECK_LT(column, kNumColumns);
- const uint32_t* data = reinterpret_cast<const uint32_t*>(&rows_[row]);
- return data[column];
+ uint32_t Dedup(Entry value) {
+ return Dedup(&value, /* count */ 1);
}
// Calculate the column bit widths based on the current data.
@@ -247,7 +266,7 @@
std::fill_n(max_column_value, kNumColumns, 0);
for (uint32_t r = 0; r < size(); r++) {
for (uint32_t c = 0; c < kNumColumns; c++) {
- max_column_value[c] |= Get(r, c) - BitTable<kNumColumns>::kValueBias;
+ max_column_value[c] |= rows_[r][c] - BitTable<kNumColumns>::kValueBias;
}
}
for (uint32_t c = 0; c < kNumColumns; c++) {
@@ -276,7 +295,7 @@
BitMemoryRegion region(MemoryRegion(out->data(), out->size()));
for (uint32_t r = 0; r < size(); r++) {
for (uint32_t c = 0; c < kNumColumns; c++) {
- region.StoreBitsAndAdvance(bit_offset, Get(r, c) - bias, column_bits[c]);
+ region.StoreBitsAndAdvance(bit_offset, rows_[r][c] - bias, column_bits[c]);
}
}
}
@@ -292,14 +311,14 @@
}
for (uint32_t r = 0; r < size(); r++) {
for (uint32_t c = 0; c < kNumColumns; c++) {
- DCHECK_EQ(Get(r, c), table.Get(r, c)) << " (" << r << ", " << c << ")";
+ DCHECK_EQ(rows_[r][c], table.Get(r, c)) << " (" << r << ", " << c << ")";
}
}
}
}
protected:
- ScopedArenaDeque<T> rows_;
+ ScopedArenaDeque<Entry> rows_;
ScopedArenaUnorderedMultimap<uint32_t, uint32_t> dedup_; // Hash -> row index.
};
diff --git a/libartbase/base/bit_table_test.cc b/libartbase/base/bit_table_test.cc
index 8abf0da..969940f 100644
--- a/libartbase/base/bit_table_test.cc
+++ b/libartbase/base/bit_table_test.cc
@@ -50,7 +50,7 @@
std::vector<uint8_t> buffer;
size_t encode_bit_offset = 0;
- BitTableBuilder<uint32_t> builder(&allocator);
+ BitTableBuilder<1> builder(&allocator);
builder.Encode(&buffer, &encode_bit_offset);
size_t decode_bit_offset = 0;
@@ -67,11 +67,11 @@
constexpr uint32_t kNoValue = -1;
std::vector<uint8_t> buffer;
size_t encode_bit_offset = 0;
- BitTableBuilder<uint32_t> builder(&allocator);
- builder.Add(42u);
- builder.Add(kNoValue);
- builder.Add(1000u);
- builder.Add(kNoValue);
+ BitTableBuilder<1> builder(&allocator);
+ builder.Add({42u});
+ builder.Add({kNoValue});
+ builder.Add({1000u});
+ builder.Add({kNoValue});
builder.Encode(&buffer, &encode_bit_offset);
size_t decode_bit_offset = 0;
@@ -93,8 +93,8 @@
for (size_t start_bit_offset = 0; start_bit_offset <= 32; start_bit_offset++) {
std::vector<uint8_t> buffer;
size_t encode_bit_offset = start_bit_offset;
- BitTableBuilder<uint32_t> builder(&allocator);
- builder.Add(42u);
+ BitTableBuilder<1> builder(&allocator);
+ builder.Add({42u});
builder.Encode(&buffer, &encode_bit_offset);
size_t decode_bit_offset = start_bit_offset;
@@ -113,15 +113,9 @@
constexpr uint32_t kNoValue = -1;
std::vector<uint8_t> buffer;
size_t encode_bit_offset = 0;
- struct RowData {
- uint32_t a;
- uint32_t b;
- uint32_t c;
- uint32_t d;
- };
- BitTableBuilder<RowData> builder(&allocator);
- builder.Add(RowData{42u, kNoValue, 0u, static_cast<uint32_t>(-2)});
- builder.Add(RowData{62u, kNoValue, 63u, static_cast<uint32_t>(-3)});
+ BitTableBuilder<4> builder(&allocator);
+ builder.Add({42u, kNoValue, 0u, static_cast<uint32_t>(-2)});
+ builder.Add({62u, kNoValue, 63u, static_cast<uint32_t>(-3)});
builder.Encode(&buffer, &encode_bit_offset);
size_t decode_bit_offset = 0;
@@ -147,13 +141,9 @@
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
- struct RowData {
- uint32_t a;
- uint32_t b;
- };
- BitTableBuilder<RowData> builder(&allocator);
- RowData value0{1, 2};
- RowData value1{3, 4};
+ BitTableBuilder<2> builder(&allocator);
+ BitTableBuilder<2>::Entry value0{1, 2};
+ BitTableBuilder<2>::Entry value1{3, 4};
EXPECT_EQ(0u, builder.Dedup(&value0));
EXPECT_EQ(1u, builder.Dedup(&value1));
EXPECT_EQ(0u, builder.Dedup(&value0));
@@ -197,16 +187,12 @@
ScopedArenaAllocator allocator(&arena_stack);
FNVHash<MemoryRegion> hasher;
- struct RowData {
- uint32_t a;
- uint32_t b;
- };
- RowData value0{56948505, 0};
- RowData value1{67108869, 0};
+ BitTableBuilder<2>::Entry value0{56948505, 0};
+ BitTableBuilder<2>::Entry value1{67108869, 0};
- BitTableBuilder<RowData> builder(&allocator);
- EXPECT_EQ(hasher(MemoryRegion(&value0, sizeof(RowData))),
- hasher(MemoryRegion(&value1, sizeof(RowData))));
+ BitTableBuilder<2> builder(&allocator);
+ EXPECT_EQ(hasher(MemoryRegion(&value0, sizeof(value0))),
+ hasher(MemoryRegion(&value1, sizeof(value1))));
EXPECT_EQ(0u, builder.Dedup(&value0));
EXPECT_EQ(1u, builder.Dedup(&value1));
EXPECT_EQ(0u, builder.Dedup(&value0));
@@ -214,12 +200,12 @@
EXPECT_EQ(2u, builder.size());
BitmapTableBuilder builder2(&allocator);
- EXPECT_EQ(hasher(MemoryRegion(&value0, BitsToBytesRoundUp(MinimumBitsToStore(value0.a)))),
- hasher(MemoryRegion(&value1, BitsToBytesRoundUp(MinimumBitsToStore(value1.a)))));
- EXPECT_EQ(0u, builder2.Dedup(&value0.a, MinimumBitsToStore(value0.a)));
- EXPECT_EQ(1u, builder2.Dedup(&value1.a, MinimumBitsToStore(value1.a)));
- EXPECT_EQ(0u, builder2.Dedup(&value0.a, MinimumBitsToStore(value0.a)));
- EXPECT_EQ(1u, builder2.Dedup(&value1.a, MinimumBitsToStore(value1.a)));
+ EXPECT_EQ(hasher(MemoryRegion(&value0, BitsToBytesRoundUp(MinimumBitsToStore(value0[0])))),
+ hasher(MemoryRegion(&value1, BitsToBytesRoundUp(MinimumBitsToStore(value1[0])))));
+ EXPECT_EQ(0u, builder2.Dedup(&value0[0], MinimumBitsToStore(value0[0])));
+ EXPECT_EQ(1u, builder2.Dedup(&value1[0], MinimumBitsToStore(value1[0])));
+ EXPECT_EQ(0u, builder2.Dedup(&value0[0], MinimumBitsToStore(value0[0])));
+ EXPECT_EQ(1u, builder2.Dedup(&value1[0], MinimumBitsToStore(value1[0])));
EXPECT_EQ(2u, builder2.size());
}
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 2db0283..2b0095c 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -546,28 +546,30 @@
os << "\n";
}
- // Dump .bss entries.
- DumpBssEntries(
- os,
- "ArtMethod",
- oat_dex_file->GetMethodBssMapping(),
- dex_file->NumMethodIds(),
- static_cast<size_t>(GetInstructionSetPointerSize(instruction_set_)),
- [=](uint32_t index) { return dex_file->PrettyMethod(index); });
- DumpBssEntries(
- os,
- "Class",
- oat_dex_file->GetTypeBssMapping(),
- dex_file->NumTypeIds(),
- sizeof(GcRoot<mirror::Class>),
- [=](uint32_t index) { return dex_file->PrettyType(dex::TypeIndex(index)); });
- DumpBssEntries(
- os,
- "String",
- oat_dex_file->GetStringBssMapping(),
- dex_file->NumStringIds(),
- sizeof(GcRoot<mirror::Class>),
- [=](uint32_t index) { return dex_file->StringDataByIdx(dex::StringIndex(index)); });
+ if (!options_.dump_header_only_) {
+ // Dump .bss entries.
+ DumpBssEntries(
+ os,
+ "ArtMethod",
+ oat_dex_file->GetMethodBssMapping(),
+ dex_file->NumMethodIds(),
+ static_cast<size_t>(GetInstructionSetPointerSize(instruction_set_)),
+ [=](uint32_t index) { return dex_file->PrettyMethod(index); });
+ DumpBssEntries(
+ os,
+ "Class",
+ oat_dex_file->GetTypeBssMapping(),
+ dex_file->NumTypeIds(),
+ sizeof(GcRoot<mirror::Class>),
+ [=](uint32_t index) { return dex_file->PrettyType(dex::TypeIndex(index)); });
+ DumpBssEntries(
+ os,
+ "String",
+ oat_dex_file->GetStringBssMapping(),
+ dex_file->NumStringIds(),
+ sizeof(GcRoot<mirror::Class>),
+ [=](uint32_t index) { return dex_file->StringDataByIdx(dex::StringIndex(index)); });
+ }
}
if (!options_.dump_header_only_) {
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 1710e78..c374e03 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -4440,8 +4440,8 @@
// Find the <init>(InvocationHandler)V method. The exact method offset varies depending
// on which front-end compiler was used to build the libcore DEX files.
- ArtMethod* proxy_constructor = proxy_class->FindConstructor(
- "(Ljava/lang/reflect/InvocationHandler;)V", image_pointer_size_);
+ ArtMethod* proxy_constructor =
+ jni::DecodeArtMethod(WellKnownClasses::java_lang_reflect_Proxy_init);
DCHECK(proxy_constructor != nullptr)
<< "Could not find <init> method in java.lang.reflect.Proxy";
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 2b1623a..3ccfa55 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -1101,11 +1101,14 @@
// that part.
ScopedQuickEntrypointChecks sqec(self, kIsDebugBuild, false);
instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+ DCHECK(!method->IsProxyMethod())
+ << "Proxy method " << method->PrettyMethod()
+ << " (declaring class: " << method->GetDeclaringClass()->PrettyClass() << ")"
+ << " should not hit instrumentation entrypoint.";
if (instrumentation->IsDeoptimized(method)) {
result = GetQuickToInterpreterBridge();
} else {
result = instrumentation->GetQuickCodeFor(method, kRuntimePointerSize);
- DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge(result));
}
bool interpreter_entry = (result == GetQuickToInterpreterBridge());
diff --git a/runtime/gc/collector/concurrent_copying-inl.h b/runtime/gc/collector/concurrent_copying-inl.h
index b331e97..36fefbd 100644
--- a/runtime/gc/collector/concurrent_copying-inl.h
+++ b/runtime/gc/collector/concurrent_copying-inl.h
@@ -32,7 +32,9 @@
namespace collector {
inline mirror::Object* ConcurrentCopying::MarkUnevacFromSpaceRegion(
- mirror::Object* ref, accounting::ContinuousSpaceBitmap* bitmap) {
+ Thread* const self,
+ mirror::Object* ref,
+ accounting::ContinuousSpaceBitmap* bitmap) {
// For the Baker-style RB, in a rare case, we could incorrectly change the object from white
// to gray even though the object has already been marked through. This happens if a mutator
// thread gets preempted before the AtomicSetReadBarrierState below, GC marks through the
@@ -63,20 +65,21 @@
if (kUseBakerReadBarrier) {
DCHECK_EQ(ref->GetReadBarrierState(), ReadBarrier::GrayState());
}
- PushOntoMarkStack(ref);
+ PushOntoMarkStack(self, ref);
}
return ref;
}
template<bool kGrayImmuneObject>
-inline mirror::Object* ConcurrentCopying::MarkImmuneSpace(mirror::Object* ref) {
+inline mirror::Object* ConcurrentCopying::MarkImmuneSpace(Thread* const self,
+ mirror::Object* ref) {
if (kUseBakerReadBarrier) {
// The GC-running thread doesn't (need to) gray immune objects except when updating thread roots
// in the thread flip on behalf of suspended threads (when gc_grays_immune_objects_ is
// true). Also, a mutator doesn't (need to) gray an immune object after GC has updated all
// immune space objects (when updated_all_immune_objects_ is true).
if (kIsDebugBuild) {
- if (Thread::Current() == thread_running_gc_) {
+ if (self == thread_running_gc_) {
DCHECK(!kGrayImmuneObject ||
updated_all_immune_objects_.load(std::memory_order_relaxed) ||
gc_grays_immune_objects_);
@@ -91,7 +94,7 @@
bool success = ref->AtomicSetReadBarrierState(/* expected_rb_state */ ReadBarrier::WhiteState(),
/* rb_state */ ReadBarrier::GrayState());
if (success) {
- MutexLock mu(Thread::Current(), immune_gray_stack_lock_);
+ MutexLock mu(self, immune_gray_stack_lock_);
immune_gray_stack_.push_back(ref);
}
}
@@ -99,7 +102,8 @@
}
template<bool kGrayImmuneObject, bool kFromGCThread>
-inline mirror::Object* ConcurrentCopying::Mark(mirror::Object* from_ref,
+inline mirror::Object* ConcurrentCopying::Mark(Thread* const self,
+ mirror::Object* from_ref,
mirror::Object* holder,
MemberOffset offset) {
if (from_ref == nullptr) {
@@ -108,7 +112,7 @@
DCHECK(heap_->collector_type_ == kCollectorTypeCC);
if (kFromGCThread) {
DCHECK(is_active_);
- DCHECK_EQ(Thread::Current(), thread_running_gc_);
+ DCHECK_EQ(self, thread_running_gc_);
} else if (UNLIKELY(kUseBakerReadBarrier && !is_active_)) {
// In the lock word forward address state, the read barrier bits
// in the lock word are part of the stored forwarding address and
@@ -134,7 +138,7 @@
mirror::Object* to_ref = GetFwdPtr(from_ref);
if (to_ref == nullptr) {
// It isn't marked yet. Mark it by copying it to the to-space.
- to_ref = Copy(from_ref, holder, offset);
+ to_ref = Copy(self, from_ref, holder, offset);
}
// The copy should either be in a to-space region, or in the
// non-moving space, if it could not fit in a to-space region.
@@ -143,7 +147,7 @@
return to_ref;
}
case space::RegionSpace::RegionType::kRegionTypeUnevacFromSpace:
- return MarkUnevacFromSpaceRegion(from_ref, region_space_bitmap_);
+ return MarkUnevacFromSpaceRegion(self, from_ref, region_space_bitmap_);
default:
// The reference is in an unused region.
LOG(FATAL_WITHOUT_ABORT) << DumpHeapReference(holder, offset, from_ref);
@@ -153,24 +157,25 @@
}
} else {
if (immune_spaces_.ContainsObject(from_ref)) {
- return MarkImmuneSpace<kGrayImmuneObject>(from_ref);
+ return MarkImmuneSpace<kGrayImmuneObject>(self, from_ref);
} else {
- return MarkNonMoving(from_ref, holder, offset);
+ return MarkNonMoving(self, from_ref, holder, offset);
}
}
}
inline mirror::Object* ConcurrentCopying::MarkFromReadBarrier(mirror::Object* from_ref) {
mirror::Object* ret;
+ Thread* const self = Thread::Current();
// We can get here before marking starts since we gray immune objects before the marking phase.
- if (from_ref == nullptr || !Thread::Current()->GetIsGcMarking()) {
+ if (from_ref == nullptr || !self->GetIsGcMarking()) {
return from_ref;
}
// TODO: Consider removing this check when we are done investigating slow paths. b/30162165
if (UNLIKELY(mark_from_read_barrier_measurements_)) {
- ret = MarkFromReadBarrierWithMeasurements(from_ref);
+ ret = MarkFromReadBarrierWithMeasurements(self, from_ref);
} else {
- ret = Mark(from_ref);
+ ret = Mark(self, from_ref);
}
// Only set the mark bit for baker barrier.
if (kUseBakerReadBarrier && LIKELY(!rb_mark_bit_stack_full_ && ret->AtomicSetMarkBit(0, 1))) {
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 2c2c437..c4d2fdd 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -129,13 +129,14 @@
void ConcurrentCopying::MarkHeapReference(mirror::HeapReference<mirror::Object>* field,
bool do_atomic_update) {
+ Thread* const self = Thread::Current();
if (UNLIKELY(do_atomic_update)) {
// Used to mark the referent in DelayReferenceReferent in transaction mode.
mirror::Object* from_ref = field->AsMirrorPtr();
if (from_ref == nullptr) {
return;
}
- mirror::Object* to_ref = Mark(from_ref);
+ mirror::Object* to_ref = Mark(self, from_ref);
if (from_ref != to_ref) {
do {
if (field->AsMirrorPtr() != from_ref) {
@@ -148,7 +149,7 @@
// Used for preserving soft references, should be OK to not have a CAS here since there should be
// no other threads which can trigger read barriers on the same referent during reference
// processing.
- field->Assign(Mark(field->AsMirrorPtr()));
+ field->Assign(Mark(self, field->AsMirrorPtr()));
}
}
@@ -300,6 +301,8 @@
immune_spaces_.Reset();
bytes_moved_.store(0, std::memory_order_relaxed);
objects_moved_.store(0, std::memory_order_relaxed);
+ bytes_moved_gc_thread_ = 0;
+ objects_moved_gc_thread_ = 0;
GcCause gc_cause = GetCurrentIteration()->GetGcCause();
if (gc_cause == kGcCauseExplicit ||
gc_cause == kGcCauseCollectorTransition ||
@@ -370,11 +373,12 @@
size_t count,
const RootInfo& info ATTRIBUTE_UNUSED)
REQUIRES_SHARED(Locks::mutator_lock_) {
+ Thread* self = Thread::Current();
for (size_t i = 0; i < count; ++i) {
mirror::Object** root = roots[i];
mirror::Object* ref = *root;
if (ref != nullptr) {
- mirror::Object* to_ref = concurrent_copying_->Mark(ref);
+ mirror::Object* to_ref = concurrent_copying_->Mark(self, ref);
if (to_ref != ref) {
*root = to_ref;
}
@@ -386,11 +390,12 @@
size_t count,
const RootInfo& info ATTRIBUTE_UNUSED)
REQUIRES_SHARED(Locks::mutator_lock_) {
+ Thread* self = Thread::Current();
for (size_t i = 0; i < count; ++i) {
mirror::CompressedReference<mirror::Object>* const root = roots[i];
if (!root->IsNull()) {
mirror::Object* ref = root->AsMirrorPtr();
- mirror::Object* to_ref = concurrent_copying_->Mark(ref);
+ mirror::Object* to_ref = concurrent_copying_->Mark(self, ref);
if (to_ref != ref) {
root->Assign(to_ref);
}
@@ -452,7 +457,7 @@
// This is safe since single threaded behavior should mean FillDummyObject does not
// happen when java_lang_Object_ is null.
if (WellKnownClasses::java_lang_Object != nullptr) {
- cc->java_lang_Object_ = down_cast<mirror::Class*>(cc->Mark(
+ cc->java_lang_Object_ = down_cast<mirror::Class*>(cc->Mark(thread,
WellKnownClasses::ToClass(WellKnownClasses::java_lang_Object).Ptr()));
} else {
cc->java_lang_Object_ = nullptr;
@@ -1024,10 +1029,10 @@
mark_stack_mode_.store(kMarkStackModeOff, std::memory_order_seq_cst);
}
-void ConcurrentCopying::PushOntoFalseGrayStack(mirror::Object* ref) {
+void ConcurrentCopying::PushOntoFalseGrayStack(Thread* const self, mirror::Object* ref) {
CHECK(kUseBakerReadBarrier);
DCHECK(ref != nullptr);
- MutexLock mu(Thread::Current(), mark_stack_lock_);
+ MutexLock mu(self, mark_stack_lock_);
false_gray_stack_.push_back(ref);
}
@@ -1070,10 +1075,9 @@
DCHECK(!gc_mark_stack_->IsFull());
}
-void ConcurrentCopying::PushOntoMarkStack(mirror::Object* to_ref) {
+void ConcurrentCopying::PushOntoMarkStack(Thread* const self, mirror::Object* to_ref) {
CHECK_EQ(is_mark_stack_push_disallowed_.load(std::memory_order_relaxed), 0)
<< " " << to_ref << " " << mirror::Object::PrettyTypeOf(to_ref);
- Thread* self = Thread::Current(); // TODO: pass self as an argument from call sites?
CHECK(thread_running_gc_ != nullptr);
MarkStackMode mark_stack_mode = mark_stack_mode_.load(std::memory_order_relaxed);
if (LIKELY(mark_stack_mode == kMarkStackModeThreadLocal)) {
@@ -1409,10 +1413,10 @@
}
bool ConcurrentCopying::ProcessMarkStackOnce() {
- Thread* self = Thread::Current();
- CHECK(thread_running_gc_ != nullptr);
- CHECK(self == thread_running_gc_);
- CHECK(self->GetThreadLocalMarkStack() == nullptr);
+ DCHECK(thread_running_gc_ != nullptr);
+ Thread* const self = Thread::Current();
+ DCHECK(self == thread_running_gc_);
+ DCHECK(thread_running_gc_->GetThreadLocalMarkStack() == nullptr);
size_t count = 0;
MarkStackMode mark_stack_mode = mark_stack_mode_.load(std::memory_order_relaxed);
if (mark_stack_mode == kMarkStackModeThreadLocal) {
@@ -1432,14 +1436,14 @@
IssueEmptyCheckpoint();
// Process the shared GC mark stack with a lock.
{
- MutexLock mu(self, mark_stack_lock_);
+ MutexLock mu(thread_running_gc_, mark_stack_lock_);
CHECK(revoked_mark_stacks_.empty());
}
while (true) {
std::vector<mirror::Object*> refs;
{
// Copy refs with lock. Note the number of refs should be small.
- MutexLock mu(self, mark_stack_lock_);
+ MutexLock mu(thread_running_gc_, mark_stack_lock_);
if (gc_mark_stack_->IsEmpty()) {
break;
}
@@ -1458,7 +1462,7 @@
CHECK_EQ(static_cast<uint32_t>(mark_stack_mode),
static_cast<uint32_t>(kMarkStackModeGcExclusive));
{
- MutexLock mu(self, mark_stack_lock_);
+ MutexLock mu(thread_running_gc_, mark_stack_lock_);
CHECK(revoked_mark_stacks_.empty());
}
// Process the GC mark stack in the exclusive mode. No need to take the lock.
@@ -1481,7 +1485,7 @@
size_t count = 0;
std::vector<accounting::AtomicStack<mirror::Object>*> mark_stacks;
{
- MutexLock mu(Thread::Current(), mark_stack_lock_);
+ MutexLock mu(thread_running_gc_, mark_stack_lock_);
// Make a copy of the mark stack vector.
mark_stacks = revoked_mark_stacks_;
revoked_mark_stacks_.clear();
@@ -1493,7 +1497,7 @@
++count;
}
{
- MutexLock mu(Thread::Current(), mark_stack_lock_);
+ MutexLock mu(thread_running_gc_, mark_stack_lock_);
if (pooled_mark_stacks_.size() >= kMarkStackPoolSize) {
// The pool has enough. Delete it.
delete mark_stack;
@@ -1596,9 +1600,9 @@
void ConcurrentCopying::SwitchToSharedMarkStackMode() {
Thread* self = Thread::Current();
- CHECK(thread_running_gc_ != nullptr);
- CHECK_EQ(self, thread_running_gc_);
- CHECK(self->GetThreadLocalMarkStack() == nullptr);
+ DCHECK(thread_running_gc_ != nullptr);
+ DCHECK(self == thread_running_gc_);
+ DCHECK(thread_running_gc_->GetThreadLocalMarkStack() == nullptr);
MarkStackMode before_mark_stack_mode = mark_stack_mode_.load(std::memory_order_relaxed);
CHECK_EQ(static_cast<uint32_t>(before_mark_stack_mode),
static_cast<uint32_t>(kMarkStackModeThreadLocal));
@@ -1614,9 +1618,9 @@
void ConcurrentCopying::SwitchToGcExclusiveMarkStackMode() {
Thread* self = Thread::Current();
- CHECK(thread_running_gc_ != nullptr);
- CHECK_EQ(self, thread_running_gc_);
- CHECK(self->GetThreadLocalMarkStack() == nullptr);
+ DCHECK(thread_running_gc_ != nullptr);
+ DCHECK(self == thread_running_gc_);
+ DCHECK(thread_running_gc_->GetThreadLocalMarkStack() == nullptr);
MarkStackMode before_mark_stack_mode = mark_stack_mode_.load(std::memory_order_relaxed);
CHECK_EQ(static_cast<uint32_t>(before_mark_stack_mode),
static_cast<uint32_t>(kMarkStackModeShared));
@@ -1629,14 +1633,14 @@
void ConcurrentCopying::CheckEmptyMarkStack() {
Thread* self = Thread::Current();
- CHECK(thread_running_gc_ != nullptr);
- CHECK_EQ(self, thread_running_gc_);
- CHECK(self->GetThreadLocalMarkStack() == nullptr);
+ DCHECK(thread_running_gc_ != nullptr);
+ DCHECK(self == thread_running_gc_);
+ DCHECK(thread_running_gc_->GetThreadLocalMarkStack() == nullptr);
MarkStackMode mark_stack_mode = mark_stack_mode_.load(std::memory_order_relaxed);
if (mark_stack_mode == kMarkStackModeThreadLocal) {
// Thread-local mark stack mode.
RevokeThreadLocalMarkStacks(false, nullptr);
- MutexLock mu(Thread::Current(), mark_stack_lock_);
+ MutexLock mu(thread_running_gc_, mark_stack_lock_);
if (!revoked_mark_stacks_.empty()) {
for (accounting::AtomicStack<mirror::Object>* mark_stack : revoked_mark_stacks_) {
while (!mark_stack->IsEmpty()) {
@@ -1655,7 +1659,7 @@
}
} else {
// Shared, GC-exclusive, or off.
- MutexLock mu(Thread::Current(), mark_stack_lock_);
+ MutexLock mu(thread_running_gc_, mark_stack_lock_);
CHECK(gc_mark_stack_->IsEmpty());
CHECK(revoked_mark_stacks_.empty());
}
@@ -1755,9 +1759,9 @@
const uint64_t from_objects = region_space_->GetObjectsAllocatedInFromSpace();
const uint64_t unevac_from_bytes = region_space_->GetBytesAllocatedInUnevacFromSpace();
const uint64_t unevac_from_objects = region_space_->GetObjectsAllocatedInUnevacFromSpace();
- uint64_t to_bytes = bytes_moved_.load(std::memory_order_seq_cst);
+ uint64_t to_bytes = bytes_moved_.load(std::memory_order_seq_cst) + bytes_moved_gc_thread_;
cumulative_bytes_moved_.fetch_add(to_bytes, std::memory_order_relaxed);
- uint64_t to_objects = objects_moved_.load(std::memory_order_seq_cst);
+ uint64_t to_objects = objects_moved_.load(std::memory_order_seq_cst) + objects_moved_gc_thread_;
cumulative_objects_moved_.fetch_add(to_objects, std::memory_order_relaxed);
if (kEnableFromSpaceAccountingCheck) {
CHECK_EQ(from_space_num_objects_at_first_pause_, from_objects + unevac_from_objects);
@@ -2075,8 +2079,8 @@
// Used to scan ref fields of an object.
class ConcurrentCopying::RefFieldsVisitor {
public:
- explicit RefFieldsVisitor(ConcurrentCopying* collector)
- : collector_(collector) {}
+ explicit RefFieldsVisitor(ConcurrentCopying* collector, Thread* const thread)
+ : collector_(collector), thread_(thread) {}
void operator()(mirror::Object* obj, MemberOffset offset, bool /* is_static */)
const ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_)
@@ -2101,11 +2105,12 @@
void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
ALWAYS_INLINE
REQUIRES_SHARED(Locks::mutator_lock_) {
- collector_->MarkRoot</*kGrayImmuneObject*/false>(root);
+ collector_->MarkRoot</*kGrayImmuneObject*/false>(thread_, root);
}
private:
ConcurrentCopying* const collector_;
+ Thread* const thread_;
};
inline void ConcurrentCopying::Scan(mirror::Object* to_ref) {
@@ -2117,12 +2122,12 @@
}
DCHECK(!region_space_->IsInFromSpace(to_ref));
DCHECK_EQ(Thread::Current(), thread_running_gc_);
- RefFieldsVisitor visitor(this);
+ RefFieldsVisitor visitor(this, thread_running_gc_);
// Disable the read barrier for a performance reason.
to_ref->VisitReferences</*kVisitNativeRoots*/true, kDefaultVerifyFlags, kWithoutReadBarrier>(
visitor, visitor);
if (kDisallowReadBarrierDuringScan && !Runtime::Current()->IsActiveTransaction()) {
- Thread::Current()->ModifyDebugDisallowReadBarrier(-1);
+ thread_running_gc_->ModifyDebugDisallowReadBarrier(-1);
}
}
@@ -2131,6 +2136,7 @@
mirror::Object* ref = obj->GetFieldObject<
mirror::Object, kVerifyNone, kWithoutReadBarrier, false>(offset);
mirror::Object* to_ref = Mark</*kGrayImmuneObject*/false, /*kFromGCThread*/true>(
+ thread_running_gc_,
ref,
/*holder*/ obj,
offset);
@@ -2156,10 +2162,11 @@
// Process some roots.
inline void ConcurrentCopying::VisitRoots(
mirror::Object*** roots, size_t count, const RootInfo& info ATTRIBUTE_UNUSED) {
+ Thread* const self = Thread::Current();
for (size_t i = 0; i < count; ++i) {
mirror::Object** root = roots[i];
mirror::Object* ref = *root;
- mirror::Object* to_ref = Mark(ref);
+ mirror::Object* to_ref = Mark(self, ref);
if (to_ref == ref) {
continue;
}
@@ -2176,10 +2183,11 @@
}
template<bool kGrayImmuneObject>
-inline void ConcurrentCopying::MarkRoot(mirror::CompressedReference<mirror::Object>* root) {
+inline void ConcurrentCopying::MarkRoot(Thread* const self,
+ mirror::CompressedReference<mirror::Object>* root) {
DCHECK(!root->IsNull());
mirror::Object* const ref = root->AsMirrorPtr();
- mirror::Object* to_ref = Mark<kGrayImmuneObject>(ref);
+ mirror::Object* to_ref = Mark<kGrayImmuneObject>(self, ref);
if (to_ref != ref) {
auto* addr = reinterpret_cast<Atomic<mirror::CompressedReference<mirror::Object>>*>(root);
auto expected_ref = mirror::CompressedReference<mirror::Object>::FromMirrorPtr(ref);
@@ -2197,11 +2205,12 @@
inline void ConcurrentCopying::VisitRoots(
mirror::CompressedReference<mirror::Object>** roots, size_t count,
const RootInfo& info ATTRIBUTE_UNUSED) {
+ Thread* const self = Thread::Current();
for (size_t i = 0; i < count; ++i) {
mirror::CompressedReference<mirror::Object>* const root = roots[i];
if (!root->IsNull()) {
// kGrayImmuneObject is true because this is used for the thread flip.
- MarkRoot</*kGrayImmuneObject*/true>(root);
+ MarkRoot</*kGrayImmuneObject*/true>(self, root);
}
}
}
@@ -2235,7 +2244,9 @@
// Fill the given memory block with a dummy object. Used to fill in a
// copy of objects that was lost in race.
-void ConcurrentCopying::FillWithDummyObject(mirror::Object* dummy_obj, size_t byte_size) {
+void ConcurrentCopying::FillWithDummyObject(Thread* const self,
+ mirror::Object* dummy_obj,
+ size_t byte_size) {
// GC doesn't gray immune objects while scanning immune objects. But we need to trigger the read
// barriers here because we need the updated reference to the int array class, etc. Temporary set
// gc_grays_immune_objects_ to true so that we won't cause a DCHECK failure in MarkImmuneSpace().
@@ -2245,7 +2256,7 @@
// Avoid going through read barrier for since kDisallowReadBarrierDuringScan may be enabled.
// Explicitly mark to make sure to get an object in the to-space.
mirror::Class* int_array_class = down_cast<mirror::Class*>(
- Mark(GetClassRoot<mirror::IntArray, kWithoutReadBarrier>().Ptr()));
+ Mark(self, GetClassRoot<mirror::IntArray, kWithoutReadBarrier>().Ptr()));
CHECK(int_array_class != nullptr);
if (ReadBarrier::kEnableToSpaceInvariantChecks) {
AssertToSpaceInvariant(nullptr, MemberOffset(0), int_array_class);
@@ -2279,10 +2290,9 @@
}
// Reuse the memory blocks that were copy of objects that were lost in race.
-mirror::Object* ConcurrentCopying::AllocateInSkippedBlock(size_t alloc_size) {
+mirror::Object* ConcurrentCopying::AllocateInSkippedBlock(Thread* const self, size_t alloc_size) {
// Try to reuse the blocks that were unused due to CAS failures.
CHECK_ALIGNED(alloc_size, space::RegionSpace::kAlignment);
- Thread* self = Thread::Current();
size_t min_object_size = RoundUp(sizeof(mirror::Object), space::RegionSpace::kAlignment);
size_t byte_size;
uint8_t* addr;
@@ -2326,7 +2336,8 @@
// FillWithDummyObject may mark an object, avoid holding skipped_blocks_lock_ to prevent lock
// violation and possible deadlock. The deadlock case is a recursive case:
// FillWithDummyObject -> Mark(IntArray.class) -> Copy -> AllocateInSkippedBlock.
- FillWithDummyObject(reinterpret_cast<mirror::Object*>(addr + alloc_size),
+ FillWithDummyObject(self,
+ reinterpret_cast<mirror::Object*>(addr + alloc_size),
byte_size - alloc_size);
CHECK(region_space_->IsInToSpace(reinterpret_cast<mirror::Object*>(addr + alloc_size)));
{
@@ -2337,7 +2348,8 @@
return reinterpret_cast<mirror::Object*>(addr);
}
-mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref,
+mirror::Object* ConcurrentCopying::Copy(Thread* const self,
+ mirror::Object* from_ref,
mirror::Object* holder,
MemberOffset offset) {
DCHECK(region_space_->IsInFromSpace(from_ref));
@@ -2366,7 +2378,7 @@
DCHECK_EQ(region_space_alloc_size, region_space_bytes_allocated);
} else {
// Failed to allocate in the region space. Try the skipped blocks.
- to_ref = AllocateInSkippedBlock(region_space_alloc_size);
+ to_ref = AllocateInSkippedBlock(self, region_space_alloc_size);
if (to_ref != nullptr) {
// Succeeded to allocate in a skipped block.
if (heap_->use_tlab_) {
@@ -2386,7 +2398,7 @@
<< " skipped_objects="
<< to_space_objects_skipped_.load(std::memory_order_seq_cst);
}
- to_ref = heap_->non_moving_space_->Alloc(Thread::Current(), obj_size,
+ to_ref = heap_->non_moving_space_->Alloc(self, obj_size,
&non_moving_space_bytes_allocated, nullptr, &dummy);
if (UNLIKELY(to_ref == nullptr)) {
LOG(FATAL_WITHOUT_ABORT) << "Fall-back non-moving space allocation failed for a "
@@ -2427,7 +2439,7 @@
// the forwarding pointer first. Make the lost copy (to_ref)
// look like a valid but dead (dummy) object and keep it for
// future reuse.
- FillWithDummyObject(to_ref, bytes_allocated);
+ FillWithDummyObject(self, to_ref, bytes_allocated);
if (!fall_back_to_non_moving) {
DCHECK(region_space_->IsInToSpace(to_ref));
if (bytes_allocated > space::RegionSpace::kRegionSize) {
@@ -2438,7 +2450,7 @@
heap_->num_bytes_allocated_.fetch_add(bytes_allocated, std::memory_order_seq_cst);
to_space_bytes_skipped_.fetch_add(bytes_allocated, std::memory_order_seq_cst);
to_space_objects_skipped_.fetch_add(1, std::memory_order_seq_cst);
- MutexLock mu(Thread::Current(), skipped_blocks_lock_);
+ MutexLock mu(self, skipped_blocks_lock_);
skipped_blocks_map_.insert(std::make_pair(bytes_allocated,
reinterpret_cast<uint8_t*>(to_ref)));
}
@@ -2450,7 +2462,7 @@
heap_mark_bitmap_->GetContinuousSpaceBitmap(to_ref);
CHECK(mark_bitmap != nullptr);
CHECK(mark_bitmap->Clear(to_ref));
- heap_->non_moving_space_->Free(Thread::Current(), to_ref);
+ heap_->non_moving_space_->Free(self, to_ref);
}
// Get the winner's forward ptr.
@@ -2481,8 +2493,15 @@
bool success = from_ref->CasLockWordWeakRelaxed(old_lock_word, new_lock_word);
if (LIKELY(success)) {
// The CAS succeeded.
- objects_moved_.fetch_add(1, std::memory_order_relaxed);
- bytes_moved_.fetch_add(region_space_alloc_size, std::memory_order_relaxed);
+ DCHECK(thread_running_gc_ != nullptr);
+ if (LIKELY(self == thread_running_gc_)) {
+ objects_moved_gc_thread_ += 1;
+ bytes_moved_gc_thread_ += region_space_alloc_size;
+ } else {
+ objects_moved_.fetch_add(1, std::memory_order_relaxed);
+ bytes_moved_.fetch_add(region_space_alloc_size, std::memory_order_relaxed);
+ }
+
if (LIKELY(!fall_back_to_non_moving)) {
DCHECK(region_space_->IsInToSpace(to_ref));
} else {
@@ -2494,7 +2513,7 @@
}
DCHECK(GetFwdPtr(from_ref) == to_ref);
CHECK_NE(to_ref->GetLockWord(false).GetState(), LockWord::kForwardingAddress);
- PushOntoMarkStack(to_ref);
+ PushOntoMarkStack(self, to_ref);
return to_ref;
} else {
// The CAS failed. It may have lost the race or may have failed
@@ -2573,7 +2592,8 @@
return alloc_stack->Contains(ref);
}
-mirror::Object* ConcurrentCopying::MarkNonMoving(mirror::Object* ref,
+mirror::Object* ConcurrentCopying::MarkNonMoving(Thread* const self,
+ mirror::Object* ref,
mirror::Object* holder,
MemberOffset offset) {
// ref is in a non-moving space (from_ref == to_ref).
@@ -2636,20 +2656,20 @@
// Already marked.
if (kUseBakerReadBarrier && cas_success &&
ref->GetReadBarrierState() == ReadBarrier::GrayState()) {
- PushOntoFalseGrayStack(ref);
+ PushOntoFalseGrayStack(self, ref);
}
} else if (is_los && los_bitmap->AtomicTestAndSet(ref)) {
// Already marked in LOS.
if (kUseBakerReadBarrier && cas_success &&
ref->GetReadBarrierState() == ReadBarrier::GrayState()) {
- PushOntoFalseGrayStack(ref);
+ PushOntoFalseGrayStack(self, ref);
}
} else {
// Newly marked.
if (kUseBakerReadBarrier) {
DCHECK_EQ(ref->GetReadBarrierState(), ReadBarrier::GrayState());
}
- PushOntoMarkStack(ref);
+ PushOntoMarkStack(self, ref);
}
}
}
@@ -2742,7 +2762,7 @@
}
mirror::Object* ConcurrentCopying::MarkObject(mirror::Object* from_ref) {
- return Mark(from_ref);
+ return Mark(Thread::Current(), from_ref);
}
void ConcurrentCopying::DelayReferenceReferent(ObjPtr<mirror::Class> klass,
@@ -2763,15 +2783,16 @@
region_space_->RevokeAllThreadLocalBuffers();
}
-mirror::Object* ConcurrentCopying::MarkFromReadBarrierWithMeasurements(mirror::Object* from_ref) {
- if (Thread::Current() != thread_running_gc_) {
+mirror::Object* ConcurrentCopying::MarkFromReadBarrierWithMeasurements(Thread* const self,
+ mirror::Object* from_ref) {
+ if (self != thread_running_gc_) {
rb_slow_path_count_.fetch_add(1u, std::memory_order_relaxed);
} else {
rb_slow_path_count_gc_.fetch_add(1u, std::memory_order_relaxed);
}
ScopedTrace tr(__FUNCTION__);
const uint64_t start_time = measure_read_barrier_slow_path_ ? NanoTime() : 0u;
- mirror::Object* ret = Mark(from_ref);
+ mirror::Object* ret = Mark(self, from_ref);
if (measure_read_barrier_slow_path_) {
rb_slow_path_ns_.fetch_add(NanoTime() - start_time, std::memory_order_relaxed);
}
diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h
index a00dbb5..f1e7e2f 100644
--- a/runtime/gc/collector/concurrent_copying.h
+++ b/runtime/gc/collector/concurrent_copying.h
@@ -112,7 +112,8 @@
}
template<bool kGrayImmuneObject = true, bool kFromGCThread = false>
// Mark object `from_ref`, copying it to the to-space if needed.
- ALWAYS_INLINE mirror::Object* Mark(mirror::Object* from_ref,
+ ALWAYS_INLINE mirror::Object* Mark(Thread* const self,
+ mirror::Object* from_ref,
mirror::Object* holder = nullptr,
MemberOffset offset = MemberOffset(0))
REQUIRES_SHARED(Locks::mutator_lock_)
@@ -144,9 +145,11 @@
REQUIRES_SHARED(Locks::mutator_lock_);
private:
- void PushOntoMarkStack(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_)
+ void PushOntoMarkStack(Thread* const self, mirror::Object* obj)
+ REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!mark_stack_lock_);
- mirror::Object* Copy(mirror::Object* from_ref,
+ mirror::Object* Copy(Thread* const self,
+ mirror::Object* from_ref,
mirror::Object* holder,
MemberOffset offset)
REQUIRES_SHARED(Locks::mutator_lock_)
@@ -162,7 +165,7 @@
OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_);
template<bool kGrayImmuneObject>
- void MarkRoot(mirror::CompressedReference<mirror::Object>* root)
+ void MarkRoot(Thread* const self, mirror::CompressedReference<mirror::Object>* root)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_);
virtual void VisitRoots(mirror::CompressedReference<mirror::Object>** roots, size_t count,
@@ -220,10 +223,10 @@
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_);
void MarkZygoteLargeObjects()
REQUIRES_SHARED(Locks::mutator_lock_);
- void FillWithDummyObject(mirror::Object* dummy_obj, size_t byte_size)
+ void FillWithDummyObject(Thread* const self, mirror::Object* dummy_obj, size_t byte_size)
REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
- mirror::Object* AllocateInSkippedBlock(size_t alloc_size)
+ mirror::Object* AllocateInSkippedBlock(Thread* const self, size_t alloc_size)
REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
void CheckEmptyMarkStack() REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_);
@@ -253,25 +256,30 @@
void DisableMarking() REQUIRES_SHARED(Locks::mutator_lock_);
void IssueDisableMarkingCheckpoint() REQUIRES_SHARED(Locks::mutator_lock_);
void ExpandGcMarkStack() REQUIRES_SHARED(Locks::mutator_lock_);
- mirror::Object* MarkNonMoving(mirror::Object* from_ref,
+ mirror::Object* MarkNonMoving(Thread* const self,
+ mirror::Object* from_ref,
mirror::Object* holder = nullptr,
MemberOffset offset = MemberOffset(0))
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_);
- ALWAYS_INLINE mirror::Object* MarkUnevacFromSpaceRegion(mirror::Object* from_ref,
+ ALWAYS_INLINE mirror::Object* MarkUnevacFromSpaceRegion(Thread* const self,
+ mirror::Object* from_ref,
accounting::SpaceBitmap<kObjectAlignment>* bitmap)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_);
template<bool kGrayImmuneObject>
- ALWAYS_INLINE mirror::Object* MarkImmuneSpace(mirror::Object* from_ref)
+ ALWAYS_INLINE mirror::Object* MarkImmuneSpace(Thread* const self,
+ mirror::Object* from_ref)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!immune_gray_stack_lock_);
- void PushOntoFalseGrayStack(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_)
+ void PushOntoFalseGrayStack(Thread* const self, mirror::Object* obj)
+ REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!mark_stack_lock_);
void ProcessFalseGrayStack() REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!mark_stack_lock_);
void ScanImmuneObject(mirror::Object* obj)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_);
- mirror::Object* MarkFromReadBarrierWithMeasurements(mirror::Object* from_ref)
+ mirror::Object* MarkFromReadBarrierWithMeasurements(Thread* const self,
+ mirror::Object* from_ref)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_);
void DumpPerformanceInfo(std::ostream& os) OVERRIDE REQUIRES(!rb_slow_path_histogram_lock_);
@@ -330,8 +338,12 @@
bool weak_ref_access_enabled_ GUARDED_BY(Locks::thread_list_lock_);
// How many objects and bytes we moved. Used for accounting.
- Atomic<size_t> bytes_moved_;
- Atomic<size_t> objects_moved_;
+ // GC thread moves many more objects than mutators.
+ // Therefore, we separate the two to avoid CAS.
+ Atomic<size_t> bytes_moved_; // Used by mutators
+ Atomic<size_t> objects_moved_; // Used by mutators
+ size_t bytes_moved_gc_thread_; // Used by GC
+ size_t objects_moved_gc_thread_; // Used by GC
Atomic<uint64_t> cumulative_bytes_moved_;
Atomic<uint64_t> cumulative_objects_moved_;
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index d7f33d5..d752805 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -154,8 +154,16 @@
return;
}
// Don't stub Proxy.<init>. Note that the Proxy class itself is not a proxy class.
- if (method->IsConstructor() &&
- method->GetDeclaringClass()->DescriptorEquals("Ljava/lang/reflect/Proxy;")) {
+ // TODO We should remove the need for this since it means we cannot always correctly detect calls
+ // to Proxy.<init>
+ // Annoyingly this can be called before we have actually initialized WellKnownClasses so therefore
+ // we also need to check this based on the declaring-class descriptor. The check is valid because
+ // Proxy only has a single constructor.
+ ArtMethod* well_known_proxy_init = jni::DecodeArtMethod(
+ WellKnownClasses::java_lang_reflect_Proxy_init);
+ if ((LIKELY(well_known_proxy_init != nullptr) && UNLIKELY(method == well_known_proxy_init)) ||
+ UNLIKELY(method->IsConstructor() &&
+ method->GetDeclaringClass()->DescriptorEquals("Ljava/lang/reflect/Proxy;"))) {
return;
}
const void* new_quick_code;
@@ -354,7 +362,7 @@
}
uint32_t dex_pc = visitor.dex_pcs_.back();
visitor.dex_pcs_.pop_back();
- if (!isi->interpreter_entry_) {
+ if (!isi->interpreter_entry_ && !isi->method_->IsRuntimeMethod()) {
instrumentation->MethodEnterEvent(thread, (*isi).this_object_, (*isi).method_, dex_pc);
}
}
@@ -785,7 +793,13 @@
if (class_linker->IsQuickResolutionStub(quick_code) ||
class_linker->IsQuickToInterpreterBridge(quick_code)) {
new_quick_code = quick_code;
- } else if (entry_exit_stubs_installed_) {
+ } else if (entry_exit_stubs_installed_ &&
+ // We need to make sure not to replace anything that InstallStubsForMethod
+ // wouldn't. Specifically we cannot stub out Proxy.<init> since subtypes copy the
+ // implementation directly and this will confuse the instrumentation trampolines.
+ // TODO We should remove the need for this since it makes it impossible to profile
+ // Proxy.<init> correctly in all cases.
+ method != jni::DecodeArtMethod(WellKnownClasses::java_lang_reflect_Proxy_init)) {
new_quick_code = GetQuickInstrumentationEntryPoint();
} else {
new_quick_code = quick_code;
@@ -912,7 +926,7 @@
}
// If there is no deoptimized method left, we can restore the stack of each thread.
- if (empty) {
+ if (empty && !entry_exit_stubs_installed_) {
MutexLock mu(self, *Locks::thread_list_lock_);
Runtime::Current()->GetThreadList()->ForEach(InstrumentationRestoreStack, this);
instrumentation_stubs_installed_ = false;
diff --git a/runtime/oat.h b/runtime/oat.h
index 40f4edd..22c6a39 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,8 +32,8 @@
class PACKED(4) OatHeader {
public:
static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
- // Last oat version changed reason: compiler support invoke-custom
- static constexpr uint8_t kOatVersion[] = { '1', '4', '8', '\0' };
+ // Last oat version changed reason: Add Kind column to stack maps.
+ static constexpr uint8_t kOatVersion[] = { '1', '4', '9', '\0' };
static constexpr const char* kImageLocationKey = "image-location";
static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
diff --git a/runtime/proxy_test.cc b/runtime/proxy_test.cc
index 946ea01..36dea60 100644
--- a/runtime/proxy_test.cc
+++ b/runtime/proxy_test.cc
@@ -23,11 +23,24 @@
#include "mirror/field-inl.h"
#include "proxy_test.h"
#include "scoped_thread_state_change-inl.h"
+#include "well_known_classes.h"
namespace art {
namespace proxy_test {
-class ProxyTest : public CommonRuntimeTest {};
+class ProxyTest : public CommonRuntimeTest {
+ protected:
+ void SetUp() OVERRIDE {
+ CommonRuntimeTest::SetUp();
+ // The creation of a Proxy class uses WellKnownClasses. These are not normally initialized by
+ // CommonRuntimeTest so we need to do that now.
+ WellKnownClasses::Clear();
+ WellKnownClasses::Init(art::Thread::Current()->GetJniEnv());
+ // Since we aren't actually calling any of the native functions we can just immediately call
+ // LateInit after calling Init.
+ WellKnownClasses::LateInit(art::Thread::Current()->GetJniEnv());
+ }
+};
// Creates a proxy class and check ClassHelper works correctly.
TEST_F(ProxyTest, ProxyClassHelper) {
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 2188cdc..a181bfe 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -849,7 +849,8 @@
uint8_t* return_pc_addr = reinterpret_cast<uint8_t*>(cur_quick_frame_) + return_pc_offset;
uintptr_t return_pc = *reinterpret_cast<uintptr_t*>(return_pc_addr);
- if (UNLIKELY(exit_stubs_installed)) {
+ if (UNLIKELY(exit_stubs_installed ||
+ reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc()) == return_pc)) {
// While profiling, the return pc is restored from the side stack, except when walking
// the stack for an exception where the side stack will be unwound in VisitFrame.
if (reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc()) == return_pc) {
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index aa19f09..8af73e9 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -119,16 +119,23 @@
* - Knowing the inlining information,
* - Knowing the values of dex registers.
*/
-class StackMap : public BitTable<7>::Accessor {
+class StackMap : public BitTable<8>::Accessor {
public:
+ enum Kind {
+ Default = -1,
+ Catch = 0,
+ OSR = 1,
+ Debug = 2,
+ };
BIT_TABLE_HEADER()
- BIT_TABLE_COLUMN(0, PackedNativePc)
- BIT_TABLE_COLUMN(1, DexPc)
- BIT_TABLE_COLUMN(2, RegisterMaskIndex)
- BIT_TABLE_COLUMN(3, StackMaskIndex)
- BIT_TABLE_COLUMN(4, InlineInfoIndex)
- BIT_TABLE_COLUMN(5, DexRegisterMaskIndex)
- BIT_TABLE_COLUMN(6, DexRegisterMapIndex)
+ BIT_TABLE_COLUMN(0, Kind)
+ BIT_TABLE_COLUMN(1, PackedNativePc)
+ BIT_TABLE_COLUMN(2, DexPc)
+ BIT_TABLE_COLUMN(3, RegisterMaskIndex)
+ BIT_TABLE_COLUMN(4, StackMaskIndex)
+ BIT_TABLE_COLUMN(5, InlineInfoIndex)
+ BIT_TABLE_COLUMN(6, DexRegisterMaskIndex)
+ BIT_TABLE_COLUMN(7, DexRegisterMapIndex)
ALWAYS_INLINE uint32_t GetNativePcOffset(InstructionSet instruction_set) const {
return UnpackNativePc(Get<kPackedNativePc>(), instruction_set);
@@ -377,19 +384,18 @@
StackMap GetStackMapForDexPc(uint32_t dex_pc) const {
for (size_t i = 0, e = GetNumberOfStackMaps(); i < e; ++i) {
StackMap stack_map = GetStackMapAt(i);
- if (stack_map.GetDexPc() == dex_pc) {
+ if (stack_map.GetDexPc() == dex_pc && stack_map.GetKind() != StackMap::Kind::Debug) {
return stack_map;
}
}
return StackMap();
}
- // Searches the stack map list backwards because catch stack maps are stored
- // at the end.
+ // Searches the stack map list backwards because catch stack maps are stored at the end.
StackMap GetCatchStackMapForDexPc(uint32_t dex_pc) const {
for (size_t i = GetNumberOfStackMaps(); i > 0; --i) {
StackMap stack_map = GetStackMapAt(i - 1);
- if (stack_map.GetDexPc() == dex_pc) {
+ if (stack_map.GetDexPc() == dex_pc && stack_map.GetKind() == StackMap::Kind::Catch) {
return stack_map;
}
}
@@ -397,41 +403,26 @@
}
StackMap GetOsrStackMapForDexPc(uint32_t dex_pc) const {
- size_t e = GetNumberOfStackMaps();
- if (e == 0) {
- // There cannot be OSR stack map if there is no stack map.
- return StackMap();
- }
- // Walk over all stack maps. If two consecutive stack maps are identical, then we
- // have found a stack map suitable for OSR.
- for (size_t i = 0; i < e - 1; ++i) {
+ for (size_t i = 0, e = GetNumberOfStackMaps(); i < e; ++i) {
StackMap stack_map = GetStackMapAt(i);
- if (stack_map.GetDexPc() == dex_pc) {
- StackMap other = GetStackMapAt(i + 1);
- if (other.GetDexPc() == dex_pc &&
- other.GetNativePcOffset(kRuntimeISA) ==
- stack_map.GetNativePcOffset(kRuntimeISA)) {
- if (i < e - 2) {
- // Make sure there are not three identical stack maps following each other.
- DCHECK_NE(
- stack_map.GetNativePcOffset(kRuntimeISA),
- GetStackMapAt(i + 2).GetNativePcOffset(kRuntimeISA));
- }
- return stack_map;
- }
+ if (stack_map.GetDexPc() == dex_pc && stack_map.GetKind() == StackMap::Kind::OSR) {
+ return stack_map;
}
}
return StackMap();
}
- StackMap GetStackMapForNativePcOffset(uint32_t native_pc_offset) const {
+ StackMap GetStackMapForNativePcOffset(uint32_t pc, InstructionSet isa = kRuntimeISA) const {
// TODO: Safepoint stack maps are sorted by native_pc_offset but catch stack
// maps are not. If we knew that the method does not have try/catch,
// we could do binary search.
for (size_t i = 0, e = GetNumberOfStackMaps(); i < e; ++i) {
StackMap stack_map = GetStackMapAt(i);
- if (stack_map.GetNativePcOffset(kRuntimeISA) == native_pc_offset) {
- return stack_map;
+ if (stack_map.GetNativePcOffset(isa) == pc) {
+ StackMap::Kind kind = static_cast<StackMap::Kind>(stack_map.GetKind());
+ if (kind == StackMap::Kind::Default || kind == StackMap::Kind::OSR) {
+ return stack_map;
+ }
}
}
return StackMap();
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index c64e7bb..206418f 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -26,6 +26,7 @@
#include "base/enums.h"
#include "class_linker.h"
#include "entrypoints/quick/quick_entrypoints_enum.h"
+#include "entrypoints/runtime_asm_entrypoints.h"
#include "hidden_api.h"
#include "jni/jni_internal.h"
#include "mirror/class.h"
@@ -98,6 +99,7 @@
jmethodID WellKnownClasses::java_lang_ref_FinalizerReference_add;
jmethodID WellKnownClasses::java_lang_ref_ReferenceQueue_add;
jmethodID WellKnownClasses::java_lang_reflect_Parameter_init;
+jmethodID WellKnownClasses::java_lang_reflect_Proxy_init;
jmethodID WellKnownClasses::java_lang_reflect_Proxy_invoke;
jmethodID WellKnownClasses::java_lang_Runtime_nativeLoad;
jmethodID WellKnownClasses::java_lang_Short_valueOf;
@@ -418,6 +420,14 @@
CacheMethod(env, java_lang_Runtime.get(), true, "nativeLoad",
"(Ljava/lang/String;Ljava/lang/ClassLoader;)"
"Ljava/lang/String;");
+ java_lang_reflect_Proxy_init =
+ CacheMethod(env, java_lang_reflect_Proxy, false, "<init>",
+ "(Ljava/lang/reflect/InvocationHandler;)V");
+ // This invariant is important since otherwise we will have the entire proxy invoke system
+ // confused.
+ DCHECK_NE(
+ jni::DecodeArtMethod(java_lang_reflect_Proxy_init)->GetEntryPointFromQuickCompiledCode(),
+ GetQuickInstrumentationEntryPoint());
java_lang_reflect_Proxy_invoke =
CacheMethod(env, java_lang_reflect_Proxy, true, "invoke",
"(Ljava/lang/reflect/Proxy;Ljava/lang/reflect/Method;"
@@ -484,6 +494,7 @@
java_lang_ref_FinalizerReference_add = nullptr;
java_lang_ref_ReferenceQueue_add = nullptr;
java_lang_reflect_Parameter_init = nullptr;
+ java_lang_reflect_Proxy_init = nullptr;
java_lang_reflect_Proxy_invoke = nullptr;
java_lang_Runtime_nativeLoad = nullptr;
java_lang_Short_valueOf = nullptr;
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index c81062f..ce5ab1d 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -108,6 +108,7 @@
static jmethodID java_lang_ref_FinalizerReference_add;
static jmethodID java_lang_ref_ReferenceQueue_add;
static jmethodID java_lang_reflect_Parameter_init;
+ static jmethodID java_lang_reflect_Proxy_init;
static jmethodID java_lang_reflect_Proxy_invoke;
static jmethodID java_lang_Runtime_nativeLoad;
static jmethodID java_lang_Short_valueOf;
diff --git a/test/004-ThreadStress/run b/test/004-ThreadStress/run
index 8004036..067e0d0 100755
--- a/test/004-ThreadStress/run
+++ b/test/004-ThreadStress/run
@@ -15,7 +15,29 @@
# limitations under the License.
# Enable lock contention logging.
-${RUN} --runtime-option -Xlockprofthreshold:10 "${@}"
+if [[ "x$ART_DEFAULT_GC_TYPE" = xGSS ]]; then
+ # NonMovingAlloc operations fail an assertion with the Generational
+ # Semi-Space (GSS) collector (see b/72738921); disable them for now
+ # by explicitly assigning frequencies to operations when the GSS
+ # collector is used.
+ #
+ # Note: The trick to use command substitution to have comments within
+ # a multi-line command is from https://stackoverflow.com/a/12797512.
+ ${RUN} --runtime-option -Xlockprofthreshold:10 "${@}" Main \
+ -oom:0.005 `# 1/200` \
+ -sigquit:0.095 `# 19/200` \
+ -alloc:0.225 `# 45/200` \
+ -largealloc:0.05 `# 10/200` \
+ -nonmovingalloc:0.0 `# 0/200` \
+ -stacktrace:0.1 `# 20/200` \
+ -exit:0.225 `# 45/200` \
+ -sleep:0.125 `# 25/200` \
+ -timedwait:0.05 `# 10/200` \
+ -wait:0.075 `# 15/200` \
+ -queuedwait:0.05 `# 10/200`
+else
+ ${RUN} --runtime-option -Xlockprofthreshold:10 "${@}"
+fi
return_status1=$?
# Run locks-only mode with stack-dump lock profiling. Reduce the number of total operations from
diff --git a/test/004-ThreadStress/src-art/Main.java b/test/004-ThreadStress/src-art/Main.java
index a142934..3a89f4f 100644
--- a/test/004-ThreadStress/src-art/Main.java
+++ b/test/004-ThreadStress/src-art/Main.java
@@ -315,11 +315,9 @@
Map<Operation, Double> frequencyMap = new HashMap<Operation, Double>();
frequencyMap.put(new OOM(), 0.005); // 1/200
frequencyMap.put(new SigQuit(), 0.095); // 19/200
- frequencyMap.put(new Alloc(), 0.225); // 45/200
+ frequencyMap.put(new Alloc(), 0.2); // 40/200
frequencyMap.put(new LargeAlloc(), 0.05); // 10/200
- // TODO: NonMovingAlloc operations fail an assertion with the
- // GSS collector (see b/72738921); disable them for now.
- frequencyMap.put(new NonMovingAlloc(), 0.0); // 0/200
+ frequencyMap.put(new NonMovingAlloc(), 0.025); // 5/200
frequencyMap.put(new StackTrace(), 0.1); // 20/200
frequencyMap.put(new Exit(), 0.225); // 45/200
frequencyMap.put(new Sleep(), 0.125); // 25/200
@@ -379,6 +377,8 @@
op = new Alloc();
} else if (split[0].equals("-largealloc")) {
op = new LargeAlloc();
+ } else if (split[0].equals("-nonmovingalloc")) {
+ op = new NonMovingAlloc();
} else if (split[0].equals("-stacktrace")) {
op = new StackTrace();
} else if (split[0].equals("-exit")) {
diff --git a/test/458-checker-instruct-simplification/src/Main.java b/test/458-checker-instruct-simplification/src/Main.java
index 40e3778..9e714f5 100644
--- a/test/458-checker-instruct-simplification/src/Main.java
+++ b/test/458-checker-instruct-simplification/src/Main.java
@@ -2458,6 +2458,77 @@
return (byte)((int)(((long)(b & 0xff)) & 255L));
}
+ /// CHECK-START: int Main.$noinline$emptyStringIndexOf(int) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Empty:l\d+>> LoadString
+ /// CHECK-DAG: <<Equals:i\d+>> InvokeVirtual [<<Empty>>,<<Arg>>] intrinsic:StringIndexOf
+ /// CHECK-DAG: Return [<<Equals>>]
+
+ /// CHECK-START: int Main.$noinline$emptyStringIndexOf(int) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ /// CHECK-START: int Main.$noinline$emptyStringIndexOf(int) instruction_simplifier (after)
+ /// CHECK-DAG: <<Minus1:i\d+>> IntConstant -1
+ /// CHECK-DAG: Return [<<Minus1>>]
+ public static int $noinline$emptyStringIndexOf(int ch) {
+ return "".indexOf(ch);
+ }
+
+ /// CHECK-START: int Main.$noinline$emptyStringIndexOfAfter(int, int) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg1:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Arg2:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Empty:l\d+>> LoadString
+ /// CHECK-DAG: <<Equals:i\d+>> InvokeVirtual [<<Empty>>,<<Arg1>>,<<Arg2>>] intrinsic:StringIndexOfAfter
+ /// CHECK-DAG: Return [<<Equals>>]
+
+ /// CHECK-START: int Main.$noinline$emptyStringIndexOfAfter(int, int) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ /// CHECK-START: int Main.$noinline$emptyStringIndexOfAfter(int, int) instruction_simplifier (after)
+ /// CHECK-DAG: <<Minus1:i\d+>> IntConstant -1
+ /// CHECK-DAG: Return [<<Minus1>>]
+ public static int $noinline$emptyStringIndexOfAfter(int ch, int fromIndex) {
+ return "".indexOf(ch, fromIndex);
+ }
+
+ /// CHECK-START: int Main.$noinline$singleCharStringIndexOf(int) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Empty:l\d+>> LoadString
+ /// CHECK-DAG: <<Equals:i\d+>> InvokeVirtual [<<Empty>>,<<Arg>>] intrinsic:StringIndexOf
+ /// CHECK-DAG: Return [<<Equals>>]
+
+ /// CHECK-START: int Main.$noinline$singleCharStringIndexOf(int) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ /// CHECK-START: int Main.$noinline$singleCharStringIndexOf(int) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: <<x:i\d+>> IntConstant 120
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Minus1:i\d+>> IntConstant -1
+ /// CHECK-DAG: <<Eq:z\d+>> Equal [<<Arg>>,<<x>>]
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Minus1>>,<<Zero>>,<<Eq>>]
+ /// CHECK-DAG: Return [<<Select>>]
+ public static int $noinline$singleCharStringIndexOf(int ch) {
+ return "x".indexOf(ch);
+ }
+
+ /// CHECK-START: int Main.$noinline$singleCharStringIndexOfAfter(int, int) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg1:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Arg2:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Empty:l\d+>> LoadString
+ /// CHECK-DAG: <<Equals:i\d+>> InvokeVirtual [<<Empty>>,<<Arg1>>,<<Arg2>>] intrinsic:StringIndexOfAfter
+ /// CHECK-DAG: Return [<<Equals>>]
+
+ /// CHECK-START: int Main.$noinline$singleCharStringIndexOfAfter(int, int) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg1:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Arg2:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Empty:l\d+>> LoadString
+ /// CHECK-DAG: <<Equals:i\d+>> InvokeVirtual [<<Empty>>,<<Arg1>>,<<Arg2>>] intrinsic:StringIndexOfAfter
+ /// CHECK-DAG: Return [<<Equals>>]
+ public static int $noinline$singleCharStringIndexOfAfter(int ch, int fromIndex) {
+ return "x".indexOf(ch, fromIndex); // Not simplified.
+ }
+
public static void main(String[] args) throws Exception {
Class smaliTests2 = Class.forName("SmaliTests2");
Method $noinline$XorAllOnes = smaliTests2.getMethod("$noinline$XorAllOnes", int.class);
@@ -2709,6 +2780,19 @@
assertIntEquals(1, (int)$noinline$bug68142795Boolean.invoke(null, true));
assertIntEquals(0x7f, $noinline$bug68142795Elaborate((byte) 0x7f));
assertIntEquals((byte) 0x80, $noinline$bug68142795Elaborate((byte) 0x80));
+
+ assertIntEquals(-1, $noinline$emptyStringIndexOf('a'));
+ assertIntEquals(-1, $noinline$emptyStringIndexOf('Z'));
+ assertIntEquals(-1, $noinline$emptyStringIndexOfAfter('a', 0));
+ assertIntEquals(-1, $noinline$emptyStringIndexOfAfter('Z', -1));
+
+ assertIntEquals(-1, $noinline$singleCharStringIndexOf('a'));
+ assertIntEquals(0, $noinline$singleCharStringIndexOf('x'));
+ assertIntEquals(-1, $noinline$singleCharStringIndexOf('Z'));
+ assertIntEquals(-1, $noinline$singleCharStringIndexOfAfter('a', 0));
+ assertIntEquals(0, $noinline$singleCharStringIndexOfAfter('x', -1));
+ assertIntEquals(-1, $noinline$singleCharStringIndexOfAfter('x', 1));
+ assertIntEquals(-1, $noinline$singleCharStringIndexOfAfter('Z', -1));
}
private static boolean $inline$true() { return true; }
diff --git a/test/566-checker-signum/build b/test/566-checker-signum/build
deleted file mode 100644
index 10ffcc5..0000000
--- a/test/566-checker-signum/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# See b/65168732
-export USE_D8=false
-
-./default-build "$@"
diff --git a/test/566-checker-signum/smali/Main2.smali b/test/566-checker-signum/smali/Main2.smali
new file mode 100644
index 0000000..d99ad86
--- /dev/null
+++ b/test/566-checker-signum/smali/Main2.smali
@@ -0,0 +1,83 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LMain2;
+.super Ljava/lang/Object;
+
+## CHECK-START: int Main2.signBoolean(boolean) intrinsics_recognition (after)
+## CHECK-DAG: <<Method:[ij]\d+>> CurrentMethod
+## CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+## CHECK-DAG: <<One:i\d+>> IntConstant 1
+## CHECK-DAG: <<Phi:i\d+>> Phi [<<One>>,<<Zero>>]
+## CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect [<<Phi>>,<<Method>>] intrinsic:IntegerSignum
+## CHECK-DAG: Return [<<Result>>]
+
+## CHECK-START: int Main2.signBoolean(boolean) instruction_simplifier (after)
+## CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+## CHECK-DAG: <<One:i\d+>> IntConstant 1
+## CHECK-DAG: <<Phi:i\d+>> Phi [<<One>>,<<Zero>>]
+## CHECK-DAG: <<Result:i\d+>> Compare [<<Phi>>,<<Zero>>]
+## CHECK-DAG: Return [<<Result>>]
+
+## CHECK-START: int Main2.signBoolean(boolean) instruction_simplifier (after)
+## CHECK-NOT: InvokeStaticOrDirect
+
+## CHECK-START: int Main2.signBoolean(boolean) select_generator (after)
+## CHECK-DAG: <<Arg:z\d+>> ParameterValue
+## CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+## CHECK-DAG: <<One:i\d+>> IntConstant 1
+## CHECK-DAG: <<Sel:i\d+>> Select [<<Zero>>,<<One>>,<<Arg>>]
+## CHECK-DAG: <<Result:i\d+>> Compare [<<Sel>>,<<Zero>>]
+## CHECK-DAG: Return [<<Result>>]
+
+## CHECK-START: int Main2.signBoolean(boolean) select_generator (after)
+## CHECK-NOT: Phi
+
+## CHECK-START: int Main2.signBoolean(boolean) instruction_simplifier$after_bce (after)
+## CHECK-DAG: <<Arg:z\d+>> ParameterValue
+## CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+## CHECK-DAG: <<Result:i\d+>> Compare [<<Arg>>,<<Zero>>]
+## CHECK-DAG: Return [<<Result>>]
+
+## CHECK-START: int Main2.signBoolean(boolean) instruction_simplifier$after_bce (after)
+## CHECK-NOT: Select
+
+# Original java source:
+#
+# private static int signBoolean(boolean x) {
+# return Integer.signum(x ? 1 : 0);
+# }
+
+.method public static signBoolean(Z)I
+ .registers 2
+ .param p0, "x" # Z
+
+ .prologue
+ .line 58
+ if-eqz p0, :cond_8
+
+ const/4 v0, 0x1
+
+ :goto_3
+ invoke-static {v0}, Ljava/lang/Integer;->signum(I)I
+
+ move-result v0
+
+ return v0
+
+ :cond_8
+ const/4 v0, 0x0
+
+ goto :goto_3
+.end method
diff --git a/test/566-checker-signum/src-art/Main.java b/test/566-checker-signum/src-art/Main.java
new file mode 100644
index 0000000..f1e1e1b
--- /dev/null
+++ b/test/566-checker-signum/src-art/Main.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Method;
+
+public class Main {
+
+ /// CHECK-START: int Main.signByte(byte) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerSignum
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.signByte(byte) instruction_simplifier (after)
+ /// CHECK-DAG: <<Result:i\d+>> Compare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.signByte(byte) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ private static int signByte(byte x) {
+ return Integer.signum(x);
+ }
+
+ /// CHECK-START: int Main.signShort(short) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerSignum
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.signShort(short) instruction_simplifier (after)
+ /// CHECK-DAG: <<Result:i\d+>> Compare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.signShort(short) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ private static int signShort(short x) {
+ return Integer.signum(x);
+ }
+
+ /// CHECK-START: int Main.signChar(char) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerSignum
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.signChar(char) instruction_simplifier (after)
+ /// CHECK-DAG: <<Result:i\d+>> Compare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.signChar(char) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ private static int signChar(char x) {
+ return Integer.signum(x);
+ }
+
+ /// CHECK-START: int Main.signInt(int) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerSignum
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.signInt(int) instruction_simplifier (after)
+ /// CHECK-DAG: <<Result:i\d+>> Compare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.signInt(int) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ private static int signInt(int x) {
+ return Integer.signum(x);
+ }
+
+ /// CHECK-START: int Main.signLong(long) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:LongSignum
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.signLong(long) instruction_simplifier (after)
+ /// CHECK-DAG: <<Result:i\d+>> Compare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.signLong(long) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ private static int signLong(long x) {
+ return Long.signum(x);
+ }
+
+
+ public static void testSignBoolean() throws Exception {
+ Method signBoolean = Class.forName("Main2").getMethod("signBoolean", boolean.class);
+ expectEquals(0, (int)signBoolean.invoke(null, false));
+ expectEquals(1, (int)signBoolean.invoke(null, true));
+ }
+
+ public static void testSignByte() {
+ expectEquals(-1, signByte((byte)Byte.MIN_VALUE));
+ expectEquals(-1, signByte((byte)-64));
+ expectEquals(-1, signByte((byte)-1));
+ expectEquals(0, signByte((byte)0));
+ expectEquals(1, signByte((byte)1));
+ expectEquals(1, signByte((byte)64));
+ expectEquals(1, signByte((byte)Byte.MAX_VALUE));
+ }
+
+ public static void testSignShort() {
+ expectEquals(-1, signShort((short)Short.MIN_VALUE));
+ expectEquals(-1, signShort((short)-12345));
+ expectEquals(-1, signShort((short)-1));
+ expectEquals(0, signShort((short)0));
+ expectEquals(1, signShort((short)1));
+ expectEquals(1, signShort((short)12345));
+ expectEquals(1, signShort((short)Short.MAX_VALUE));
+ }
+
+ public static void testSignChar() {
+ expectEquals(0, signChar((char)0));
+ expectEquals(1, signChar((char)1));
+ expectEquals(1, signChar((char)12345));
+ expectEquals(1, signChar((char)Character.MAX_VALUE));
+ }
+
+ public static void testSignInt() {
+ expectEquals(-1, signInt(Integer.MIN_VALUE));
+ expectEquals(-1, signInt(-12345));
+ expectEquals(-1, signInt(-1));
+ expectEquals(0, signInt(0));
+ expectEquals(1, signInt(1));
+ expectEquals(1, signInt(12345));
+ expectEquals(1, signInt(Integer.MAX_VALUE));
+
+ for (int i = -11; i <= 11; i++) {
+ int expected = 0;
+ if (i < 0) expected = -1;
+ else if (i > 0) expected = 1;
+ expectEquals(expected, signInt(i));
+ }
+ }
+
+ public static void testSignLong() {
+ expectEquals(-1, signLong(Long.MIN_VALUE));
+ expectEquals(-1, signLong(-12345L));
+ expectEquals(-1, signLong(-1L));
+ expectEquals(0, signLong(0L));
+ expectEquals(1, signLong(1L));
+ expectEquals(1, signLong(12345L));
+ expectEquals(1, signLong(Long.MAX_VALUE));
+
+ expectEquals(-1, signLong(0x800000007FFFFFFFL));
+ expectEquals(-1, signLong(0x80000000FFFFFFFFL));
+ expectEquals(1, signLong(0x000000007FFFFFFFL));
+ expectEquals(1, signLong(0x00000000FFFFFFFFL));
+ expectEquals(1, signLong(0x7FFFFFFF7FFFFFFFL));
+ expectEquals(1, signLong(0x7FFFFFFFFFFFFFFFL));
+
+ for (long i = -11L; i <= 11L; i++) {
+ int expected = 0;
+ if (i < 0) expected = -1;
+ else if (i > 0) expected = 1;
+ expectEquals(expected, signLong(i));
+ }
+
+ for (long i = Long.MIN_VALUE; i <= Long.MIN_VALUE + 11L; i++) {
+ expectEquals(-1, signLong(i));
+ }
+
+ for (long i = Long.MAX_VALUE; i >= Long.MAX_VALUE - 11L; i--) {
+ expectEquals(1, signLong(i));
+ }
+ }
+
+
+ public static void main(String args[]) throws Exception {
+ testSignBoolean();
+ testSignByte();
+ testSignShort();
+ testSignChar();
+ testSignInt();
+ testSignLong();
+
+ System.out.println("passed");
+ }
+
+ private static void expectEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+}
diff --git a/test/566-checker-signum/src/Main.java b/test/566-checker-signum/src/Main.java
index 7fc9e84..fa8e5cd 100644
--- a/test/566-checker-signum/src/Main.java
+++ b/test/566-checker-signum/src/Main.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,222 +14,9 @@
* limitations under the License.
*/
+// This file is just for running on the RI as the test is ART specific.
public class Main {
-
- /// CHECK-START: int Main.signBoolean(boolean) intrinsics_recognition (after)
- /// CHECK-DAG: <<Method:[ij]\d+>> CurrentMethod
- /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
- /// CHECK-DAG: <<One:i\d+>> IntConstant 1
- /// CHECK-DAG: <<Phi:i\d+>> Phi [<<One>>,<<Zero>>]
- /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect [<<Phi>>,<<Method>>] intrinsic:IntegerSignum
- /// CHECK-DAG: Return [<<Result>>]
-
- /// CHECK-START: int Main.signBoolean(boolean) instruction_simplifier (after)
- /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
- /// CHECK-DAG: <<One:i\d+>> IntConstant 1
- /// CHECK-DAG: <<Phi:i\d+>> Phi [<<One>>,<<Zero>>]
- /// CHECK-DAG: <<Result:i\d+>> Compare [<<Phi>>,<<Zero>>]
- /// CHECK-DAG: Return [<<Result>>]
-
- /// CHECK-START: int Main.signBoolean(boolean) instruction_simplifier (after)
- /// CHECK-NOT: InvokeStaticOrDirect
-
- /// CHECK-START: int Main.signBoolean(boolean) select_generator (after)
- /// CHECK-DAG: <<Arg:z\d+>> ParameterValue
- /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
- /// CHECK-DAG: <<One:i\d+>> IntConstant 1
- /// CHECK-DAG: <<Sel:i\d+>> Select [<<Zero>>,<<One>>,<<Arg>>]
- /// CHECK-DAG: <<Result:i\d+>> Compare [<<Sel>>,<<Zero>>]
- /// CHECK-DAG: Return [<<Result>>]
-
- /// CHECK-START: int Main.signBoolean(boolean) select_generator (after)
- /// CHECK-NOT: Phi
-
- /// CHECK-START: int Main.signBoolean(boolean) instruction_simplifier$after_bce (after)
- /// CHECK-DAG: <<Arg:z\d+>> ParameterValue
- /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
- /// CHECK-DAG: <<Result:i\d+>> Compare [<<Arg>>,<<Zero>>]
- /// CHECK-DAG: Return [<<Result>>]
-
- /// CHECK-START: int Main.signBoolean(boolean) instruction_simplifier$after_bce (after)
- /// CHECK-NOT: Select
-
- private static int signBoolean(boolean x) {
- return Integer.signum(x ? 1 : 0);
- }
-
- /// CHECK-START: int Main.signByte(byte) intrinsics_recognition (after)
- /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerSignum
- /// CHECK-DAG: Return [<<Result>>]
-
- /// CHECK-START: int Main.signByte(byte) instruction_simplifier (after)
- /// CHECK-DAG: <<Result:i\d+>> Compare
- /// CHECK-DAG: Return [<<Result>>]
-
- /// CHECK-START: int Main.signByte(byte) instruction_simplifier (after)
- /// CHECK-NOT: InvokeStaticOrDirect
-
- private static int signByte(byte x) {
- return Integer.signum(x);
- }
-
- /// CHECK-START: int Main.signShort(short) intrinsics_recognition (after)
- /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerSignum
- /// CHECK-DAG: Return [<<Result>>]
-
- /// CHECK-START: int Main.signShort(short) instruction_simplifier (after)
- /// CHECK-DAG: <<Result:i\d+>> Compare
- /// CHECK-DAG: Return [<<Result>>]
-
- /// CHECK-START: int Main.signShort(short) instruction_simplifier (after)
- /// CHECK-NOT: InvokeStaticOrDirect
-
- private static int signShort(short x) {
- return Integer.signum(x);
- }
-
- /// CHECK-START: int Main.signChar(char) intrinsics_recognition (after)
- /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerSignum
- /// CHECK-DAG: Return [<<Result>>]
-
- /// CHECK-START: int Main.signChar(char) instruction_simplifier (after)
- /// CHECK-DAG: <<Result:i\d+>> Compare
- /// CHECK-DAG: Return [<<Result>>]
-
- /// CHECK-START: int Main.signChar(char) instruction_simplifier (after)
- /// CHECK-NOT: InvokeStaticOrDirect
-
- private static int signChar(char x) {
- return Integer.signum(x);
- }
-
- /// CHECK-START: int Main.signInt(int) intrinsics_recognition (after)
- /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerSignum
- /// CHECK-DAG: Return [<<Result>>]
-
- /// CHECK-START: int Main.signInt(int) instruction_simplifier (after)
- /// CHECK-DAG: <<Result:i\d+>> Compare
- /// CHECK-DAG: Return [<<Result>>]
-
- /// CHECK-START: int Main.signInt(int) instruction_simplifier (after)
- /// CHECK-NOT: InvokeStaticOrDirect
-
- private static int signInt(int x) {
- return Integer.signum(x);
- }
-
- /// CHECK-START: int Main.signLong(long) intrinsics_recognition (after)
- /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:LongSignum
- /// CHECK-DAG: Return [<<Result>>]
-
- /// CHECK-START: int Main.signLong(long) instruction_simplifier (after)
- /// CHECK-DAG: <<Result:i\d+>> Compare
- /// CHECK-DAG: Return [<<Result>>]
-
- /// CHECK-START: int Main.signLong(long) instruction_simplifier (after)
- /// CHECK-NOT: InvokeStaticOrDirect
-
- private static int signLong(long x) {
- return Long.signum(x);
- }
-
-
- public static void testSignBoolean() {
- expectEquals(0, signBoolean(false));
- expectEquals(1, signBoolean(true));
- }
-
- public static void testSignByte() {
- expectEquals(-1, signByte((byte)Byte.MIN_VALUE));
- expectEquals(-1, signByte((byte)-64));
- expectEquals(-1, signByte((byte)-1));
- expectEquals(0, signByte((byte)0));
- expectEquals(1, signByte((byte)1));
- expectEquals(1, signByte((byte)64));
- expectEquals(1, signByte((byte)Byte.MAX_VALUE));
- }
-
- public static void testSignShort() {
- expectEquals(-1, signShort((short)Short.MIN_VALUE));
- expectEquals(-1, signShort((short)-12345));
- expectEquals(-1, signShort((short)-1));
- expectEquals(0, signShort((short)0));
- expectEquals(1, signShort((short)1));
- expectEquals(1, signShort((short)12345));
- expectEquals(1, signShort((short)Short.MAX_VALUE));
- }
-
- public static void testSignChar() {
- expectEquals(0, signChar((char)0));
- expectEquals(1, signChar((char)1));
- expectEquals(1, signChar((char)12345));
- expectEquals(1, signChar((char)Character.MAX_VALUE));
- }
-
- public static void testSignInt() {
- expectEquals(-1, signInt(Integer.MIN_VALUE));
- expectEquals(-1, signInt(-12345));
- expectEquals(-1, signInt(-1));
- expectEquals(0, signInt(0));
- expectEquals(1, signInt(1));
- expectEquals(1, signInt(12345));
- expectEquals(1, signInt(Integer.MAX_VALUE));
-
- for (int i = -11; i <= 11; i++) {
- int expected = 0;
- if (i < 0) expected = -1;
- else if (i > 0) expected = 1;
- expectEquals(expected, signInt(i));
- }
- }
-
- public static void testSignLong() {
- expectEquals(-1, signLong(Long.MIN_VALUE));
- expectEquals(-1, signLong(-12345L));
- expectEquals(-1, signLong(-1L));
- expectEquals(0, signLong(0L));
- expectEquals(1, signLong(1L));
- expectEquals(1, signLong(12345L));
- expectEquals(1, signLong(Long.MAX_VALUE));
-
- expectEquals(-1, signLong(0x800000007FFFFFFFL));
- expectEquals(-1, signLong(0x80000000FFFFFFFFL));
- expectEquals(1, signLong(0x000000007FFFFFFFL));
- expectEquals(1, signLong(0x00000000FFFFFFFFL));
- expectEquals(1, signLong(0x7FFFFFFF7FFFFFFFL));
- expectEquals(1, signLong(0x7FFFFFFFFFFFFFFFL));
-
- for (long i = -11L; i <= 11L; i++) {
- int expected = 0;
- if (i < 0) expected = -1;
- else if (i > 0) expected = 1;
- expectEquals(expected, signLong(i));
- }
-
- for (long i = Long.MIN_VALUE; i <= Long.MIN_VALUE + 11L; i++) {
- expectEquals(-1, signLong(i));
- }
-
- for (long i = Long.MAX_VALUE; i >= Long.MAX_VALUE - 11L; i--) {
- expectEquals(1, signLong(i));
- }
- }
-
-
- public static void main(String args[]) {
- testSignBoolean();
- testSignByte();
- testSignShort();
- testSignChar();
- testSignInt();
- testSignLong();
-
+ public static void main(String[] args) {
System.out.println("passed");
}
-
- private static void expectEquals(int expected, int result) {
- if (expected != result) {
- throw new Error("Expected: " + expected + ", found: " + result);
- }
- }
}
diff --git a/test/570-checker-osr/build b/test/570-checker-osr/build
deleted file mode 100644
index 10ffcc5..0000000
--- a/test/570-checker-osr/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# See b/65168732
-export USE_D8=false
-
-./default-build "$@"
diff --git a/test/618-checker-induction/build b/test/618-checker-induction/build
deleted file mode 100644
index 10ffcc5..0000000
--- a/test/618-checker-induction/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# See b/65168732
-export USE_D8=false
-
-./default-build "$@"
diff --git a/test/618-checker-induction/src/Main.java b/test/618-checker-induction/src/Main.java
index 0080ffa..1460725 100644
--- a/test/618-checker-induction/src/Main.java
+++ b/test/618-checker-induction/src/Main.java
@@ -290,7 +290,7 @@
/// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop1>> outer_loop:none
/// CHECK-DAG: <<Phi3:i\d+>> Phi loop:<<Loop2:B\d+>> outer_loop:<<Loop1>>
/// CHECK-DAG: <<Phi4:i\d+>> Phi loop:<<Loop2>> outer_loop:<<Loop1>>
- /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ /// CHECK-DAG: Return [<<Phi2>>] loop:none
//
/// CHECK-START: int Main.closedFormNested() loop_optimization (after)
/// CHECK-NOT: Phi
@@ -313,7 +313,7 @@
/// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop1>> outer_loop:none
/// CHECK-DAG: <<Phi3:i\d+>> Phi loop:<<Loop2:B\d+>> outer_loop:<<Loop1>>
/// CHECK-DAG: <<Phi4:i\d+>> Phi loop:<<Loop2>> outer_loop:<<Loop1>>
- /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ /// CHECK-DAG: Return [<<Phi2>>] loop:none
//
/// CHECK-START: int Main.closedFormNestedAlt() loop_optimization (after)
/// CHECK-NOT: Phi
@@ -411,7 +411,7 @@
/// CHECK-START: int Main.periodicReturned9() loop_optimization (before)
/// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi2>>] loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
//
/// CHECK-START: int Main.periodicReturned9() loop_optimization (after)
/// CHECK-NOT: Phi
@@ -430,7 +430,7 @@
/// CHECK-START: int Main.periodicReturned10() loop_optimization (before)
/// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi2>>] loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
//
/// CHECK-START: int Main.periodicReturned10() loop_optimization (after)
/// CHECK-NOT: Phi
@@ -450,7 +450,7 @@
/// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<Phi3:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi3>>] loop:none
+ /// CHECK-DAG: Return [<<Phi2>>] loop:none
//
/// CHECK-START: int Main.getSum21() loop_optimization (after)
/// CHECK-NOT: Phi
@@ -505,7 +505,7 @@
/// CHECK-START: int Main.periodicReturnedN(int) loop_optimization (before)
/// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi2>>] loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
//
/// CHECK-START: int Main.periodicReturnedN(int) loop_optimization (after)
/// CHECK-NOT: Phi
@@ -547,7 +547,7 @@
/// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop1>> outer_loop:none
/// CHECK-DAG: <<Phi3:i\d+>> Phi loop:<<Loop2:B\d+>> outer_loop:none
/// CHECK-DAG: <<Phi4:i\d+>> Phi loop:<<Loop2>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi3>>] loop:none
+ /// CHECK-DAG: Return [<<Phi4>>] loop:none
/// CHECK-EVAL: "<<Loop1>>" != "<<Loop2>>"
//
/// CHECK-START: int Main.closedFeed() loop_optimization (after)
@@ -691,7 +691,7 @@
/// CHECK-START: boolean Main.periodicBoolIdiom1N(boolean, int) loop_optimization (before)
/// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi2>>] loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
//
/// CHECK-START: boolean Main.periodicBoolIdiom1N(boolean, int) loop_optimization (after)
/// CHECK-NOT: Phi
@@ -705,7 +705,7 @@
/// CHECK-START: boolean Main.periodicBoolIdiom2N(boolean, int) loop_optimization (before)
/// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi2>>] loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
//
/// CHECK-START: boolean Main.periodicBoolIdiom2N(boolean, int) loop_optimization (after)
/// CHECK-NOT: Phi
@@ -719,7 +719,7 @@
/// CHECK-START: boolean Main.periodicBoolIdiom3N(boolean, int) loop_optimization (before)
/// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi2>>] loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
//
/// CHECK-START: boolean Main.periodicBoolIdiom3N(boolean, int) loop_optimization (after)
/// CHECK-NOT: Phi
diff --git a/test/626-checker-arm64-scratch-register/build b/test/626-checker-arm64-scratch-register/build
deleted file mode 100644
index d85147f..0000000
--- a/test/626-checker-arm64-scratch-register/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# See b/65168732
-export USE_D8=false
-
-./default-build "$@"
diff --git a/test/626-checker-arm64-scratch-register/smali/Main2.smali b/test/626-checker-arm64-scratch-register/smali/Main2.smali
new file mode 100644
index 0000000..914ae6e
--- /dev/null
+++ b/test/626-checker-arm64-scratch-register/smali/Main2.smali
@@ -0,0 +1,1768 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LMain2;
+.super Ljava/lang/Object;
+
+
+# instance fields
+.field b00:Z
+
+.field b01:Z
+
+.field b02:Z
+
+.field b03:Z
+
+.field b04:Z
+
+.field b05:Z
+
+.field b06:Z
+
+.field b07:Z
+
+.field b08:Z
+
+.field b09:Z
+
+.field b10:Z
+
+.field b11:Z
+
+.field b12:Z
+
+.field b13:Z
+
+.field b14:Z
+
+.field b15:Z
+
+.field b16:Z
+
+.field b17:Z
+
+.field b18:Z
+
+.field b19:Z
+
+.field b20:Z
+
+.field b21:Z
+
+.field b22:Z
+
+.field b23:Z
+
+.field b24:Z
+
+.field b25:Z
+
+.field b26:Z
+
+.field b27:Z
+
+.field b28:Z
+
+.field b29:Z
+
+.field b30:Z
+
+.field b31:Z
+
+.field b32:Z
+
+.field b33:Z
+
+.field b34:Z
+
+.field b35:Z
+
+.field b36:Z
+
+.field conditionA:Z
+
+.field conditionB:Z
+
+.field conditionC:Z
+
+
+# direct methods
+.method public constructor <init>()V
+ .registers 1
+
+ .prologue
+ .line 17
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+
+ return-void
+.end method
+
+## CHECK-START-ARM64: void Main2.test() register (after)
+## CHECK: begin_block
+## CHECK: name "B0"
+## CHECK: <<This:l\d+>> ParameterValue
+## CHECK: end_block
+## CHECK: begin_block
+## CHECK: successors "<<ThenBlock:B\d+>>" "<<ElseBlock:B\d+>>"
+## CHECK: <<CondB:z\d+>> InstanceFieldGet [<<This>>] field_name:Main2.conditionB
+## CHECK: If [<<CondB>>]
+## CHECK: end_block
+## CHECK: begin_block
+## CHECK: name "<<ElseBlock>>"
+## CHECK: ParallelMove moves:[40(sp)->d0,24(sp)->32(sp),28(sp)->36(sp),d0->d3,d3->d4,d2->d5,d4->d6,d5->d7,d6->d18,d7->d19,d18->d20,d19->d21,d20->d22,d21->d23,d22->d10,d23->d11,16(sp)->24(sp),20(sp)->28(sp),d10->d14,d11->d12,d12->d13,d13->d1,d14->d2,32(sp)->16(sp),36(sp)->20(sp)]
+## CHECK: end_block
+
+## CHECK-START-ARM64: void Main2.test() disassembly (after)
+## CHECK: begin_block
+## CHECK: name "B0"
+## CHECK: <<This:l\d+>> ParameterValue
+## CHECK: end_block
+## CHECK: begin_block
+## CHECK: successors "<<ThenBlock:B\d+>>" "<<ElseBlock:B\d+>>"
+## CHECK: <<CondB:z\d+>> InstanceFieldGet [<<This>>] field_name:Main2.conditionB
+## CHECK: If [<<CondB>>]
+## CHECK: end_block
+## CHECK: begin_block
+## CHECK: name "<<ElseBlock>>"
+## CHECK: ParallelMove moves:[invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid]
+## CHECK: fmov d31, d2
+## CHECK: ldr s2, [sp, #36]
+## CHECK: ldr w16, [sp, #16]
+## CHECK: str w16, [sp, #36]
+## CHECK: str s14, [sp, #16]
+## CHECK: ldr s14, [sp, #28]
+## CHECK: str s1, [sp, #28]
+## CHECK: ldr s1, [sp, #32]
+## CHECK: str s31, [sp, #32]
+## CHECK: ldr s31, [sp, #20]
+## CHECK: str s31, [sp, #40]
+## CHECK: str s12, [sp, #20]
+## CHECK: fmov d12, d11
+## CHECK: fmov d11, d10
+## CHECK: fmov d10, d23
+## CHECK: fmov d23, d22
+## CHECK: fmov d22, d21
+## CHECK: fmov d21, d20
+## CHECK: fmov d20, d19
+## CHECK: fmov d19, d18
+## CHECK: fmov d18, d7
+## CHECK: fmov d7, d6
+## CHECK: fmov d6, d5
+## CHECK: fmov d5, d4
+## CHECK: fmov d4, d3
+## CHECK: fmov d3, d13
+## CHECK: ldr s13, [sp, #24]
+## CHECK: str s3, [sp, #24]
+## CHECK: ldr s3, pc+{{\d+}} (addr {{0x[0-9a-f]+}}) (100)
+## CHECK: end_block
+
+# Original java source:
+#
+# public void test() {
+# String r = "";
+#
+# // For the purpose of this regression test, the order of
+# // definition of these float variable matters. Likewise with the
+# // order of the instructions where these variables are used below.
+# // Reordering these lines make make the original (b/32545705)
+# // issue vanish.
+# float f17 = b17 ? 0.0f : 1.0f;
+# float f16 = b16 ? 0.0f : 1.0f;
+# float f18 = b18 ? 0.0f : 1.0f;
+# float f19 = b19 ? 0.0f : 1.0f;
+# float f20 = b20 ? 0.0f : 1.0f;
+# float f21 = b21 ? 0.0f : 1.0f;
+# float f15 = b15 ? 0.0f : 1.0f;
+# float f00 = b00 ? 0.0f : 1.0f;
+# float f22 = b22 ? 0.0f : 1.0f;
+# float f23 = b23 ? 0.0f : 1.0f;
+# float f24 = b24 ? 0.0f : 1.0f;
+# float f25 = b25 ? 0.0f : 1.0f;
+# float f26 = b26 ? 0.0f : 1.0f;
+# float f27 = b27 ? 0.0f : 1.0f;
+# float f29 = b29 ? 0.0f : 1.0f;
+# float f28 = b28 ? 0.0f : 1.0f;
+# float f01 = b01 ? 0.0f : 1.0f;
+# float f02 = b02 ? 0.0f : 1.0f;
+# float f03 = b03 ? 0.0f : 1.0f;
+# float f04 = b04 ? 0.0f : 1.0f;
+# float f05 = b05 ? 0.0f : 1.0f;
+# float f07 = b07 ? 0.0f : 1.0f;
+# float f06 = b06 ? 0.0f : 1.0f;
+# float f30 = b30 ? 0.0f : 1.0f;
+# float f31 = b31 ? 0.0f : 1.0f;
+# float f32 = b32 ? 0.0f : 1.0f;
+# float f33 = b33 ? 0.0f : 1.0f;
+# float f34 = b34 ? 0.0f : 1.0f;
+# float f36 = b36 ? 0.0f : 1.0f;
+# float f35 = b35 ? 0.0f : 1.0f;
+# float f08 = b08 ? 0.0f : 1.0f;
+# float f09 = b09 ? 0.0f : 1.0f;
+# float f10 = b10 ? 0.0f : 1.0f;
+# float f11 = b11 ? 0.0f : 1.0f;
+# float f12 = b12 ? 0.0f : 1.0f;
+# float f14 = b14 ? 0.0f : 1.0f;
+# float f13 = b13 ? 0.0f : 1.0f;
+#
+# if (conditionA) {
+# f16 /= 1000.0f;
+# f17 /= 1000.0f;
+# f18 /= 1000.0f;
+# f19 /= 1000.0f;
+# f20 /= 1000.0f;
+# f21 /= 1000.0f;
+# f15 /= 1000.0f;
+# f08 /= 1000.0f;
+# f09 /= 1000.0f;
+# f10 /= 1000.0f;
+# f11 /= 1000.0f;
+# f12 /= 1000.0f;
+# f30 /= 1000.0f;
+# f31 /= 1000.0f;
+# f32 /= 1000.0f;
+# f33 /= 1000.0f;
+# f34 /= 1000.0f;
+# f01 /= 1000.0f;
+# f02 /= 1000.0f;
+# f03 /= 1000.0f;
+# f04 /= 1000.0f;
+# f05 /= 1000.0f;
+# f23 /= 1000.0f;
+# f24 /= 1000.0f;
+# f25 /= 1000.0f;
+# f26 /= 1000.0f;
+# f27 /= 1000.0f;
+# f22 /= 1000.0f;
+# f00 /= 1000.0f;
+# f14 /= 1000.0f;
+# f13 /= 1000.0f;
+# f36 /= 1000.0f;
+# f35 /= 1000.0f;
+# f07 /= 1000.0f;
+# f06 /= 1000.0f;
+# f29 /= 1000.0f;
+# f28 /= 1000.0f;
+# }
+# // The parallel move that used to exhaust the ARM64 parallel move
+# // resolver's scratch register pool (provided by VIXL) was in the
+# // "else" branch of the following condition generated by ART's
+# // compiler.
+# if (conditionB) {
+# f16 /= 100.0f;
+# f17 /= 100.0f;
+# f18 /= 100.0f;
+# f19 /= 100.0f;
+# f20 /= 100.0f;
+# f21 /= 100.0f;
+# f15 /= 100.0f;
+# f08 /= 100.0f;
+# f09 /= 100.0f;
+# f10 /= 100.0f;
+# f11 /= 100.0f;
+# f12 /= 100.0f;
+# f30 /= 100.0f;
+# f31 /= 100.0f;
+# f32 /= 100.0f;
+# f33 /= 100.0f;
+# f34 /= 100.0f;
+# f01 /= 100.0f;
+# f02 /= 100.0f;
+# f03 /= 100.0f;
+# f04 /= 100.0f;
+# f05 /= 100.0f;
+# f23 /= 100.0f;
+# f24 /= 100.0f;
+# f25 /= 100.0f;
+# f26 /= 100.0f;
+# f27 /= 100.0f;
+# f22 /= 100.0f;
+# f00 /= 100.0f;
+# f14 /= 100.0f;
+# f13 /= 100.0f;
+# f36 /= 100.0f;
+# f35 /= 100.0f;
+# f07 /= 100.0f;
+# f06 /= 100.0f;
+# f29 /= 100.0f;
+# f28 /= 100.0f;
+# }
+# if (conditionC) {
+# f16 /= 12.0f;
+# f17 /= 12.0f;
+# f18 /= 12.0f;
+# f19 /= 12.0f;
+# f20 /= 12.0f;
+# f21 /= 12.0f;
+# f15 /= 12.0f;
+# f08 /= 12.0f;
+# f09 /= 12.0f;
+# f10 /= 12.0f;
+# f11 /= 12.0f;
+# f12 /= 12.0f;
+# f30 /= 12.0f;
+# f31 /= 12.0f;
+# f32 /= 12.0f;
+# f33 /= 12.0f;
+# f34 /= 12.0f;
+# f01 /= 12.0f;
+# f02 /= 12.0f;
+# f03 /= 12.0f;
+# f04 /= 12.0f;
+# f05 /= 12.0f;
+# f23 /= 12.0f;
+# f24 /= 12.0f;
+# f25 /= 12.0f;
+# f26 /= 12.0f;
+# f27 /= 12.0f;
+# f22 /= 12.0f;
+# f00 /= 12.0f;
+# f14 /= 12.0f;
+# f13 /= 12.0f;
+# f36 /= 12.0f;
+# f35 /= 12.0f;
+# f07 /= 12.0f;
+# f06 /= 12.0f;
+# f29 /= 12.0f;
+# f28 /= 12.0f;
+# }
+# float s = 0.0f;
+# s = ((float) Math.round(100.0f * s)) / 100.0f;
+# String res = s + r;
+# }
+
+# virtual methods
+.method public test()V
+ .registers 45
+
+ .prologue
+ .line 121
+ const-string v39, ""
+
+ .line 128
+ .local v39, "r":Ljava/lang/String;
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->b17:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_367
+
+ const/16 v19, 0x0
+
+ .line 129
+ .local v19, "f17":F
+ :goto_c
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->b16:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_36b
+
+ const/16 v18, 0x0
+
+ .line 130
+ .local v18, "f16":F
+ :goto_16
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->b18:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_36f
+
+ const/16 v20, 0x0
+
+ .line 131
+ .local v20, "f18":F
+ :goto_20
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->b19:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_373
+
+ const/16 v21, 0x0
+
+ .line 132
+ .local v21, "f19":F
+ :goto_2a
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->b20:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_377
+
+ const/16 v22, 0x0
+
+ .line 133
+ .local v22, "f20":F
+ :goto_34
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->b21:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_37b
+
+ const/16 v23, 0x0
+
+ .line 134
+ .local v23, "f21":F
+ :goto_3e
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->b15:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_37f
+
+ const/16 v17, 0x0
+
+ .line 135
+ .local v17, "f15":F
+ :goto_48
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->b00:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_383
+
+ const/4 v2, 0x0
+
+ .line 136
+ .local v2, "f00":F
+ :goto_51
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->b22:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_387
+
+ const/16 v24, 0x0
+
+ .line 137
+ .local v24, "f22":F
+ :goto_5b
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->b23:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_38b
+
+ const/16 v25, 0x0
+
+ .line 138
+ .local v25, "f23":F
+ :goto_65
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->b24:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_38f
+
+ const/16 v26, 0x0
+
+ .line 139
+ .local v26, "f24":F
+ :goto_6f
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->b25:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_393
+
+ const/16 v27, 0x0
+
+ .line 140
+ .local v27, "f25":F
+ :goto_79
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->b26:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_397
+
+ const/16 v28, 0x0
+
+ .line 141
+ .local v28, "f26":F
+ :goto_83
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->b27:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_39b
+
+ const/16 v29, 0x0
+
+ .line 142
+ .local v29, "f27":F
+ :goto_8d
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->b29:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_39f
+
+ const/16 v31, 0x0
+
+ .line 143
+ .local v31, "f29":F
+ :goto_97
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->b28:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_3a3
+
+ const/16 v30, 0x0
+
+ .line 144
+ .local v30, "f28":F
+ :goto_a1
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->b01:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_3a7
+
+ const/4 v3, 0x0
+
+ .line 145
+ .local v3, "f01":F
+ :goto_aa
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->b02:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_3ab
+
+ const/4 v4, 0x0
+
+ .line 146
+ .local v4, "f02":F
+ :goto_b3
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->b03:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_3af
+
+ const/4 v5, 0x0
+
+ .line 147
+ .local v5, "f03":F
+ :goto_bc
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->b04:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_3b3
+
+ const/4 v6, 0x0
+
+ .line 148
+ .local v6, "f04":F
+ :goto_c5
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->b05:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_3b7
+
+ const/4 v7, 0x0
+
+ .line 149
+ .local v7, "f05":F
+ :goto_ce
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->b07:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_3bb
+
+ const/4 v9, 0x0
+
+ .line 150
+ .local v9, "f07":F
+ :goto_d7
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->b06:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_3bf
+
+ const/4 v8, 0x0
+
+ .line 151
+ .local v8, "f06":F
+ :goto_e0
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->b30:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_3c3
+
+ const/16 v32, 0x0
+
+ .line 152
+ .local v32, "f30":F
+ :goto_ea
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->b31:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_3c7
+
+ const/16 v33, 0x0
+
+ .line 153
+ .local v33, "f31":F
+ :goto_f4
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->b32:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_3cb
+
+ const/16 v34, 0x0
+
+ .line 154
+ .local v34, "f32":F
+ :goto_fe
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->b33:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_3cf
+
+ const/16 v35, 0x0
+
+ .line 155
+ .local v35, "f33":F
+ :goto_108
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->b34:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_3d3
+
+ const/16 v36, 0x0
+
+ .line 156
+ .local v36, "f34":F
+ :goto_112
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->b36:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_3d7
+
+ const/16 v38, 0x0
+
+ .line 157
+ .local v38, "f36":F
+ :goto_11c
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->b35:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_3db
+
+ const/16 v37, 0x0
+
+ .line 158
+ .local v37, "f35":F
+ :goto_126
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->b08:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_3df
+
+ const/4 v10, 0x0
+
+ .line 159
+ .local v10, "f08":F
+ :goto_12f
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->b09:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_3e3
+
+ const/4 v11, 0x0
+
+ .line 160
+ .local v11, "f09":F
+ :goto_138
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->b10:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_3e7
+
+ const/4 v12, 0x0
+
+ .line 161
+ .local v12, "f10":F
+ :goto_141
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->b11:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_3eb
+
+ const/4 v13, 0x0
+
+ .line 162
+ .local v13, "f11":F
+ :goto_14a
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->b12:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_3ef
+
+ const/4 v14, 0x0
+
+ .line 163
+ .local v14, "f12":F
+ :goto_153
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->b14:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_3f3
+
+ const/16 v16, 0x0
+
+ .line 164
+ .local v16, "f14":F
+ :goto_15d
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->b13:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_3f7
+
+ const/4 v15, 0x0
+
+ .line 166
+ .local v15, "f13":F
+ :goto_166
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->conditionA:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_202
+
+ .line 167
+ const/high16 v42, 0x447a0000 # 1000.0f
+
+ div-float v18, v18, v42
+
+ .line 168
+ const/high16 v42, 0x447a0000 # 1000.0f
+
+ div-float v19, v19, v42
+
+ .line 169
+ const/high16 v42, 0x447a0000 # 1000.0f
+
+ div-float v20, v20, v42
+
+ .line 170
+ const/high16 v42, 0x447a0000 # 1000.0f
+
+ div-float v21, v21, v42
+
+ .line 171
+ const/high16 v42, 0x447a0000 # 1000.0f
+
+ div-float v22, v22, v42
+
+ .line 172
+ const/high16 v42, 0x447a0000 # 1000.0f
+
+ div-float v23, v23, v42
+
+ .line 173
+ const/high16 v42, 0x447a0000 # 1000.0f
+
+ div-float v17, v17, v42
+
+ .line 174
+ const/high16 v42, 0x447a0000 # 1000.0f
+
+ div-float v10, v10, v42
+
+ .line 175
+ const/high16 v42, 0x447a0000 # 1000.0f
+
+ div-float v11, v11, v42
+
+ .line 176
+ const/high16 v42, 0x447a0000 # 1000.0f
+
+ div-float v12, v12, v42
+
+ .line 177
+ const/high16 v42, 0x447a0000 # 1000.0f
+
+ div-float v13, v13, v42
+
+ .line 178
+ const/high16 v42, 0x447a0000 # 1000.0f
+
+ div-float v14, v14, v42
+
+ .line 179
+ const/high16 v42, 0x447a0000 # 1000.0f
+
+ div-float v32, v32, v42
+
+ .line 180
+ const/high16 v42, 0x447a0000 # 1000.0f
+
+ div-float v33, v33, v42
+
+ .line 181
+ const/high16 v42, 0x447a0000 # 1000.0f
+
+ div-float v34, v34, v42
+
+ .line 182
+ const/high16 v42, 0x447a0000 # 1000.0f
+
+ div-float v35, v35, v42
+
+ .line 183
+ const/high16 v42, 0x447a0000 # 1000.0f
+
+ div-float v36, v36, v42
+
+ .line 184
+ const/high16 v42, 0x447a0000 # 1000.0f
+
+ div-float v3, v3, v42
+
+ .line 185
+ const/high16 v42, 0x447a0000 # 1000.0f
+
+ div-float v4, v4, v42
+
+ .line 186
+ const/high16 v42, 0x447a0000 # 1000.0f
+
+ div-float v5, v5, v42
+
+ .line 187
+ const/high16 v42, 0x447a0000 # 1000.0f
+
+ div-float v6, v6, v42
+
+ .line 188
+ const/high16 v42, 0x447a0000 # 1000.0f
+
+ div-float v7, v7, v42
+
+ .line 189
+ const/high16 v42, 0x447a0000 # 1000.0f
+
+ div-float v25, v25, v42
+
+ .line 190
+ const/high16 v42, 0x447a0000 # 1000.0f
+
+ div-float v26, v26, v42
+
+ .line 191
+ const/high16 v42, 0x447a0000 # 1000.0f
+
+ div-float v27, v27, v42
+
+ .line 192
+ const/high16 v42, 0x447a0000 # 1000.0f
+
+ div-float v28, v28, v42
+
+ .line 193
+ const/high16 v42, 0x447a0000 # 1000.0f
+
+ div-float v29, v29, v42
+
+ .line 194
+ const/high16 v42, 0x447a0000 # 1000.0f
+
+ div-float v24, v24, v42
+
+ .line 195
+ const/high16 v42, 0x447a0000 # 1000.0f
+
+ div-float v2, v2, v42
+
+ .line 196
+ const/high16 v42, 0x447a0000 # 1000.0f
+
+ div-float v16, v16, v42
+
+ .line 197
+ const/high16 v42, 0x447a0000 # 1000.0f
+
+ div-float v15, v15, v42
+
+ .line 198
+ const/high16 v42, 0x447a0000 # 1000.0f
+
+ div-float v38, v38, v42
+
+ .line 199
+ const/high16 v42, 0x447a0000 # 1000.0f
+
+ div-float v37, v37, v42
+
+ .line 200
+ const/high16 v42, 0x447a0000 # 1000.0f
+
+ div-float v9, v9, v42
+
+ .line 201
+ const/high16 v42, 0x447a0000 # 1000.0f
+
+ div-float v8, v8, v42
+
+ .line 202
+ const/high16 v42, 0x447a0000 # 1000.0f
+
+ div-float v31, v31, v42
+
+ .line 203
+ const/high16 v42, 0x447a0000 # 1000.0f
+
+ div-float v30, v30, v42
+
+ .line 209
+ :cond_202
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->conditionB:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_29e
+
+ .line 210
+ const/high16 v42, 0x42c80000 # 100.0f
+
+ div-float v18, v18, v42
+
+ .line 211
+ const/high16 v42, 0x42c80000 # 100.0f
+
+ div-float v19, v19, v42
+
+ .line 212
+ const/high16 v42, 0x42c80000 # 100.0f
+
+ div-float v20, v20, v42
+
+ .line 213
+ const/high16 v42, 0x42c80000 # 100.0f
+
+ div-float v21, v21, v42
+
+ .line 214
+ const/high16 v42, 0x42c80000 # 100.0f
+
+ div-float v22, v22, v42
+
+ .line 215
+ const/high16 v42, 0x42c80000 # 100.0f
+
+ div-float v23, v23, v42
+
+ .line 216
+ const/high16 v42, 0x42c80000 # 100.0f
+
+ div-float v17, v17, v42
+
+ .line 217
+ const/high16 v42, 0x42c80000 # 100.0f
+
+ div-float v10, v10, v42
+
+ .line 218
+ const/high16 v42, 0x42c80000 # 100.0f
+
+ div-float v11, v11, v42
+
+ .line 219
+ const/high16 v42, 0x42c80000 # 100.0f
+
+ div-float v12, v12, v42
+
+ .line 220
+ const/high16 v42, 0x42c80000 # 100.0f
+
+ div-float v13, v13, v42
+
+ .line 221
+ const/high16 v42, 0x42c80000 # 100.0f
+
+ div-float v14, v14, v42
+
+ .line 222
+ const/high16 v42, 0x42c80000 # 100.0f
+
+ div-float v32, v32, v42
+
+ .line 223
+ const/high16 v42, 0x42c80000 # 100.0f
+
+ div-float v33, v33, v42
+
+ .line 224
+ const/high16 v42, 0x42c80000 # 100.0f
+
+ div-float v34, v34, v42
+
+ .line 225
+ const/high16 v42, 0x42c80000 # 100.0f
+
+ div-float v35, v35, v42
+
+ .line 226
+ const/high16 v42, 0x42c80000 # 100.0f
+
+ div-float v36, v36, v42
+
+ .line 227
+ const/high16 v42, 0x42c80000 # 100.0f
+
+ div-float v3, v3, v42
+
+ .line 228
+ const/high16 v42, 0x42c80000 # 100.0f
+
+ div-float v4, v4, v42
+
+ .line 229
+ const/high16 v42, 0x42c80000 # 100.0f
+
+ div-float v5, v5, v42
+
+ .line 230
+ const/high16 v42, 0x42c80000 # 100.0f
+
+ div-float v6, v6, v42
+
+ .line 231
+ const/high16 v42, 0x42c80000 # 100.0f
+
+ div-float v7, v7, v42
+
+ .line 232
+ const/high16 v42, 0x42c80000 # 100.0f
+
+ div-float v25, v25, v42
+
+ .line 233
+ const/high16 v42, 0x42c80000 # 100.0f
+
+ div-float v26, v26, v42
+
+ .line 234
+ const/high16 v42, 0x42c80000 # 100.0f
+
+ div-float v27, v27, v42
+
+ .line 235
+ const/high16 v42, 0x42c80000 # 100.0f
+
+ div-float v28, v28, v42
+
+ .line 236
+ const/high16 v42, 0x42c80000 # 100.0f
+
+ div-float v29, v29, v42
+
+ .line 237
+ const/high16 v42, 0x42c80000 # 100.0f
+
+ div-float v24, v24, v42
+
+ .line 238
+ const/high16 v42, 0x42c80000 # 100.0f
+
+ div-float v2, v2, v42
+
+ .line 239
+ const/high16 v42, 0x42c80000 # 100.0f
+
+ div-float v16, v16, v42
+
+ .line 240
+ const/high16 v42, 0x42c80000 # 100.0f
+
+ div-float v15, v15, v42
+
+ .line 241
+ const/high16 v42, 0x42c80000 # 100.0f
+
+ div-float v38, v38, v42
+
+ .line 242
+ const/high16 v42, 0x42c80000 # 100.0f
+
+ div-float v37, v37, v42
+
+ .line 243
+ const/high16 v42, 0x42c80000 # 100.0f
+
+ div-float v9, v9, v42
+
+ .line 244
+ const/high16 v42, 0x42c80000 # 100.0f
+
+ div-float v8, v8, v42
+
+ .line 245
+ const/high16 v42, 0x42c80000 # 100.0f
+
+ div-float v31, v31, v42
+
+ .line 246
+ const/high16 v42, 0x42c80000 # 100.0f
+
+ div-float v30, v30, v42
+
+ .line 248
+ :cond_29e
+ move-object/from16 v0, p0
+
+ iget-boolean v0, v0, LMain2;->conditionC:Z
+
+ move/from16 v42, v0
+
+ if-eqz v42, :cond_33a
+
+ .line 249
+ const/high16 v42, 0x41400000 # 12.0f
+
+ div-float v18, v18, v42
+
+ .line 250
+ const/high16 v42, 0x41400000 # 12.0f
+
+ div-float v19, v19, v42
+
+ .line 251
+ const/high16 v42, 0x41400000 # 12.0f
+
+ div-float v20, v20, v42
+
+ .line 252
+ const/high16 v42, 0x41400000 # 12.0f
+
+ div-float v21, v21, v42
+
+ .line 253
+ const/high16 v42, 0x41400000 # 12.0f
+
+ div-float v22, v22, v42
+
+ .line 254
+ const/high16 v42, 0x41400000 # 12.0f
+
+ div-float v23, v23, v42
+
+ .line 255
+ const/high16 v42, 0x41400000 # 12.0f
+
+ div-float v17, v17, v42
+
+ .line 256
+ const/high16 v42, 0x41400000 # 12.0f
+
+ div-float v10, v10, v42
+
+ .line 257
+ const/high16 v42, 0x41400000 # 12.0f
+
+ div-float v11, v11, v42
+
+ .line 258
+ const/high16 v42, 0x41400000 # 12.0f
+
+ div-float v12, v12, v42
+
+ .line 259
+ const/high16 v42, 0x41400000 # 12.0f
+
+ div-float v13, v13, v42
+
+ .line 260
+ const/high16 v42, 0x41400000 # 12.0f
+
+ div-float v14, v14, v42
+
+ .line 261
+ const/high16 v42, 0x41400000 # 12.0f
+
+ div-float v32, v32, v42
+
+ .line 262
+ const/high16 v42, 0x41400000 # 12.0f
+
+ div-float v33, v33, v42
+
+ .line 263
+ const/high16 v42, 0x41400000 # 12.0f
+
+ div-float v34, v34, v42
+
+ .line 264
+ const/high16 v42, 0x41400000 # 12.0f
+
+ div-float v35, v35, v42
+
+ .line 265
+ const/high16 v42, 0x41400000 # 12.0f
+
+ div-float v36, v36, v42
+
+ .line 266
+ const/high16 v42, 0x41400000 # 12.0f
+
+ div-float v3, v3, v42
+
+ .line 267
+ const/high16 v42, 0x41400000 # 12.0f
+
+ div-float v4, v4, v42
+
+ .line 268
+ const/high16 v42, 0x41400000 # 12.0f
+
+ div-float v5, v5, v42
+
+ .line 269
+ const/high16 v42, 0x41400000 # 12.0f
+
+ div-float v6, v6, v42
+
+ .line 270
+ const/high16 v42, 0x41400000 # 12.0f
+
+ div-float v7, v7, v42
+
+ .line 271
+ const/high16 v42, 0x41400000 # 12.0f
+
+ div-float v25, v25, v42
+
+ .line 272
+ const/high16 v42, 0x41400000 # 12.0f
+
+ div-float v26, v26, v42
+
+ .line 273
+ const/high16 v42, 0x41400000 # 12.0f
+
+ div-float v27, v27, v42
+
+ .line 274
+ const/high16 v42, 0x41400000 # 12.0f
+
+ div-float v28, v28, v42
+
+ .line 275
+ const/high16 v42, 0x41400000 # 12.0f
+
+ div-float v29, v29, v42
+
+ .line 276
+ const/high16 v42, 0x41400000 # 12.0f
+
+ div-float v24, v24, v42
+
+ .line 277
+ const/high16 v42, 0x41400000 # 12.0f
+
+ div-float v2, v2, v42
+
+ .line 278
+ const/high16 v42, 0x41400000 # 12.0f
+
+ div-float v16, v16, v42
+
+ .line 279
+ const/high16 v42, 0x41400000 # 12.0f
+
+ div-float v15, v15, v42
+
+ .line 280
+ const/high16 v42, 0x41400000 # 12.0f
+
+ div-float v38, v38, v42
+
+ .line 281
+ const/high16 v42, 0x41400000 # 12.0f
+
+ div-float v37, v37, v42
+
+ .line 282
+ const/high16 v42, 0x41400000 # 12.0f
+
+ div-float v9, v9, v42
+
+ .line 283
+ const/high16 v42, 0x41400000 # 12.0f
+
+ div-float v8, v8, v42
+
+ .line 284
+ const/high16 v42, 0x41400000 # 12.0f
+
+ div-float v31, v31, v42
+
+ .line 285
+ const/high16 v42, 0x41400000 # 12.0f
+
+ div-float v30, v30, v42
+
+ .line 287
+ :cond_33a
+ const/16 v41, 0x0
+
+ .line 288
+ .local v41, "s":F
+ const/high16 v42, 0x42c80000 # 100.0f
+
+ mul-float v42, v42, v41
+
+ invoke-static/range {v42 .. v42}, Ljava/lang/Math;->round(F)I
+
+ move-result v42
+
+ move/from16 v0, v42
+
+ int-to-float v0, v0
+
+ move/from16 v42, v0
+
+ const/high16 v43, 0x42c80000 # 100.0f
+
+ div-float v41, v42, v43
+
+ .line 289
+ new-instance v42, Ljava/lang/StringBuilder;
+
+ invoke-direct/range {v42 .. v42}, Ljava/lang/StringBuilder;-><init>()V
+
+ move-object/from16 v0, v42
+
+ move/from16 v1, v41
+
+ invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(F)Ljava/lang/StringBuilder;
+
+ move-result-object v42
+
+ move-object/from16 v0, v42
+
+ move-object/from16 v1, v39
+
+ invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+
+ move-result-object v42
+
+ invoke-virtual/range {v42 .. v42}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
+
+ move-result-object v40
+
+ .line 290
+ .local v40, "res":Ljava/lang/String;
+ return-void
+
+ .line 128
+ .end local v2 # "f00":F
+ .end local v3 # "f01":F
+ .end local v4 # "f02":F
+ .end local v5 # "f03":F
+ .end local v6 # "f04":F
+ .end local v7 # "f05":F
+ .end local v8 # "f06":F
+ .end local v9 # "f07":F
+ .end local v10 # "f08":F
+ .end local v11 # "f09":F
+ .end local v12 # "f10":F
+ .end local v13 # "f11":F
+ .end local v14 # "f12":F
+ .end local v15 # "f13":F
+ .end local v16 # "f14":F
+ .end local v17 # "f15":F
+ .end local v18 # "f16":F
+ .end local v19 # "f17":F
+ .end local v20 # "f18":F
+ .end local v21 # "f19":F
+ .end local v22 # "f20":F
+ .end local v23 # "f21":F
+ .end local v24 # "f22":F
+ .end local v25 # "f23":F
+ .end local v26 # "f24":F
+ .end local v27 # "f25":F
+ .end local v28 # "f26":F
+ .end local v29 # "f27":F
+ .end local v30 # "f28":F
+ .end local v31 # "f29":F
+ .end local v32 # "f30":F
+ .end local v33 # "f31":F
+ .end local v34 # "f32":F
+ .end local v35 # "f33":F
+ .end local v36 # "f34":F
+ .end local v37 # "f35":F
+ .end local v38 # "f36":F
+ .end local v40 # "res":Ljava/lang/String;
+ .end local v41 # "s":F
+ :cond_367
+ const/high16 v19, 0x3f800000 # 1.0f
+
+ goto/16 :goto_c
+
+ .line 129
+ .restart local v19 # "f17":F
+ :cond_36b
+ const/high16 v18, 0x3f800000 # 1.0f
+
+ goto/16 :goto_16
+
+ .line 130
+ .restart local v18 # "f16":F
+ :cond_36f
+ const/high16 v20, 0x3f800000 # 1.0f
+
+ goto/16 :goto_20
+
+ .line 131
+ .restart local v20 # "f18":F
+ :cond_373
+ const/high16 v21, 0x3f800000 # 1.0f
+
+ goto/16 :goto_2a
+
+ .line 132
+ .restart local v21 # "f19":F
+ :cond_377
+ const/high16 v22, 0x3f800000 # 1.0f
+
+ goto/16 :goto_34
+
+ .line 133
+ .restart local v22 # "f20":F
+ :cond_37b
+ const/high16 v23, 0x3f800000 # 1.0f
+
+ goto/16 :goto_3e
+
+ .line 134
+ .restart local v23 # "f21":F
+ :cond_37f
+ const/high16 v17, 0x3f800000 # 1.0f
+
+ goto/16 :goto_48
+
+ .line 135
+ .restart local v17 # "f15":F
+ :cond_383
+ const/high16 v2, 0x3f800000 # 1.0f
+
+ goto/16 :goto_51
+
+ .line 136
+ .restart local v2 # "f00":F
+ :cond_387
+ const/high16 v24, 0x3f800000 # 1.0f
+
+ goto/16 :goto_5b
+
+ .line 137
+ .restart local v24 # "f22":F
+ :cond_38b
+ const/high16 v25, 0x3f800000 # 1.0f
+
+ goto/16 :goto_65
+
+ .line 138
+ .restart local v25 # "f23":F
+ :cond_38f
+ const/high16 v26, 0x3f800000 # 1.0f
+
+ goto/16 :goto_6f
+
+ .line 139
+ .restart local v26 # "f24":F
+ :cond_393
+ const/high16 v27, 0x3f800000 # 1.0f
+
+ goto/16 :goto_79
+
+ .line 140
+ .restart local v27 # "f25":F
+ :cond_397
+ const/high16 v28, 0x3f800000 # 1.0f
+
+ goto/16 :goto_83
+
+ .line 141
+ .restart local v28 # "f26":F
+ :cond_39b
+ const/high16 v29, 0x3f800000 # 1.0f
+
+ goto/16 :goto_8d
+
+ .line 142
+ .restart local v29 # "f27":F
+ :cond_39f
+ const/high16 v31, 0x3f800000 # 1.0f
+
+ goto/16 :goto_97
+
+ .line 143
+ .restart local v31 # "f29":F
+ :cond_3a3
+ const/high16 v30, 0x3f800000 # 1.0f
+
+ goto/16 :goto_a1
+
+ .line 144
+ .restart local v30 # "f28":F
+ :cond_3a7
+ const/high16 v3, 0x3f800000 # 1.0f
+
+ goto/16 :goto_aa
+
+ .line 145
+ .restart local v3 # "f01":F
+ :cond_3ab
+ const/high16 v4, 0x3f800000 # 1.0f
+
+ goto/16 :goto_b3
+
+ .line 146
+ .restart local v4 # "f02":F
+ :cond_3af
+ const/high16 v5, 0x3f800000 # 1.0f
+
+ goto/16 :goto_bc
+
+ .line 147
+ .restart local v5 # "f03":F
+ :cond_3b3
+ const/high16 v6, 0x3f800000 # 1.0f
+
+ goto/16 :goto_c5
+
+ .line 148
+ .restart local v6 # "f04":F
+ :cond_3b7
+ const/high16 v7, 0x3f800000 # 1.0f
+
+ goto/16 :goto_ce
+
+ .line 149
+ .restart local v7 # "f05":F
+ :cond_3bb
+ const/high16 v9, 0x3f800000 # 1.0f
+
+ goto/16 :goto_d7
+
+ .line 150
+ .restart local v9 # "f07":F
+ :cond_3bf
+ const/high16 v8, 0x3f800000 # 1.0f
+
+ goto/16 :goto_e0
+
+ .line 151
+ .restart local v8 # "f06":F
+ :cond_3c3
+ const/high16 v32, 0x3f800000 # 1.0f
+
+ goto/16 :goto_ea
+
+ .line 152
+ .restart local v32 # "f30":F
+ :cond_3c7
+ const/high16 v33, 0x3f800000 # 1.0f
+
+ goto/16 :goto_f4
+
+ .line 153
+ .restart local v33 # "f31":F
+ :cond_3cb
+ const/high16 v34, 0x3f800000 # 1.0f
+
+ goto/16 :goto_fe
+
+ .line 154
+ .restart local v34 # "f32":F
+ :cond_3cf
+ const/high16 v35, 0x3f800000 # 1.0f
+
+ goto/16 :goto_108
+
+ .line 155
+ .restart local v35 # "f33":F
+ :cond_3d3
+ const/high16 v36, 0x3f800000 # 1.0f
+
+ goto/16 :goto_112
+
+ .line 156
+ .restart local v36 # "f34":F
+ :cond_3d7
+ const/high16 v38, 0x3f800000 # 1.0f
+
+ goto/16 :goto_11c
+
+ .line 157
+ .restart local v38 # "f36":F
+ :cond_3db
+ const/high16 v37, 0x3f800000 # 1.0f
+
+ goto/16 :goto_126
+
+ .line 158
+ .restart local v37 # "f35":F
+ :cond_3df
+ const/high16 v10, 0x3f800000 # 1.0f
+
+ goto/16 :goto_12f
+
+ .line 159
+ .restart local v10 # "f08":F
+ :cond_3e3
+ const/high16 v11, 0x3f800000 # 1.0f
+
+ goto/16 :goto_138
+
+ .line 160
+ .restart local v11 # "f09":F
+ :cond_3e7
+ const/high16 v12, 0x3f800000 # 1.0f
+
+ goto/16 :goto_141
+
+ .line 161
+ .restart local v12 # "f10":F
+ :cond_3eb
+ const/high16 v13, 0x3f800000 # 1.0f
+
+ goto/16 :goto_14a
+
+ .line 162
+ .restart local v13 # "f11":F
+ :cond_3ef
+ const/high16 v14, 0x3f800000 # 1.0f
+
+ goto/16 :goto_153
+
+ .line 163
+ .restart local v14 # "f12":F
+ :cond_3f3
+ const/high16 v16, 0x3f800000 # 1.0f
+
+ goto/16 :goto_15d
+
+ .line 164
+ .restart local v16 # "f14":F
+ :cond_3f7
+ const/high16 v15, 0x3f800000 # 1.0f
+
+ goto/16 :goto_166
+.end method
diff --git a/test/626-checker-arm64-scratch-register/src-art/Main.java b/test/626-checker-arm64-scratch-register/src-art/Main.java
new file mode 100644
index 0000000..b816586
--- /dev/null
+++ b/test/626-checker-arm64-scratch-register/src-art/Main.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ Class main2 = Class.forName("Main2");
+ main2.getMethod("test").invoke(main2.newInstance());
+ System.out.println("passed");
+ }
+}
diff --git a/test/626-checker-arm64-scratch-register/src/Main.java b/test/626-checker-arm64-scratch-register/src/Main.java
index 1394917..fa8e5cd 100644
--- a/test/626-checker-arm64-scratch-register/src/Main.java
+++ b/test/626-checker-arm64-scratch-register/src/Main.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,284 +14,9 @@
* limitations under the License.
*/
+// This file is just for running on the RI as the test is ART specific.
public class Main {
-
- boolean b00;
- boolean b01;
- boolean b02;
- boolean b03;
- boolean b04;
- boolean b05;
- boolean b06;
- boolean b07;
- boolean b08;
- boolean b09;
- boolean b10;
- boolean b11;
- boolean b12;
- boolean b13;
- boolean b14;
- boolean b15;
- boolean b16;
- boolean b17;
- boolean b18;
- boolean b19;
- boolean b20;
- boolean b21;
- boolean b22;
- boolean b23;
- boolean b24;
- boolean b25;
- boolean b26;
- boolean b27;
- boolean b28;
- boolean b29;
- boolean b30;
- boolean b31;
- boolean b32;
- boolean b33;
- boolean b34;
- boolean b35;
- boolean b36;
-
- boolean conditionA;
- boolean conditionB;
- boolean conditionC;
-
- /// CHECK-START-ARM64: void Main.test() register (after)
- /// CHECK: begin_block
- /// CHECK: name "B0"
- /// CHECK: <<This:l\d+>> ParameterValue
- /// CHECK: end_block
- /// CHECK: begin_block
- /// CHECK: successors "<<ThenBlock:B\d+>>" "<<ElseBlock:B\d+>>"
- /// CHECK: <<CondB:z\d+>> InstanceFieldGet [<<This>>] field_name:Main.conditionB
- /// CHECK: If [<<CondB>>]
- /// CHECK: end_block
- /// CHECK: begin_block
- /// CHECK: name "<<ElseBlock>>"
- /// CHECK: ParallelMove moves:[40(sp)->d0,24(sp)->32(sp),28(sp)->36(sp),d0->d3,d3->d4,d2->d5,d4->d6,d5->d7,d6->d18,d7->d19,d18->d20,d19->d21,d20->d22,d21->d23,d22->d10,d23->d11,16(sp)->24(sp),20(sp)->28(sp),d10->d14,d11->d12,d12->d13,d13->d1,d14->d2,32(sp)->16(sp),36(sp)->20(sp)]
- /// CHECK: end_block
-
- /// CHECK-START-ARM64: void Main.test() disassembly (after)
- /// CHECK: begin_block
- /// CHECK: name "B0"
- /// CHECK: <<This:l\d+>> ParameterValue
- /// CHECK: end_block
- /// CHECK: begin_block
- /// CHECK: successors "<<ThenBlock:B\d+>>" "<<ElseBlock:B\d+>>"
- /// CHECK: <<CondB:z\d+>> InstanceFieldGet [<<This>>] field_name:Main.conditionB
- /// CHECK: If [<<CondB>>]
- /// CHECK: end_block
- /// CHECK: begin_block
- /// CHECK: name "<<ElseBlock>>"
- /// CHECK: ParallelMove moves:[invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid]
- /// CHECK: fmov d31, d2
- /// CHECK: ldr s2, [sp, #36]
- /// CHECK: ldr w16, [sp, #16]
- /// CHECK: str w16, [sp, #36]
- /// CHECK: str s14, [sp, #16]
- /// CHECK: ldr s14, [sp, #28]
- /// CHECK: str s1, [sp, #28]
- /// CHECK: ldr s1, [sp, #32]
- /// CHECK: str s31, [sp, #32]
- /// CHECK: ldr s31, [sp, #20]
- /// CHECK: str s31, [sp, #40]
- /// CHECK: str s12, [sp, #20]
- /// CHECK: fmov d12, d11
- /// CHECK: fmov d11, d10
- /// CHECK: fmov d10, d23
- /// CHECK: fmov d23, d22
- /// CHECK: fmov d22, d21
- /// CHECK: fmov d21, d20
- /// CHECK: fmov d20, d19
- /// CHECK: fmov d19, d18
- /// CHECK: fmov d18, d7
- /// CHECK: fmov d7, d6
- /// CHECK: fmov d6, d5
- /// CHECK: fmov d5, d4
- /// CHECK: fmov d4, d3
- /// CHECK: fmov d3, d13
- /// CHECK: ldr s13, [sp, #24]
- /// CHECK: str s3, [sp, #24]
- /// CHECK: ldr s3, pc+{{\d+}} (addr {{0x[0-9a-f]+}}) (100)
- /// CHECK: end_block
-
- public void test() {
- String r = "";
-
- // For the purpose of this regression test, the order of
- // definition of these float variable matters. Likewise with the
- // order of the instructions where these variables are used below.
- // Reordering these lines make make the original (b/32545705)
- // issue vanish.
- float f17 = b17 ? 0.0f : 1.0f;
- float f16 = b16 ? 0.0f : 1.0f;
- float f18 = b18 ? 0.0f : 1.0f;
- float f19 = b19 ? 0.0f : 1.0f;
- float f20 = b20 ? 0.0f : 1.0f;
- float f21 = b21 ? 0.0f : 1.0f;
- float f15 = b15 ? 0.0f : 1.0f;
- float f00 = b00 ? 0.0f : 1.0f;
- float f22 = b22 ? 0.0f : 1.0f;
- float f23 = b23 ? 0.0f : 1.0f;
- float f24 = b24 ? 0.0f : 1.0f;
- float f25 = b25 ? 0.0f : 1.0f;
- float f26 = b26 ? 0.0f : 1.0f;
- float f27 = b27 ? 0.0f : 1.0f;
- float f29 = b29 ? 0.0f : 1.0f;
- float f28 = b28 ? 0.0f : 1.0f;
- float f01 = b01 ? 0.0f : 1.0f;
- float f02 = b02 ? 0.0f : 1.0f;
- float f03 = b03 ? 0.0f : 1.0f;
- float f04 = b04 ? 0.0f : 1.0f;
- float f05 = b05 ? 0.0f : 1.0f;
- float f07 = b07 ? 0.0f : 1.0f;
- float f06 = b06 ? 0.0f : 1.0f;
- float f30 = b30 ? 0.0f : 1.0f;
- float f31 = b31 ? 0.0f : 1.0f;
- float f32 = b32 ? 0.0f : 1.0f;
- float f33 = b33 ? 0.0f : 1.0f;
- float f34 = b34 ? 0.0f : 1.0f;
- float f36 = b36 ? 0.0f : 1.0f;
- float f35 = b35 ? 0.0f : 1.0f;
- float f08 = b08 ? 0.0f : 1.0f;
- float f09 = b09 ? 0.0f : 1.0f;
- float f10 = b10 ? 0.0f : 1.0f;
- float f11 = b11 ? 0.0f : 1.0f;
- float f12 = b12 ? 0.0f : 1.0f;
- float f14 = b14 ? 0.0f : 1.0f;
- float f13 = b13 ? 0.0f : 1.0f;
-
- if (conditionA) {
- f16 /= 1000.0f;
- f17 /= 1000.0f;
- f18 /= 1000.0f;
- f19 /= 1000.0f;
- f20 /= 1000.0f;
- f21 /= 1000.0f;
- f15 /= 1000.0f;
- f08 /= 1000.0f;
- f09 /= 1000.0f;
- f10 /= 1000.0f;
- f11 /= 1000.0f;
- f12 /= 1000.0f;
- f30 /= 1000.0f;
- f31 /= 1000.0f;
- f32 /= 1000.0f;
- f33 /= 1000.0f;
- f34 /= 1000.0f;
- f01 /= 1000.0f;
- f02 /= 1000.0f;
- f03 /= 1000.0f;
- f04 /= 1000.0f;
- f05 /= 1000.0f;
- f23 /= 1000.0f;
- f24 /= 1000.0f;
- f25 /= 1000.0f;
- f26 /= 1000.0f;
- f27 /= 1000.0f;
- f22 /= 1000.0f;
- f00 /= 1000.0f;
- f14 /= 1000.0f;
- f13 /= 1000.0f;
- f36 /= 1000.0f;
- f35 /= 1000.0f;
- f07 /= 1000.0f;
- f06 /= 1000.0f;
- f29 /= 1000.0f;
- f28 /= 1000.0f;
- }
- // The parallel move that used to exhaust the ARM64 parallel move
- // resolver's scratch register pool (provided by VIXL) was in the
- // "else" branch of the following condition generated by ART's
- // compiler.
- if (conditionB) {
- f16 /= 100.0f;
- f17 /= 100.0f;
- f18 /= 100.0f;
- f19 /= 100.0f;
- f20 /= 100.0f;
- f21 /= 100.0f;
- f15 /= 100.0f;
- f08 /= 100.0f;
- f09 /= 100.0f;
- f10 /= 100.0f;
- f11 /= 100.0f;
- f12 /= 100.0f;
- f30 /= 100.0f;
- f31 /= 100.0f;
- f32 /= 100.0f;
- f33 /= 100.0f;
- f34 /= 100.0f;
- f01 /= 100.0f;
- f02 /= 100.0f;
- f03 /= 100.0f;
- f04 /= 100.0f;
- f05 /= 100.0f;
- f23 /= 100.0f;
- f24 /= 100.0f;
- f25 /= 100.0f;
- f26 /= 100.0f;
- f27 /= 100.0f;
- f22 /= 100.0f;
- f00 /= 100.0f;
- f14 /= 100.0f;
- f13 /= 100.0f;
- f36 /= 100.0f;
- f35 /= 100.0f;
- f07 /= 100.0f;
- f06 /= 100.0f;
- f29 /= 100.0f;
- f28 /= 100.0f;
- }
- if (conditionC) {
- f16 /= 12.0f;
- f17 /= 12.0f;
- f18 /= 12.0f;
- f19 /= 12.0f;
- f20 /= 12.0f;
- f21 /= 12.0f;
- f15 /= 12.0f;
- f08 /= 12.0f;
- f09 /= 12.0f;
- f10 /= 12.0f;
- f11 /= 12.0f;
- f12 /= 12.0f;
- f30 /= 12.0f;
- f31 /= 12.0f;
- f32 /= 12.0f;
- f33 /= 12.0f;
- f34 /= 12.0f;
- f01 /= 12.0f;
- f02 /= 12.0f;
- f03 /= 12.0f;
- f04 /= 12.0f;
- f05 /= 12.0f;
- f23 /= 12.0f;
- f24 /= 12.0f;
- f25 /= 12.0f;
- f26 /= 12.0f;
- f27 /= 12.0f;
- f22 /= 12.0f;
- f00 /= 12.0f;
- f14 /= 12.0f;
- f13 /= 12.0f;
- f36 /= 12.0f;
- f35 /= 12.0f;
- f07 /= 12.0f;
- f06 /= 12.0f;
- f29 /= 12.0f;
- f28 /= 12.0f;
- }
- float s = 0.0f;
- s = ((float) Math.round(100.0f * s)) / 100.0f;
- String res = s + r;
- }
-
public static void main(String[] args) {
- Main main = new Main();
- main.test();
System.out.println("passed");
}
}
diff --git a/test/679-checker-minmax/src/Main.java b/test/679-checker-minmax/src/Main.java
index abf8c27..4b72656 100644
--- a/test/679-checker-minmax/src/Main.java
+++ b/test/679-checker-minmax/src/Main.java
@@ -37,6 +37,13 @@
//
/// CHECK-START: int Main.minI(int) instruction_simplifier (after)
/// CHECK-NOT: InvokeStaticOrDirect
+ //
+ /// CHECK-START-ARM64: int Main.minI(int) disassembly (after)
+ /// CHECK-NOT: mov {{w\d+}}, #0x14
+ /// CHECK: cmp {{w\d+}}, #0x14
+ // Check that the constant generation was handled by VIXL.
+ /// CHECK: mov w16, #0x14
+ /// CHECK: csel {{w\d+}}, {{w\d+}}, w16, lt
public static int minI(int a) {
return Math.min(a, 20);
}
@@ -55,6 +62,13 @@
//
/// CHECK-START: long Main.minL(long) instruction_simplifier (after)
/// CHECK-NOT: InvokeStaticOrDirect
+ //
+ /// CHECK-START-ARM64: long Main.minL(long) disassembly (after)
+ /// CHECK-NOT: mov {{x\d+}}, #0x14
+ /// CHECK: cmp {{x\d+}}, #0x14
+ // Check that the constant generation was handled by VIXL.
+ /// CHECK: mov x16, #0x14
+ /// CHECK: csel {{x\d+}}, {{x\d+}}, x16, lt
public static long minL(long a) {
return Math.min(a, 20L);
}
@@ -73,6 +87,13 @@
//
/// CHECK-START: int Main.maxI(int) instruction_simplifier (after)
/// CHECK-NOT: InvokeStaticOrDirect
+ //
+ /// CHECK-START-ARM64: int Main.maxI(int) disassembly (after)
+ /// CHECK-NOT: mov {{w\d+}}, #0x14
+ /// CHECK: cmp {{w\d+}}, #0x14
+ // Check that the constant generation was handled by VIXL.
+ /// CHECK: mov w16, #0x14
+ /// CHECK: csel {{w\d+}}, {{w\d+}}, w16, gt
public static int maxI(int a) {
return Math.max(a, 20);
}
@@ -91,11 +112,166 @@
//
/// CHECK-START: long Main.maxL(long) instruction_simplifier (after)
/// CHECK-NOT: InvokeStaticOrDirect
+ //
+ /// CHECK-START-ARM64: long Main.maxL(long) disassembly (after)
+ /// CHECK-NOT: mov {{x\d+}}, #0x14
+ /// CHECK: cmp {{x\d+}}, #0x14
+ // Check that the constant generation was handled by VIXL.
+ /// CHECK: mov x16, #0x14
+ /// CHECK: csel {{x\d+}}, {{x\d+}}, x16, gt
public static long maxL(long a) {
return Math.max(a, 20L);
}
//
+ // Special Cases
+ //
+
+ /// CHECK-START-ARM64: int Main.minIntConstantZero(int) disassembly (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: mov {{w\d+}}, #0x0
+ /// CHECK: cmp {{w\d+}}, #0x0 (0)
+ /// CHECK: csel {{w\d+}}, {{w\d+}}, wzr, lt
+ /// CHECK: ret
+ public static int minIntConstantZero(int a) {
+ return Math.min(a, 0);
+ }
+
+ /// CHECK-START-ARM64: int Main.minIntConstantOne(int) disassembly (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: mov {{w\d+}}, #0x1
+ /// CHECK: cmp {{w\d+}}, #0x1 (1)
+ /// CHECK: csinc {{w\d+}}, {{w\d+}}, wzr, lt
+ /// CHECK: ret
+ public static int minIntConstantOne(int a) {
+ return Math.min(a, 1);
+ }
+
+ /// CHECK-START-ARM64: int Main.minIntConstantMinusOne(int) disassembly (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: mov {{w\d+}}, #0xffffffff
+ /// CHECK: cmn {{w\d+}}, #0x1 (1)
+ /// CHECK: csinv {{w\d+}}, {{w\d+}}, wzr, lt
+ /// CHECK: ret
+ public static int minIntConstantMinusOne(int a) {
+ return Math.min(a, -1);
+ }
+
+ /// CHECK-START-ARM64: long Main.minLongConstantZero(long) disassembly (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: mov {{x\d+}}, #0x0
+ /// CHECK: cmp {{x\d+}}, #0x0 (0)
+ /// CHECK: csel {{x\d+}}, {{x\d+}}, xzr, lt
+ /// CHECK: ret
+ public static long minLongConstantZero(long a) {
+ return Math.min(a, 0L);
+ }
+
+ /// CHECK-START-ARM64: long Main.minLongConstantOne(long) disassembly (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: mov {{x\d+}}, #0x1
+ /// CHECK: cmp {{x\d+}}, #0x1 (1)
+ /// CHECK: csinc {{x\d+}}, {{x\d+}}, xzr, lt
+ /// CHECK: ret
+ public static long minLongConstantOne(long a) {
+ return Math.min(a, 1L);
+ }
+
+ /// CHECK-START-ARM64: long Main.minLongConstantMinusOne(long) disassembly (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: mov {{x\d+}}, #0xffffffffffffffff
+ /// CHECK: cmn {{x\d+}}, #0x1 (1)
+ /// CHECK: csinv {{x\d+}}, {{x\d+}}, xzr, lt
+ /// CHECK: ret
+ public static long minLongConstantMinusOne(long a) {
+ return Math.min(a, -1L);
+ }
+
+ /// CHECK-START-ARM64: int Main.maxIntConstantZero(int) disassembly (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: mov {{w\d+}}, #0x0
+ /// CHECK: cmp {{w\d+}}, #0x0 (0)
+ /// CHECK: csel {{w\d+}}, {{w\d+}}, wzr, gt
+ /// CHECK: ret
+ public static int maxIntConstantZero(int a) {
+ return Math.max(a, 0);
+ }
+
+ /// CHECK-START-ARM64: int Main.maxIntConstantOne(int) disassembly (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: mov {{w\d+}}, #0x1
+ /// CHECK: cmp {{w\d+}}, #0x1 (1)
+ /// CHECK: csinc {{w\d+}}, {{w\d+}}, wzr, gt
+ /// CHECK: ret
+ public static int maxIntConstantOne(int a) {
+ return Math.max(a, 1);
+ }
+
+ /// CHECK-START-ARM64: int Main.maxIntConstantMinusOne(int) disassembly (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: mov {{w\d+}}, #0xffffffff
+ /// CHECK: cmn {{w\d+}}, #0x1 (1)
+ /// CHECK: csinv {{w\d+}}, {{w\d+}}, wzr, gt
+ /// CHECK: ret
+ public static int maxIntConstantMinusOne(int a) {
+ return Math.max(a, -1);
+ }
+
+ /// CHECK-START-ARM64: int Main.maxIntLargeConstant(int) disassembly (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK: mov {{w\d+}}, #0x2001
+ /// CHECK: cmp {{w\d+}}, {{w\d+}}
+ // Check that constant generation was not handled by VIXL.
+ /// CHECK-NOT: mov {{w\d+}}, #0x2001
+ /// CHECK: csel {{w\d+}}, {{w\d+}}, {{w\d+}}, gt
+ /// CHECK: ret
+ public static int maxIntLargeConstant(int a) {
+ return Math.max(a, 8193);
+ }
+
+ /// CHECK-START-ARM64: long Main.maxLongConstantZero(long) disassembly (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: mov {{x\d+}}, #0x0
+ /// CHECK: cmp {{x\d+}}, #0x0 (0)
+ /// CHECK: csel {{x\d+}}, {{x\d+}}, xzr, gt
+ /// CHECK: ret
+ public static long maxLongConstantZero(long a) {
+ return Math.max(a, 0L);
+ }
+
+ /// CHECK-START-ARM64: long Main.maxLongConstantOne(long) disassembly (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: mov {{x\d+}}, #0x1
+ /// CHECK: cmp {{x\d+}}, #0x1 (1)
+ /// CHECK: csinc {{x\d+}}, {{x\d+}}, xzr, gt
+ /// CHECK: ret
+ public static long maxLongConstantOne(long a) {
+ return Math.max(a, 1L);
+ }
+
+ /// CHECK-START-ARM64: long Main.maxLongConstantMinusOne(long) disassembly (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK-NOT: mov {{x\d+}}, #0xffffffffffffffff
+ /// CHECK: cmn {{x\d+}}, #0x1 (1)
+ /// CHECK: csinv {{x\d+}}, {{x\d+}}, xzr, gt
+ /// CHECK: ret
+ public static long maxLongConstantMinusOne(long a) {
+ return Math.max(a, -1L);
+ }
+
+ /// CHECK-START-ARM64: long Main.maxLongLargeConstant(long) disassembly (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ /// CHECK: mov {{x\d+}}, #0x2001
+ /// CHECK: cmp {{x\d+}}, {{x\d+}}
+ // Check that constant generation was not handled by VIXL.
+ /// CHECK-NOT: mov {{x\d+}}, #0x2001
+ /// CHECK: csel {{x\d+}}, {{x\d+}}, {{x\d+}}, gt
+ /// CHECK: ret
+ public static long maxLongLargeConstant(long a) {
+ return Math.max(a, 8193L);
+ }
+
+ //
// Different types.
//
@@ -538,12 +714,40 @@
// Intrinsics.
expectEquals(10, minI(10));
expectEquals(20, minI(25));
+ expectEquals(-1, minIntConstantZero(-1));
+ expectEquals(0, minIntConstantZero(1));
+ expectEquals(0, minIntConstantOne(0));
+ expectEquals(1, minIntConstantOne(2));
+ expectEquals(-2, minIntConstantMinusOne(-2));
+ expectEquals(-1, minIntConstantMinusOne(0));
expectEquals(10L, minL(10L));
expectEquals(20L, minL(25L));
+ expectEquals(-1L, minLongConstantZero(-1L));
+ expectEquals(0L, minLongConstantZero(1L));
+ expectEquals(0L, minLongConstantOne(0L));
+ expectEquals(1L, minLongConstantOne(2L));
+ expectEquals(-2L, minLongConstantMinusOne(-2L));
+ expectEquals(-1L, minLongConstantMinusOne(0L));
expectEquals(20, maxI(10));
expectEquals(25, maxI(25));
+ expectEquals(0, maxIntConstantZero(-1));
+ expectEquals(1, maxIntConstantZero(1));
+ expectEquals(1, maxIntConstantOne(0));
+ expectEquals(2, maxIntConstantOne(2));
+ expectEquals(-1, maxIntConstantMinusOne(-2));
+ expectEquals(0, maxIntConstantMinusOne(0));
+ expectEquals(8193, maxIntLargeConstant(8192));
+ expectEquals(9000, maxIntLargeConstant(9000));
expectEquals(20L, maxL(10L));
expectEquals(25L, maxL(25L));
+ expectEquals(0L, maxLongConstantZero(-1L));
+ expectEquals(1L, maxLongConstantZero(1L));
+ expectEquals(1L, maxLongConstantOne(0L));
+ expectEquals(2L, maxLongConstantOne(2L));
+ expectEquals(-1L, maxLongConstantMinusOne(-2L));
+ expectEquals(0L, maxLongConstantMinusOne(0L));
+ expectEquals(8193L, maxLongLargeConstant(8192L));
+ expectEquals(9000L, maxLongLargeConstant(9000L));
// Types.
expectEquals(10, min1(10, 20));
expectEquals(10, min2(10, 20));
diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py
index 067e678..e8d4290 100755
--- a/test/testrunner/testrunner.py
+++ b/test/testrunner/testrunner.py
@@ -328,7 +328,7 @@
if gdb_arg:
options_all += ' --gdb-arg ' + gdb_arg
- options_all += ' '.join(run_test_option)
+ options_all += ' ' + ' '.join(run_test_option)
if runtime_option:
for opt in runtime_option:
@@ -1010,11 +1010,11 @@
if build:
build_targets = ''
if 'host' in _user_input_variants['target']:
- build_targets += 'test-art-host-run-test-dependencies'
+ build_targets += 'test-art-host-run-test-dependencies '
if 'target' in _user_input_variants['target']:
- build_targets += 'test-art-target-run-test-dependencies'
+ build_targets += 'test-art-target-run-test-dependencies '
if 'jvm' in _user_input_variants['target']:
- build_targets += 'test-art-host-run-test-dependencies'
+ build_targets += 'test-art-host-run-test-dependencies '
build_command = 'make'
build_command += ' DX='
build_command += ' -j'
diff --git a/tools/ahat/src/main/com/android/ahat/dominators/DominatorsComputation.java b/tools/ahat/src/main/com/android/ahat/dominators/DominatorsComputation.java
index d3fea48..6185dee 100644
--- a/tools/ahat/src/main/com/android/ahat/dominators/DominatorsComputation.java
+++ b/tools/ahat/src/main/com/android/ahat/dominators/DominatorsComputation.java
@@ -17,9 +17,8 @@
package com.android.ahat.dominators;
import java.util.ArrayDeque;
-import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Deque;
-import java.util.List;
import java.util.Queue;
/**
@@ -106,43 +105,133 @@
// dominator of B.
public long id;
- // Upper bound on the id of this node's dominator.
- // The true immediate dominator of this node must have id <= domid.
- // This upper bound is slowly tightened as part of the dominators
- // computation.
- public long domid;
+ // The largest id of all nodes reachable from this node.
+ // If foo.id > this.maxReachableId, then foo is not reachable from this
+ // node.
+ public long maxReachableId;
+
+ // The set of ids of nodes that have references to this node.
+ public IdSet inRefIds = new IdSet();
// The current candidate dominator for this node.
- // Invariant: (domid < domS.id) implies this node is on the queue of
- // nodes to be revisited.
+ // The true immediate dominator of this node must have id <= domS.id.
public NodeS domS;
- // A node with a reference to this node that is one step closer to the
- // root than this node.
- // Invariant: srcS.id < this.id
- public NodeS srcS;
+ // The previous candidate dominator for this node.
+ // Invariant:
+ // * There are no nodes xS reachable from this node on a path of nodes
+ // with increasing ids (not counting xS.id) for which
+ // this.id > xS.domS.id > this.oldDomS.id.
+ // This ensures that when all nodes xS satisfy xS.domS == xS.oldDomS, we
+ // have found the true immediate dominator of each node.
+ //
+ // Note: We only use this field to tell if this node is scheduled to be
+ // revisited. We could replace it with a boolean to save space, but it
+ // probably doesn't save that much space and it's easier to explain the
+ // algorithm if we can refer to this field.
+ public NodeS oldDomS;
- // The largest id of the nodes we have seen so far on a path from the root
- // to this node. Used to keep track of which nodes we have already seen
- // and avoid processing them again.
- public long seenid;
+ // The set of nodes that this node is the candidate immediate dominator
+ // of. More precisely, the set of nodes xS such that xS.domS == this.
+ public NodeSet dominated = new NodeSet();
- // The set of nodes X reachable by 'this' on a path of nodes from the
- // root with increasing ids (possibly excluding X) that this node does not
- // dominate (this.id > X.domid).
- // We can use a List instead of a Set for this because we guarentee based
- // on seenid that we don't add the same node more than once to the list.
- public List<NodeS> undom = new ArrayList<NodeS>();
+ // The set of nodes that this node is the old candidate immediate
+ // dominator of that need to be revisited. Specifically, the set of nodes
+ // xS such that:
+ // xS.oldDomS == this && xS.oldDomS != xS.domS.
+ //
+ // The empty set is represented as null instead of an empty NodeSet to
+ // save memory.
+ // Invariant:
+ // If revisit != null, this node is on the global list of nodes to be
+ // revisited.
+ public NodeSet revisit = null;
}
- private static class Link {
- public NodeS srcS;
- public Node dst;
+ // A collection of node ids.
+ private static class IdSet {
+ private int size = 0;
+ private long[] ids = new long[4];
+ // Adds an id to the set.
+ public void add(long id) {
+ if (size == ids.length) {
+ ids = Arrays.copyOf(ids, size * 2);
+ }
+ ids[size++] = id;
+ }
+
+ // Returns the most recent id added to the set. Behavior is undefined if
+ // the set is empty.
+ public long last() {
+ assert size != 0;
+ return ids[size - 1];
+ }
+
+ // Returns true if the set contains an id in the range [low, high]
+ // inclusive, false otherwise.
+ public boolean hasIdInRange(long low, long high) {
+ for (int i = 0; i < size; ++i) {
+ if (low <= ids[i] && ids[i] <= high) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ // An unordered set of nodes data structure supporting efficient iteration
+ // over elements. The bulk of the time spent in the dominators algorithm is
+ // iterating over these sets. Using an array to store the set provides
+ // noticable performance improvements over ArrayList or a linked list.
+ private static class NodeSet {
+ public int size = 0;
+ public NodeS[] nodes = new NodeS[4];
+
+ public void add(NodeS nodeS) {
+ if (size == nodes.length) {
+ nodes = Arrays.copyOf(nodes, size * 2);
+ }
+ nodes[size++] = nodeS;
+ }
+
+ public void remove(NodeS nodeS) {
+ for (int i = 0; i < size; ++i) {
+ if (nodes[i] == nodeS) {
+ remove(i);
+ break;
+ }
+ }
+ }
+
+ public void remove(int index) {
+ nodes[index] = nodes[--size];
+ nodes[size] = null;
+ }
+ }
+
+ // A reference from a source node to a destination node to be processed
+ // during the initial depth-first traversal of nodes.
+ //
+ // Also used as a marker to indicate when the depth-first traversal has been
+ // completed for a node. In that case, srcS is the node depth-first
+ // traversal has been completed for, and dst will be set to null.
+ private static class Link {
+ public final NodeS srcS;
+ public final Node dst;
+
+ // Constructor for a reference from srcS to dst.
public Link(NodeS srcS, Node dst) {
this.srcS = srcS;
this.dst = dst;
}
+
+ // Constructor for a marker indicating depth-first traversal has been
+ // completed for srcS.
+ public Link(NodeS srcS) {
+ this.srcS = srcS;
+ this.dst = null;
+ }
}
/**
@@ -158,16 +247,10 @@
public static void computeDominators(Node root) {
long id = 0;
- // List of all nodes seen. We keep track of this here to update all the
- // dominators once we are done.
- List<NodeS> nodes = new ArrayList<NodeS>();
-
- // The set of nodes N such that N.domid < N.domS.id. These nodes need
- // to be revisisted because their dominator is clearly wrong.
+ // The set of nodes xS such that xS.revisit != null.
// Use a Queue instead of a Set because performance will be better. We
- // avoid adding nodes already on the queue by checking whether it was
- // already true that N.domid < N.domS.id, in which case the node is
- // already on the queue.
+ // avoid adding nodes already on the queue by checking
+ // xS == null before adding the node to the queue.
Queue<NodeS> revisit = new ArrayDeque<NodeS>();
// Set up the root node specially.
@@ -176,107 +259,166 @@
rootS.id = id++;
root.setDominatorsComputationState(rootS);
- // 1. Do a depth first search of the nodes, label them with ids and come
- // up with intial candidate dominators for them.
Deque<Link> dfs = new ArrayDeque<Link>();
+ dfs.push(new Link(rootS));
for (Node child : root.getReferencesForDominators()) {
dfs.push(new Link(rootS, child));
}
+ // 1. Do a depth first search of the nodes, label them with ids and come
+ // up with initial candidate dominators for them.
while (!dfs.isEmpty()) {
Link link = dfs.pop();
- NodeS dstS = (NodeS)link.dst.getDominatorsComputationState();
- if (dstS == null) {
- // This is the first time we have seen the node. The candidate
- // dominator is link src.
- dstS = new NodeS();
- dstS.node = link.dst;
- dstS.id = id++;
- dstS.domid = link.srcS.id;
- dstS.domS = link.srcS;
- dstS.srcS = link.srcS;
- dstS.seenid = dstS.domid;
- nodes.add(dstS);
- link.dst.setDominatorsComputationState(dstS);
- for (Node child : link.dst.getReferencesForDominators()) {
- dfs.push(new Link(dstS, child));
- }
+ if (link.dst == null) {
+ // This is the marker link indicating we have now visited all
+ // nodes reachable from link.srcS.
+ link.srcS.maxReachableId = id - 1;
} else {
- // We have seen the node already. Update the state based on the new
- // potential dominator.
- NodeS srcS = link.srcS;
- boolean revisiting = dstS.domid < dstS.domS.id;
+ NodeS dstS = (NodeS)link.dst.getDominatorsComputationState();
+ if (dstS == null) {
+ // We are seeing the destination node for the first time.
+ // The candidate dominator is the source node.
+ dstS = new NodeS();
+ link.dst.setDominatorsComputationState(dstS);
- while (srcS.id > dstS.seenid) {
- srcS.undom.add(dstS);
- srcS = srcS.srcS;
- }
- dstS.seenid = link.srcS.id;
+ dstS.node = link.dst;
+ dstS.id = id++;
+ dstS.inRefIds.add(link.srcS.id);
+ dstS.domS = link.srcS;
+ dstS.domS.dominated.add(dstS);
+ dstS.oldDomS = link.srcS;
- if (srcS.id < dstS.domid) {
- // In this case, dstS.domid must be wrong, because we just found a
- // path to dstS that does not go through dstS.domid:
- // All nodes from root to srcS have id < domid, and all nodes from
- // srcS to dstS had id > domid, so dstS.domid cannot be on this path
- // from root to dstS.
- dstS.domid = srcS.id;
- if (!revisiting) {
- revisit.add(dstS);
+ dfs.push(new Link(dstS));
+ for (Node child : link.dst.getReferencesForDominators()) {
+ dfs.push(new Link(dstS, child));
+ }
+ } else {
+ // We have seen the destination node before. Update the state based
+ // on the new potential dominator.
+ long seenid = dstS.inRefIds.last();
+ dstS.inRefIds.add(link.srcS.id);
+
+ // Go up the dominator chain until we reach a node we haven't already
+ // seen with a path to dstS.
+ NodeS xS = link.srcS;
+ while (xS.id > seenid) {
+ xS = xS.domS;
+ }
+
+ // The new dominator for dstS must have an id less than the node we
+ // just reached. Pull the dominator for dstS up its dominator
+ // chain until we find a suitable new dominator for dstS.
+ long domid = xS.id;
+ if (dstS.domS.id > domid) {
+ // Mark the node as needing to be revisited.
+ if (dstS.domS == dstS.oldDomS) {
+ if (dstS.oldDomS.revisit == null) {
+ dstS.oldDomS.revisit = new NodeSet();
+ revisit.add(dstS.oldDomS);
+ }
+ dstS.oldDomS.revisit.add(dstS);
+ }
+
+ // Update the node's candidate dominator.
+ dstS.domS.dominated.remove(dstS);
+ do {
+ dstS.domS = dstS.domS.domS;
+ } while (dstS.domS.id > domid);
+ dstS.domS.dominated.add(dstS);
}
}
}
}
- // 2. Continue revisiting nodes until they all satisfy the requirement
- // that domS.id <= domid.
+ // 2. Continue revisiting nodes until every node satisfies the requirement
+ // that domS.id == oldDomS.id.
+ while (!revisit.isEmpty()) {
+ NodeS oldDomS = revisit.poll();
+ assert oldDomS.revisit != null;
+
+ NodeSet nodes = oldDomS.revisit;
+ oldDomS.revisit = null;
+
+ // Search for pairs of nodes nodeS, xS for which
+ // nodeS.id > xS.domS.id > nodeS.oldDomS.id
+ // and there is a path of nodes with increasing ids from nodeS to xS.
+ // In that case, xS.domS must be wrong, because there is a path to xS
+ // from the root that does not go through xS.domS:
+ // * There is a path from the root to nodeS.oldDomS that doesn't go
+ // through xS.domS. Otherwise xS.domS would be a dominator of
+ // nodeS.oldDomS, but it can't be because xS.domS.id > nodeS.oldDomS.id.
+ // * There is a path from nodeS.oldDomS to nodeS that doesn't go through
+ // xS.domS, because xS.domS is not a dominator of nodeS.
+ // * There is a path from nodeS to xS that doesn't go through xS.domS,
+ // because we have a path of increasing ids from nodeS to xS, none of
+ // which can have an id smaller than nodeS as xS.domS does.
+ for (int i = 0; i < oldDomS.dominated.size; ++i) {
+ NodeS xS = oldDomS.dominated.nodes[i];
+ for (int j = 0; j < nodes.size; ++j) {
+ NodeS nodeS = nodes.nodes[j];
+ assert nodeS.oldDomS == oldDomS;
+ if (isReachableAscending(nodeS, xS)) {
+ // Update the dominator for xS.
+ if (xS.domS == xS.oldDomS) {
+ if (xS.oldDomS.revisit == null) {
+ xS.oldDomS.revisit = new NodeSet();
+ revisit.add(xS.oldDomS);
+ }
+ xS.oldDomS.revisit.add(xS);
+ }
+ oldDomS.dominated.remove(i--);
+ xS.domS = nodeS.domS;
+ xS.domS.dominated.add(xS);
+ break;
+ }
+ }
+ }
+
+ // We can now safely update oldDomS for each of the nodes nodeS while
+ // preserving the oldDomS invariant.
+ for (int i = 0; i < nodes.size; ++i) {
+ NodeS nodeS = nodes.nodes[i];
+ nodeS.oldDomS = oldDomS.oldDomS;
+ if (nodeS.oldDomS != nodeS.domS) {
+ if (nodeS.oldDomS.revisit == null) {
+ nodeS.oldDomS.revisit = new NodeSet();
+ revisit.add(nodeS.oldDomS);
+ }
+ nodeS.oldDomS.revisit.add(nodeS);
+ }
+ }
+ }
+
+ // 3. We have figured out the correct dominator for each node. Notify the
+ // user of the results by doing one last traversal of the nodes.
+ assert revisit.isEmpty();
+ revisit.add(rootS);
while (!revisit.isEmpty()) {
NodeS nodeS = revisit.poll();
- NodeS domS = nodeS.domS;
- assert nodeS.domid < domS.id;
- while (domS.id > nodeS.domid) {
- if (domS.domS.id < nodeS.domid) {
- // In this case, nodeS.domid must be wrong, because there is a path
- // from root to nodeS that does not go through nodeS.domid:
- // * We can go from root to domS without going through nodeS.domid,
- // because otherwise nodeS.domid would dominate domS, not
- // domS.domS.
- // * We can go from domS to nodeS without going through nodeS.domid
- // because we know nodeS is reachable from domS on a path of nodes
- // with increases ids, which cannot include nodeS.domid, which
- // has a smaller id than domS.
- nodeS.domid = domS.domS.id;
- }
- domS.undom.add(nodeS);
- domS = domS.srcS;
- }
- nodeS.domS = domS;
- nodeS.domid = domS.id;
-
- for (NodeS xS : nodeS.undom) {
- if (domS.id < xS.domid) {
- // In this case, xS.domid must be wrong, because there is a path
- // from the root to xX that does not go through xS.domid:
- // * We can go from root to nodeS without going through xS.domid,
- // because otherwise xS.domid would dominate nodeS, not domS.
- // * We can go from nodeS to xS without going through xS.domid
- // because we know xS is reachable from nodeS on a path of nodes
- // with increasing ids, which cannot include xS.domid, which has
- // a smaller id than nodeS.
- boolean revisiting = xS.domid < xS.domS.id;
- xS.domid = domS.id;
- if (!revisiting) {
- revisit.add(xS);
- }
- }
- }
- }
-
- // 3. Update the dominators of the nodes.
- root.setDominatorsComputationState(null);
- for (NodeS nodeS : nodes) {
- nodeS.node.setDominator(nodeS.domS.node);
+ assert nodeS.domS == nodeS.oldDomS;
+ assert nodeS.revisit == null;
nodeS.node.setDominatorsComputationState(null);
+ for (int i = 0; i < nodeS.dominated.size; ++i) {
+ NodeS xS = nodeS.dominated.nodes[i];
+ xS.node.setDominator(nodeS.node);
+ revisit.add(xS);
+ }
}
}
+
+ // Returns true if there is a path from srcS to dstS of nodes with ascending
+ // ids (not including dstS.id).
+ private static boolean isReachableAscending(NodeS srcS, NodeS dstS) {
+ if (dstS.id < srcS.id) {
+ // The first time we saw dstS was before we saw srcS. See if srcS is on
+ // the source chain for any nodes with direct references to dstS.
+ return dstS.inRefIds.hasIdInRange(srcS.id, srcS.maxReachableId);
+ }
+
+ // Otherwise dstS is only reachable from srcS on a node with ascending ids
+ // if it was visited for the first time while performing the depth-first
+ // traversal of srcS.
+ return dstS.id <= srcS.maxReachableId;
+ }
}
diff --git a/tools/ahat/src/test/com/android/ahat/DominatorsTest.java b/tools/ahat/src/test/com/android/ahat/DominatorsTest.java
index 0424e10..d9af363 100644
--- a/tools/ahat/src/test/com/android/ahat/DominatorsTest.java
+++ b/tools/ahat/src/test/com/android/ahat/DominatorsTest.java
@@ -295,4 +295,35 @@
assertEquals(p, d.dominator);
assertEquals(p, e.dominator);
}
+
+ @Test
+ public void twiceRevisit() {
+ // /---->---\
+ // / /--> f -->-\
+ // --> a --> b -->--x---> c --> d
+ // \----------->----/
+ // A regression test for a bug where we failed to ever revisit a node more
+ // than once. The node c is revisited a first time to bring its dominator
+ // up to b. c needs to be revisited again after the dominator for f is
+ // pulled up to a, and that revisit of c is necessary to ensure the
+ // dominator for d is pulled up to a.
+ Node a = new Node("a");
+ Node b = new Node("b");
+ Node x = new Node("x");
+ Node c = new Node("c");
+ Node d = new Node("d");
+ Node f = new Node("f");
+ a.depends = Arrays.asList(f, b);
+ b.depends = Arrays.asList(f, d, x);
+ x.depends = Arrays.asList(c);
+ c.depends = Arrays.asList(d);
+ f.depends = Arrays.asList(c);
+
+ a.computeDominators();
+ assertEquals(a, b.dominator);
+ assertEquals(b, x.dominator);
+ assertEquals(a, c.dominator);
+ assertEquals(a, d.dominator);
+ assertEquals(a, f.dominator);
+ }
}
diff --git a/tools/art b/tools/art
index 781ee2f..62df7eb 100644
--- a/tools/art
+++ b/tools/art
@@ -22,6 +22,7 @@
LAUNCH_WRAPPER=
LIBART=libart.so
JIT_PROFILE="no"
+ALLOW_DEFAULT_JDWP="no"
VERBOSE="no"
CLEAN_OAT_FILES="yes"
EXTRA_OPTIONS=()
@@ -90,6 +91,8 @@
--profile Run with profiling, then run using profile data.
--verbose Run script verbosely.
--no-clean Don't cleanup oat directories.
+ --allow-default-jdwp Don't automatically put in -XjdwpProvider:none.
+ You probably do not want this.
The ART_OPTIONS are passed directly to the Android Runtime.
@@ -306,6 +309,9 @@
--no-clean)
CLEAN_OAT_FILES="no"
;;
+ --allow-default-jdwp)
+ ALLOW_DEFAULT_JDWP="yes"
+ ;;
--*)
echo "unknown option: $1" 1>&2
usage
@@ -357,6 +363,10 @@
EXTRA_OPTIONS+=(-Xcompiler-option --generate-debug-info)
fi
+if [ "$ALLOW_DEFAULT_JDWP" = "no" ]; then
+ EXTRA_OPTIONS+=(-XjdwpProvider:none)
+fi
+
if [ "$JIT_PROFILE" = "yes" ]; then
# Create the profile. The runtime expects profiles to be created before
# execution.
diff --git a/tools/cleanup-buildbot-device.sh b/tools/cleanup-buildbot-device.sh
index ca5219a..694c739 100755
--- a/tools/cleanup-buildbot-device.sh
+++ b/tools/cleanup-buildbot-device.sh
@@ -28,33 +28,16 @@
exit 1
fi
- echo -e "${green}Clean up /system in chroot${nc}"
- # Remove all files under /system except the potential property_contexts file.
- #
- # The current ART Buildbot set-up runs the "setup device" step
- # (performed by script tools/setup-buildbot-device.sh) before the
- # "device cleanup" step (implemented by this script). As
- # property_contexts file aliases are created during the former step,
- # we need this exception to prevent the property_contexts file under
- # /system in the chroot from being removed by the latter step.
- #
- # TODO: Reorder ART Buildbot steps so that "device cleanup" happens
- # before "setup device" and remove this special case.
- adb shell test -d "$ART_TEST_CHROOT/system" \
- "&&" find "$ART_TEST_CHROOT/system" \
- ! -path "$ART_TEST_CHROOT/system/etc/selinux/plat_property_contexts" \
- ! -type d \
- -exec rm -f \{\} +
+ if adb shell test -d "$ART_TEST_CHROOT"; then
+ echo -e "${green}Remove entire /system directory from chroot directory${nc}"
+ adb shell rm -rf "$ART_TEST_CHROOT/system"
- echo -e "${green}Clean up some subdirs in /data in chroot${nc}"
- adb shell rm -rf \
- "$ART_TEST_CHROOT/data/local/tmp/*" \
- "$ART_TEST_CHROOT/data/art-test" \
- "$ART_TEST_CHROOT/data/nativetest" \
- "$ART_TEST_CHROOT/data/nativetest64" \
- "$ART_TEST_CHROOT/data/run-test" \
- "$ART_TEST_CHROOT/data/dalvik-cache/*" \
- "$ART_TEST_CHROOT/data/misc/trace/*"
+ echo -e "${green}Remove entire /data directory from chroot directory${nc}"
+ adb shell rm -rf "$ART_TEST_CHROOT/data"
+
+ echo -e "${green}Remove entire chroot directory${nc}"
+ adb shell rmdir "$ART_TEST_CHROOT" || adb shell ls -la "$ART_TEST_CHROOT"
+ fi
else
adb shell rm -rf \
/data/local/tmp /data/art-test /data/nativetest /data/nativetest64 '/data/misc/trace/*'
diff --git a/tools/dexanalyze/dexanalyze_experiments.cc b/tools/dexanalyze/dexanalyze_experiments.cc
index 244f45b..5299c9e 100644
--- a/tools/dexanalyze/dexanalyze_experiments.cc
+++ b/tools/dexanalyze/dexanalyze_experiments.cc
@@ -16,6 +16,7 @@
#include "dexanalyze_experiments.h"
+#include <algorithm>
#include <stdint.h>
#include <inttypes.h>
#include <iostream>
@@ -289,10 +290,21 @@
for (ClassAccessor accessor : dex_file.GetClasses()) {
std::set<size_t> unique_method_ids;
std::set<size_t> unique_string_ids;
+ // Types accessed and count.
+ std::map<size_t, size_t> types_accessed;
+
+ // Map from dex field index -> class field index.
+ std::map<uint32_t, uint32_t> field_index_map_;
+ size_t current_idx = 0u;
+ for (const ClassAccessor::Field& field : accessor.GetInstanceFields()) {
+ field_index_map_[field.GetIndex()] = current_idx++;
+ }
+
for (const ClassAccessor::Method& method : accessor.GetMethods()) {
- dex_code_bytes_ += method.GetInstructions().InsnsSizeInBytes();
+ CodeItemDataAccessor code_item(dex_file, method.GetCodeItem());
+ dex_code_bytes_ += code_item.InsnsSizeInBytes();
unique_code_items.insert(method.GetCodeItemOffset());
- for (const DexInstructionPcPair& inst : method.GetInstructions()) {
+ for (const DexInstructionPcPair& inst : code_item) {
switch (inst->Opcode()) {
case Instruction::CONST_STRING: {
const dex::StringIndex string_index(inst->VRegB_21c());
@@ -300,6 +312,36 @@
++num_string_ids_from_code_;
break;
}
+ case Instruction::IGET:
+ case Instruction::IGET_WIDE:
+ case Instruction::IGET_OBJECT:
+ case Instruction::IGET_BOOLEAN:
+ case Instruction::IGET_BYTE:
+ case Instruction::IGET_CHAR:
+ case Instruction::IGET_SHORT:
+ case Instruction::IPUT:
+ case Instruction::IPUT_WIDE:
+ case Instruction::IPUT_OBJECT:
+ case Instruction::IPUT_BOOLEAN:
+ case Instruction::IPUT_BYTE:
+ case Instruction::IPUT_CHAR:
+ case Instruction::IPUT_SHORT: {
+ const uint32_t receiver = inst->VRegB_22c();
+ const uint32_t dex_field_idx = inst->VRegC_22c();
+ const uint32_t first_arg_reg = code_item.RegistersSize() - code_item.InsSize();
+ ++field_receiver_[(receiver - first_arg_reg) & 0xF];
+ ++types_accessed[dex_file.GetFieldId(dex_field_idx).class_idx_.index_];
+ if (first_arg_reg == receiver) {
+ auto it = field_index_map_.find(dex_field_idx);
+ if (it != field_index_map_.end() && it->second < kMaxFieldIndex) {
+ ++field_index_[it->second];
+ } else {
+ ++field_index_other_;
+ }
+ }
+ ++field_output_[inst->VRegA_22c()];
+ break;
+ }
case Instruction::CONST_STRING_JUMBO: {
const dex::StringIndex string_index(inst->VRegB_31c());
unique_string_ids.insert(string_index.index_);
@@ -310,6 +352,7 @@
case Instruction::INVOKE_VIRTUAL:
case Instruction::INVOKE_VIRTUAL_RANGE: {
uint32_t method_idx = DexMethodIndex(inst.Inst());
+ ++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_];
if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) {
++same_class_virtual_;
}
@@ -320,6 +363,7 @@
case Instruction::INVOKE_DIRECT:
case Instruction::INVOKE_DIRECT_RANGE: {
uint32_t method_idx = DexMethodIndex(inst.Inst());
+ ++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_];
if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) {
++same_class_direct_;
}
@@ -330,6 +374,7 @@
case Instruction::INVOKE_STATIC:
case Instruction::INVOKE_STATIC_RANGE: {
uint32_t method_idx = DexMethodIndex(inst.Inst());
+ ++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_];
if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) {
++same_class_static_;
}
@@ -340,6 +385,7 @@
case Instruction::INVOKE_INTERFACE:
case Instruction::INVOKE_INTERFACE_RANGE: {
uint32_t method_idx = DexMethodIndex(inst.Inst());
+ ++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_];
if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) {
++same_class_interface_;
}
@@ -350,6 +396,7 @@
case Instruction::INVOKE_SUPER:
case Instruction::INVOKE_SUPER_RANGE: {
uint32_t method_idx = DexMethodIndex(inst.Inst());
+ ++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_];
if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) {
++same_class_super_;
}
@@ -357,18 +404,69 @@
unique_method_ids.insert(method_idx);
break;
}
+ case Instruction::NEW_ARRAY: {
+ ++types_accessed[inst->VRegC_22c()];
+ break;
+ }
+ case Instruction::FILLED_NEW_ARRAY: {
+ ++types_accessed[inst->VRegB_35c()];
+ break;
+ }
+ case Instruction::FILLED_NEW_ARRAY_RANGE: {
+ ++types_accessed[inst->VRegB_3rc()];
+ break;
+ }
+ case Instruction::CONST_CLASS:
+ case Instruction::CHECK_CAST:
+ case Instruction::NEW_INSTANCE: {
+ ++types_accessed[inst->VRegB_21c()];
+ break;
+ }
+ case Instruction::INSTANCE_OF: {
+ ++types_accessed[inst->VRegB_21c()];
+ break;
+ }
default:
break;
}
}
}
- total_unique_method_idx_ += unique_method_ids.size();
+ // Count uses of top 16n.
+ std::vector<size_t> uses;
+ for (auto&& p : types_accessed) {
+ uses.push_back(p.second);
+ }
+ std::sort(uses.rbegin(), uses.rend());
+ for (size_t i = 0; i < uses.size(); ++i) {
+ if (i < 16) {
+ uses_top_types_ += uses[i];
+ }
+ uses_all_types_ += uses[i];
+ }
+ total_unique_types_ += types_accessed.size();
+ total_unique_method_ids_ += unique_method_ids.size();
total_unique_string_ids_ += unique_string_ids.size();
}
total_unique_code_items_ += unique_code_items.size();
}
void CountDexIndices::Dump(std::ostream& os, uint64_t total_size) const {
+ const uint64_t fields_total = std::accumulate(field_receiver_, field_receiver_ + 16u, 0u);
+ for (size_t i = 0; i < 16; ++i) {
+ os << "receiver_reg=" << i << ": " << Percent(field_receiver_[i], fields_total) << "\n";
+ }
+ for (size_t i = 0; i < 16; ++i) {
+ os << "output_reg=" << i << ": " << Percent(field_output_[i], fields_total) << "\n";
+ }
+ const uint64_t fields_idx_total = std::accumulate(field_index_,
+ field_index_ + kMaxFieldIndex,
+ 0u) + field_index_other_;
+ for (size_t i = 0; i < kMaxFieldIndex; ++i) {
+ os << "field_idx=" << i << ": " << Percent(field_index_[i], fields_idx_total) << "\n";
+ }
+ os << "field_idx=other: " << Percent(field_index_other_, fields_idx_total) << "\n";
+ os << "field_idx_savings=" << Percent((fields_idx_total - field_index_other_) * 2, total_size)
+ << "\n";
os << "Num string ids: " << num_string_ids_ << "\n";
os << "Num method ids: " << num_method_ids_ << "\n";
os << "Num field ids: " << num_field_ids_ << "\n";
@@ -380,8 +478,10 @@
os << "Interface same class: " << PercentDivide(same_class_interface_, total_interface_) << "\n";
os << "Super same class: " << PercentDivide(same_class_super_, total_super_) << "\n";
os << "Num strings accessed from code: " << num_string_ids_from_code_ << "\n";
- os << "Unique(per class) method ids accessed from code: " << total_unique_method_idx_ << "\n";
- os << "Unique(per class) string ids accessed from code: " << total_unique_string_ids_ << "\n";
+ os << "Avg unique methods accessed per class: "
+ << double(total_unique_method_ids_) / double(num_class_defs_) << "\n";
+ os << "Avg unique strings accessed per class: "
+ << double(total_unique_string_ids_) / double(num_class_defs_) << "\n";
const size_t same_class_total =
same_class_direct_ +
same_class_virtual_ +
@@ -396,6 +496,9 @@
total_super_;
os << "Same class invokes: " << PercentDivide(same_class_total, other_class_total) << "\n";
os << "Invokes from code: " << (same_class_total + other_class_total) << "\n";
+ os << "Type uses on top types: " << PercentDivide(uses_top_types_, uses_all_types_) << "\n";
+ os << "Type uses 1b savings: " << PercentDivide(uses_top_types_, total_size) << "\n";
+ os << "Total unique types accessed per class " << total_unique_types_ << "\n";
os << "Total Dex code bytes: " << Percent(dex_code_bytes_, total_size) << "\n";
os << "Total unique code items: " << total_unique_code_items_ << "\n";
os << "Total Dex size: " << total_size << "\n";
@@ -420,7 +523,7 @@
}
case Instruction::MOVE_RESULT:
case Instruction::MOVE_RESULT_OBJECT: {
- if (space_for_out_arg) {
+ if (space_for_out_arg && inst->VRegA_11x() < 16) {
move_result_savings_ += inst->SizeInCodeUnits() * 2;
}
break;
@@ -441,7 +544,7 @@
}
os << "Move result savings: " << Percent(move_result_savings_, total_size) << "\n";
os << "One byte invoke savings: " << Percent(total, total_size) << "\n";
- const uint64_t low_arg_total = std::accumulate(arg_counts_, arg_counts_ + 3, 0u);
+ const uint64_t low_arg_total = std::accumulate(arg_counts_, arg_counts_ + 2, 0u);
os << "Low arg savings: " << Percent(low_arg_total * 2, total_size) << "\n";
}
diff --git a/tools/dexanalyze/dexanalyze_experiments.h b/tools/dexanalyze/dexanalyze_experiments.h
index 2be53d6..d3a9c59 100644
--- a/tools/dexanalyze/dexanalyze_experiments.h
+++ b/tools/dexanalyze/dexanalyze_experiments.h
@@ -17,6 +17,7 @@
#ifndef ART_TOOLS_DEXANALYZE_DEXANALYZE_EXPERIMENTS_H_
#define ART_TOOLS_DEXANALYZE_DEXANALYZE_EXPERIMENTS_H_
+#include <cstdint>
#include <iosfwd>
#include <memory>
#include <set>
@@ -90,10 +91,16 @@
private:
// Total string ids loaded from dex code.
size_t num_string_ids_from_code_ = 0;
- size_t total_unique_method_idx_ = 0;
+ size_t total_unique_method_ids_ = 0;
size_t total_unique_string_ids_ = 0;
uint64_t total_unique_code_items_ = 0u;
+ static constexpr size_t kMaxFieldIndex = 32;
+ uint64_t field_index_[kMaxFieldIndex] = {};
+ uint64_t field_index_other_ = 0u;
+ uint64_t field_receiver_[16] = {};
+ uint64_t field_output_[16] = {};
+
// Other dex ids.
size_t dex_code_bytes_ = 0;
size_t num_string_ids_ = 0;
@@ -113,6 +120,11 @@
size_t total_interface_ = 0;
size_t same_class_super_ = 0;
size_t total_super_ = 0;
+
+ // Type usage.
+ uint64_t uses_top_types_ = 0u;
+ uint64_t uses_all_types_ = 0u;
+ uint64_t total_unique_types_ = 0u;
};
// Measure various code metrics including args per invoke-virtual, fill/spill move patterns.
diff --git a/tools/teardown-buildbot-device.sh b/tools/teardown-buildbot-device.sh
index bf14ca4..d25dd2b 100755
--- a/tools/teardown-buildbot-device.sh
+++ b/tools/teardown-buildbot-device.sh
@@ -25,62 +25,82 @@
adb wait-for-device
if [[ -n "$ART_TEST_CHROOT" ]]; then
-
- # remove_filesystem_from_chroot DIR-IN-CHROOT FSTYPE REMOVE-DIR-IN-CHROOT
- # -----------------------------------------------------------------------
- # Unmount filesystem with type FSTYPE mounted in directory DIR-IN-CHROOT
- # under the chroot directory.
- # Remove DIR-IN-CHROOT under the chroot if REMOVE-DIR-IN-CHROOT is
- # true.
- remove_filesystem_from_chroot() {
- local dir_in_chroot=$1
- local fstype=$2
- local remove_dir=$3
- local dir="$ART_TEST_CHROOT/$dir_in_chroot"
- adb shell test -d "$dir" \
- && adb shell mount | grep -q "^$fstype on $dir type $fstype " \
- && if adb shell umount "$dir"; then
- $remove_dir && adb shell rmdir "$dir"
- else
- adb shell lsof "$dir"
- fi
- }
-
- # Tear down the chroot dir.
- echo -e "${green}Tear down the chroot dir in $ART_TEST_CHROOT${nc}"
-
# Check that ART_TEST_CHROOT is correctly defined.
[[ "x$ART_TEST_CHROOT" = x/* ]] || { echo "$ART_TEST_CHROOT is not an absolute path"; exit 1; }
- # Remove /dev from chroot.
- remove_filesystem_from_chroot dev tmpfs true
+ if adb shell test -d "$ART_TEST_CHROOT"; then
+ # Display users of the chroot dir.
- # Remove /sys/kernel/debug from chroot.
- # The /sys/kernel/debug directory under the chroot dir cannot be
- # deleted, as it is part of the host device's /sys filesystem.
- remove_filesystem_from_chroot sys/kernel/debug debugfs false
- # Remove /sys from chroot.
- remove_filesystem_from_chroot sys sysfs true
+ echo -e "${green}List open files under chroot dir $ART_TEST_CHROOT${nc}"
+ adb shell lsof | grep "$ART_TEST_CHROOT"
- # Remove /proc from chroot.
- remove_filesystem_from_chroot proc proc true
+ echo -e "${green}List processes running from binaries under chroot dir $ART_TEST_CHROOT${nc}"
+ for link in $(adb shell ls -d "/proc/*/root"); do
+ root=$(adb shell readlink "$link")
+ if [[ "x$root" = "x$ART_TEST_CHROOT" ]]; then
+ dir=$(dirname "$link")
+ pid=$(basename "$dir")
+ cmdline=$(adb shell cat "$dir"/cmdline | tr -d '\000')
+ echo "$cmdline (PID: $pid)"
+ fi
+ done
- # Remove /etc from chroot.
- adb shell rm -f "$ART_TEST_CHROOT/etc"
- adb shell rm -rf "$ART_TEST_CHROOT/system/etc"
- # Remove directories used for ART testing in chroot.
- adb shell rm -rf "$ART_TEST_CHROOT/data/local/tmp"
- adb shell rm -rf "$ART_TEST_CHROOT/data/dalvik-cache"
- adb shell rm -rf "$ART_TEST_CHROOT/tmp"
+ # Tear down the chroot dir.
- # Remove property_contexts file(s) from chroot.
- property_context_files="/property_contexts \
- /system/etc/selinux/plat_property_contexts \
- /vendor/etc/selinux/nonplat_property_context \
- /plat_property_contexts \
- /nonplat_property_contexts"
- for f in $property_context_files; do
- adb shell rm -f "$ART_TEST_CHROOT$f"
- done
+ echo -e "${green}Tear down the chroot set up in $ART_TEST_CHROOT${nc}"
+
+ # remove_filesystem_from_chroot DIR-IN-CHROOT FSTYPE REMOVE-DIR-IN-CHROOT
+ # -----------------------------------------------------------------------
+ # Unmount filesystem with type FSTYPE mounted in directory DIR-IN-CHROOT
+ # under the chroot directory.
+ # Remove DIR-IN-CHROOT under the chroot if REMOVE-DIR-IN-CHROOT is
+ # true.
+ remove_filesystem_from_chroot() {
+ local dir_in_chroot=$1
+ local fstype=$2
+ local remove_dir=$3
+ local dir="$ART_TEST_CHROOT/$dir_in_chroot"
+ adb shell test -d "$dir" \
+ && adb shell mount | grep -q "^$fstype on $dir type $fstype " \
+ && if adb shell umount "$dir"; then
+ $remove_dir && adb shell rmdir "$dir"
+ else
+ echo "Files still open in $dir:"
+ adb shell lsof | grep "$dir"
+ fi
+ }
+
+ # Remove /dev from chroot.
+ remove_filesystem_from_chroot dev tmpfs true
+
+ # Remove /sys/kernel/debug from chroot.
+ # The /sys/kernel/debug directory under the chroot dir cannot be
+ # deleted, as it is part of the host device's /sys filesystem.
+ remove_filesystem_from_chroot sys/kernel/debug debugfs false
+ # Remove /sys from chroot.
+ remove_filesystem_from_chroot sys sysfs true
+
+ # Remove /proc from chroot.
+ remove_filesystem_from_chroot proc proc true
+
+ # Remove /etc from chroot.
+ adb shell rm -f "$ART_TEST_CHROOT/etc"
+ adb shell rm -rf "$ART_TEST_CHROOT/system/etc"
+
+ # Remove directories used for ART testing in chroot.
+ adb shell rm -rf "$ART_TEST_CHROOT/data/local/tmp"
+ adb shell rm -rf "$ART_TEST_CHROOT/data/dalvik-cache"
+ adb shell rm -rf "$ART_TEST_CHROOT/tmp"
+
+ # Remove property_contexts file(s) from chroot.
+ property_context_files="/property_contexts \
+ /system/etc/selinux/plat_property_contexts \
+ /vendor/etc/selinux/nonplat_property_context \
+ /plat_property_contexts \
+ /nonplat_property_contexts"
+ for f in $property_context_files; do
+ adb shell rm -f "$ART_TEST_CHROOT$f"
+ done
+ fi
fi
diff --git a/tools/ti-fast/tifast.cc b/tools/ti-fast/tifast.cc
index 428304e..b147add 100644
--- a/tools/ti-fast/tifast.cc
+++ b/tools/ti-fast/tifast.cc
@@ -32,6 +32,10 @@
namespace {
+// Special art ti-version number. We will use this as a fallback if we cannot get a regular JVMTI
+// env.
+static constexpr jint kArtTiVersion = JVMTI_VERSION_1_2 | 0x40000000;
+
static void AddCapsForEvent(jvmtiEvent event, jvmtiCapabilities* caps) {
switch (event) {
#define DO_CASE(name, cap_name) \
@@ -135,6 +139,17 @@
return res;
}
+static jint SetupJvmtiEnv(JavaVM* vm, jvmtiEnv** jvmti) {
+ jint res = 0;
+ res = vm->GetEnv(reinterpret_cast<void**>(jvmti), JVMTI_VERSION_1_1);
+
+ if (res != JNI_OK || *jvmti == nullptr) {
+ LOG(ERROR) << "Unable to access JVMTI, error code " << res;
+ return vm->GetEnv(reinterpret_cast<void**>(jvmti), kArtTiVersion);
+ }
+ return res;
+}
+
} // namespace
static jint AgentStart(JavaVM* vm,
@@ -142,14 +157,9 @@
void* reserved ATTRIBUTE_UNUSED) {
jvmtiEnv* jvmti = nullptr;
jvmtiError error = JVMTI_ERROR_NONE;
- {
- jint res = 0;
- res = vm->GetEnv(reinterpret_cast<void**>(&jvmti), JVMTI_VERSION_1_1);
-
- if (res != JNI_OK || jvmti == nullptr) {
- LOG(ERROR) << "Unable to access JVMTI, error code " << res;
- return JNI_ERR;
- }
+ if (SetupJvmtiEnv(vm, &jvmti) != JNI_OK) {
+ LOG(ERROR) << "Could not get JVMTI env or ArtTiEnv!";
+ return JNI_ERR;
}
std::string args(options);
bool is_log = false;