diff options
Diffstat (limited to 'compiler/optimizing')
37 files changed, 1778 insertions, 735 deletions
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index 35ec7d41ff..57660c2623 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -282,7 +282,7 @@ void HGraphBuilder::InsertTryBoundaryBlocks(const DexFile::CodeItem& code_item) // Found a predecessor not covered by the same TryItem. Insert entering // boundary block. HTryBoundary* try_entry = - new (arena_) HTryBoundary(HTryBoundary::kEntry, try_block->GetDexPc()); + new (arena_) HTryBoundary(HTryBoundary::BoundaryKind::kEntry, try_block->GetDexPc()); try_block->CreateImmediateDominator()->AddInstruction(try_entry); LinkToCatchBlocks(try_entry, code_item, entry.second); break; @@ -316,7 +316,7 @@ void HGraphBuilder::InsertTryBoundaryBlocks(const DexFile::CodeItem& code_item) // Insert TryBoundary and link to catch blocks. HTryBoundary* try_exit = - new (arena_) HTryBoundary(HTryBoundary::kExit, successor->GetDexPc()); + new (arena_) HTryBoundary(HTryBoundary::BoundaryKind::kExit, successor->GetDexPc()); graph_->SplitEdge(try_block, successor)->AddInstruction(try_exit); LinkToCatchBlocks(try_exit, code_item, entry.second); } @@ -2843,7 +2843,7 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 case Instruction::MONITOR_ENTER: { current_block_->AddInstruction(new (arena_) HMonitorOperation( LoadLocal(instruction.VRegA_11x(), Primitive::kPrimNot, dex_pc), - HMonitorOperation::kEnter, + HMonitorOperation::OperationKind::kEnter, dex_pc)); break; } @@ -2851,7 +2851,7 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 case Instruction::MONITOR_EXIT: { current_block_->AddInstruction(new (arena_) HMonitorOperation( LoadLocal(instruction.VRegA_11x(), Primitive::kPrimNot, dex_pc), - HMonitorOperation::kExit, + HMonitorOperation::OperationKind::kExit, dex_pc)); break; } diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index cdbb9c31aa..aa9b01f30b 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -6413,6 +6413,33 @@ Literal* CodeGeneratorARM::DeduplicateMethodCodeLiteral(MethodReference target_m return DeduplicateMethodLiteral(target_method, &call_patches_); } +void LocationsBuilderARM::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instr, LocationSummary::kNoCall); + locations->SetInAt(HMultiplyAccumulate::kInputAccumulatorIndex, + Location::RequiresRegister()); + locations->SetInAt(HMultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister()); + locations->SetInAt(HMultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +} + +void InstructionCodeGeneratorARM::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) { + LocationSummary* locations = instr->GetLocations(); + Register res = locations->Out().AsRegister<Register>(); + Register accumulator = + locations->InAt(HMultiplyAccumulate::kInputAccumulatorIndex).AsRegister<Register>(); + Register mul_left = + locations->InAt(HMultiplyAccumulate::kInputMulLeftIndex).AsRegister<Register>(); + Register mul_right = + locations->InAt(HMultiplyAccumulate::kInputMulRightIndex).AsRegister<Register>(); + + if (instr->GetOpKind() == HInstruction::kAdd) { + __ mla(res, mul_left, mul_right, accumulator); + } else { + __ mls(res, mul_left, mul_right, accumulator); + } +} + void LocationsBuilderARM::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) { // Nothing to do, this should be removed during prepare for register allocator. LOG(FATAL) << "Unreachable"; @@ -6567,7 +6594,7 @@ void LocationsBuilderARM::VisitClassTableGet(HClassTableGet* instruction) { void InstructionCodeGeneratorARM::VisitClassTableGet(HClassTableGet* instruction) { LocationSummary* locations = instruction->GetLocations(); uint32_t method_offset = 0; - if (instruction->GetTableKind() == HClassTableGet::kVTable) { + if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) { method_offset = mirror::Class::EmbeddedVTableEntryOffset( instruction->GetIndex(), kArmPointerSize).SizeValue(); } else { diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index 2e4dc1e014..06e7c0015c 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -159,6 +159,7 @@ class LocationsBuilderARM : public HGraphVisitor { FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION) FOR_EACH_CONCRETE_INSTRUCTION_ARM(DECLARE_VISIT_INSTRUCTION) + FOR_EACH_CONCRETE_INSTRUCTION_SHARED(DECLARE_VISIT_INSTRUCTION) #undef DECLARE_VISIT_INSTRUCTION @@ -197,6 +198,7 @@ class InstructionCodeGeneratorARM : public InstructionCodeGenerator { FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION) FOR_EACH_CONCRETE_INSTRUCTION_ARM(DECLARE_VISIT_INSTRUCTION) + FOR_EACH_CONCRETE_INSTRUCTION_SHARED(DECLARE_VISIT_INSTRUCTION) #undef DECLARE_VISIT_INSTRUCTION diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 814f8b4d51..985dc056f6 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -1862,6 +1862,36 @@ void InstructionCodeGeneratorARM64::VisitAnd(HAnd* instruction) { HandleBinaryOp(instruction); } +void LocationsBuilderARM64::VisitArm64BitwiseNegatedRight(HArm64BitwiseNegatedRight* instr) { + DCHECK(Primitive::IsIntegralType(instr->GetType())) << instr->GetType(); + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instr); + locations->SetInAt(0, Location::RequiresRegister()); + // There is no immediate variant of negated bitwise instructions in AArch64. + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +} + +void InstructionCodeGeneratorARM64::VisitArm64BitwiseNegatedRight( + HArm64BitwiseNegatedRight* instr) { + Register dst = OutputRegister(instr); + Register lhs = InputRegisterAt(instr, 0); + Register rhs = InputRegisterAt(instr, 1); + + switch (instr->GetOpKind()) { + case HInstruction::kAnd: + __ Bic(dst, lhs, rhs); + break; + case HInstruction::kOr: + __ Orn(dst, lhs, rhs); + break; + case HInstruction::kXor: + __ Eon(dst, lhs, rhs); + break; + default: + LOG(FATAL) << "Unreachable"; + } +} + void LocationsBuilderARM64::VisitArm64DataProcWithShifterOp( HArm64DataProcWithShifterOp* instruction) { DCHECK(instruction->GetType() == Primitive::kPrimInt || @@ -1959,21 +1989,27 @@ void InstructionCodeGeneratorARM64::VisitArm64IntermediateAddress( Operand(InputOperandAt(instruction, 1))); } -void LocationsBuilderARM64::VisitArm64MultiplyAccumulate(HArm64MultiplyAccumulate* instr) { +void LocationsBuilderARM64::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instr, LocationSummary::kNoCall); - locations->SetInAt(HArm64MultiplyAccumulate::kInputAccumulatorIndex, - Location::RequiresRegister()); - locations->SetInAt(HArm64MultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister()); - locations->SetInAt(HArm64MultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister()); + HInstruction* accumulator = instr->InputAt(HMultiplyAccumulate::kInputAccumulatorIndex); + if (instr->GetOpKind() == HInstruction::kSub && + accumulator->IsConstant() && + accumulator->AsConstant()->IsZero()) { + // Don't allocate register for Mneg instruction. + } else { + locations->SetInAt(HMultiplyAccumulate::kInputAccumulatorIndex, + Location::RequiresRegister()); + } + locations->SetInAt(HMultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister()); + locations->SetInAt(HMultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister()); locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } -void InstructionCodeGeneratorARM64::VisitArm64MultiplyAccumulate(HArm64MultiplyAccumulate* instr) { +void InstructionCodeGeneratorARM64::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) { Register res = OutputRegister(instr); - Register accumulator = InputRegisterAt(instr, HArm64MultiplyAccumulate::kInputAccumulatorIndex); - Register mul_left = InputRegisterAt(instr, HArm64MultiplyAccumulate::kInputMulLeftIndex); - Register mul_right = InputRegisterAt(instr, HArm64MultiplyAccumulate::kInputMulRightIndex); + Register mul_left = InputRegisterAt(instr, HMultiplyAccumulate::kInputMulLeftIndex); + Register mul_right = InputRegisterAt(instr, HMultiplyAccumulate::kInputMulRightIndex); // Avoid emitting code that could trigger Cortex A53's erratum 835769. // This fixup should be carried out for all multiply-accumulate instructions: @@ -1993,10 +2029,17 @@ void InstructionCodeGeneratorARM64::VisitArm64MultiplyAccumulate(HArm64MultiplyA } if (instr->GetOpKind() == HInstruction::kAdd) { + Register accumulator = InputRegisterAt(instr, HMultiplyAccumulate::kInputAccumulatorIndex); __ Madd(res, mul_left, mul_right, accumulator); } else { DCHECK(instr->GetOpKind() == HInstruction::kSub); - __ Msub(res, mul_left, mul_right, accumulator); + HInstruction* accum_instr = instr->InputAt(HMultiplyAccumulate::kInputAccumulatorIndex); + if (accum_instr->IsConstant() && accum_instr->AsConstant()->IsZero()) { + __ Mneg(res, mul_left, mul_right); + } else { + Register accumulator = InputRegisterAt(instr, HMultiplyAccumulate::kInputAccumulatorIndex); + __ Msub(res, mul_left, mul_right, accumulator); + } } } @@ -5017,7 +5060,7 @@ void LocationsBuilderARM64::VisitClassTableGet(HClassTableGet* instruction) { void InstructionCodeGeneratorARM64::VisitClassTableGet(HClassTableGet* instruction) { LocationSummary* locations = instruction->GetLocations(); uint32_t method_offset = 0; - if (instruction->GetTableKind() == HClassTableGet::kVTable) { + if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) { method_offset = mirror::Class::EmbeddedVTableEntryOffset( instruction->GetIndex(), kArm64PointerSize).SizeValue(); } else { diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index 3527261835..10f1e7f008 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -196,6 +196,7 @@ class InstructionCodeGeneratorARM64 : public InstructionCodeGenerator { FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION) FOR_EACH_CONCRETE_INSTRUCTION_ARM64(DECLARE_VISIT_INSTRUCTION) + FOR_EACH_CONCRETE_INSTRUCTION_SHARED(DECLARE_VISIT_INSTRUCTION) #undef DECLARE_VISIT_INSTRUCTION @@ -285,6 +286,7 @@ class LocationsBuilderARM64 : public HGraphVisitor { FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION) FOR_EACH_CONCRETE_INSTRUCTION_ARM64(DECLARE_VISIT_INSTRUCTION) + FOR_EACH_CONCRETE_INSTRUCTION_SHARED(DECLARE_VISIT_INSTRUCTION) #undef DECLARE_VISIT_INSTRUCTION diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index 8d3d94b79d..f3c12efd8d 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -5245,7 +5245,7 @@ void LocationsBuilderMIPS::VisitClassTableGet(HClassTableGet* instruction) { void InstructionCodeGeneratorMIPS::VisitClassTableGet(HClassTableGet* instruction) { LocationSummary* locations = instruction->GetLocations(); uint32_t method_offset = 0; - if (instruction->GetTableKind() == HClassTableGet::kVTable) { + if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) { method_offset = mirror::Class::EmbeddedVTableEntryOffset( instruction->GetIndex(), kMipsPointerSize).SizeValue(); } else { diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 88e42f3faf..6b4a18c688 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -4127,7 +4127,7 @@ void LocationsBuilderX86::VisitClassTableGet(HClassTableGet* instruction) { void InstructionCodeGeneratorX86::VisitClassTableGet(HClassTableGet* instruction) { LocationSummary* locations = instruction->GetLocations(); uint32_t method_offset = 0; - if (instruction->GetTableKind() == HClassTableGet::kVTable) { + if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) { method_offset = mirror::Class::EmbeddedVTableEntryOffset( instruction->GetIndex(), kX86PointerSize).SizeValue(); } else { diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index bb24c6f59c..c132663016 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -3998,7 +3998,7 @@ void LocationsBuilderX86_64::VisitClassTableGet(HClassTableGet* instruction) { void InstructionCodeGeneratorX86_64::VisitClassTableGet(HClassTableGet* instruction) { LocationSummary* locations = instruction->GetLocations(); uint32_t method_offset = 0; - if (instruction->GetTableKind() == HClassTableGet::kVTable) { + if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) { method_offset = mirror::Class::EmbeddedVTableEntryOffset( instruction->GetIndex(), kX86_64PointerSize).SizeValue(); } else { diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index c0263e4e5b..b9638f2027 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -436,17 +436,23 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { StartAttributeStream("kind") << (try_boundary->IsEntry() ? "entry" : "exit"); } +#if defined(ART_ENABLE_CODEGEN_arm) || defined(ART_ENABLE_CODEGEN_arm64) + void VisitMultiplyAccumulate(HMultiplyAccumulate* instruction) OVERRIDE { + StartAttributeStream("kind") << instruction->GetOpKind(); + } +#endif + #ifdef ART_ENABLE_CODEGEN_arm64 + void VisitArm64BitwiseNegatedRight(HArm64BitwiseNegatedRight* instruction) OVERRIDE { + StartAttributeStream("kind") << instruction->GetOpKind(); + } + void VisitArm64DataProcWithShifterOp(HArm64DataProcWithShifterOp* instruction) OVERRIDE { StartAttributeStream("kind") << instruction->GetInstrKind() << "+" << instruction->GetOpKind(); if (HArm64DataProcWithShifterOp::IsShiftOp(instruction->GetOpKind())) { StartAttributeStream("shift") << instruction->GetShiftAmount(); } } - - void VisitArm64MultiplyAccumulate(HArm64MultiplyAccumulate* instruction) OVERRIDE { - StartAttributeStream("kind") << instruction->GetOpKind(); - } #endif bool IsPass(const char* name) { diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc index 37f2d79536..82a898a9f1 100644 --- a/compiler/optimizing/induction_var_analysis.cc +++ b/compiler/optimizing/induction_var_analysis.cc @@ -379,7 +379,7 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferShl(Inducti Primitive::Type type) { // Transfer over a shift left: treat shift by restricted constant as equivalent multiplication. int64_t value = -1; - if (a != nullptr && IsIntAndGet(b, &value)) { + if (a != nullptr && IsExact(b, &value)) { // Obtain the constant needed for the multiplication. This yields an existing instruction // if the constants is already there. Otherwise, this has a side effect on the HIR. // The restriction on the shift factor avoids generating a negative constant @@ -546,14 +546,17 @@ void HInductionVarAnalysis::VisitCondition(HLoopInformation* loop, // Analyze condition with induction at left-hand-side (e.g. i < U). InductionInfo* lower_expr = a->op_b; InductionInfo* upper_expr = b; - InductionInfo* stride = a->op_a; + InductionInfo* stride_expr = a->op_a; + // Constant stride? int64_t stride_value = 0; - if (!IsIntAndGet(stride, &stride_value)) { + if (!IsExact(stride_expr, &stride_value)) { return; } - // Rewrite condition i != U into i < U or i > U if end condition is reached exactly. - if (cmp == kCondNE && ((stride_value == +1 && IsTaken(lower_expr, upper_expr, kCondLT)) || - (stride_value == -1 && IsTaken(lower_expr, upper_expr, kCondGT)))) { + // Rewrite condition i != U into strict end condition i < U or i > U if this end condition + // is reached exactly (tested by verifying if the loop has a unit stride and the non-strict + // condition would be always taken). + if (cmp == kCondNE && ((stride_value == +1 && IsTaken(lower_expr, upper_expr, kCondLE)) || + (stride_value == -1 && IsTaken(lower_expr, upper_expr, kCondGE)))) { cmp = stride_value > 0 ? kCondLT : kCondGT; } // Normalize a linear loop control with a nonzero stride: @@ -561,7 +564,7 @@ void HInductionVarAnalysis::VisitCondition(HLoopInformation* loop, // stride < 0, either i > U or i >= U if ((stride_value > 0 && (cmp == kCondLT || cmp == kCondLE)) || (stride_value < 0 && (cmp == kCondGT || cmp == kCondGE))) { - VisitTripCount(loop, lower_expr, upper_expr, stride, stride_value, type, cmp); + VisitTripCount(loop, lower_expr, upper_expr, stride_expr, stride_value, type, cmp); } } } @@ -569,7 +572,7 @@ void HInductionVarAnalysis::VisitCondition(HLoopInformation* loop, void HInductionVarAnalysis::VisitTripCount(HLoopInformation* loop, InductionInfo* lower_expr, InductionInfo* upper_expr, - InductionInfo* stride, + InductionInfo* stride_expr, int64_t stride_value, Primitive::Type type, IfCondition cmp) { @@ -612,9 +615,10 @@ void HInductionVarAnalysis::VisitTripCount(HLoopInformation* loop, trip_count = CreateInvariantOp(kAdd, trip_count, CreateConstant(1, type)); } // Compensate for stride. - trip_count = CreateInvariantOp(kAdd, trip_count, stride); + trip_count = CreateInvariantOp(kAdd, trip_count, stride_expr); } - trip_count = CreateInvariantOp(kDiv, CreateInvariantOp(kSub, trip_count, lower_expr), stride); + trip_count = CreateInvariantOp( + kDiv, CreateInvariantOp(kSub, trip_count, lower_expr), stride_expr); // Assign the trip-count expression to the loop control. Clients that use the information // should be aware that the expression is only valid under the conditions listed above. InductionOp tcKind = kTripCountInBodyUnsafe; // needs both tests @@ -644,14 +648,25 @@ bool HInductionVarAnalysis::IsTaken(InductionInfo* lower_expr, IfCondition cmp) { int64_t lower_value; int64_t upper_value; - if (IsIntAndGet(lower_expr, &lower_value) && IsIntAndGet(upper_expr, &upper_value)) { - switch (cmp) { - case kCondLT: return lower_value < upper_value; - case kCondLE: return lower_value <= upper_value; - case kCondGT: return lower_value > upper_value; - case kCondGE: return lower_value >= upper_value; - default: LOG(FATAL) << "CONDITION UNREACHABLE"; - } + switch (cmp) { + case kCondLT: + return IsAtMost(lower_expr, &lower_value) + && IsAtLeast(upper_expr, &upper_value) + && lower_value < upper_value; + case kCondLE: + return IsAtMost(lower_expr, &lower_value) + && IsAtLeast(upper_expr, &upper_value) + && lower_value <= upper_value; + case kCondGT: + return IsAtLeast(lower_expr, &lower_value) + && IsAtMost(upper_expr, &upper_value) + && lower_value > upper_value; + case kCondGE: + return IsAtLeast(lower_expr, &lower_value) + && IsAtMost(upper_expr, &upper_value) + && lower_value >= upper_value; + default: + LOG(FATAL) << "CONDITION UNREACHABLE"; } return false; // not certain, may be untaken } @@ -660,25 +675,23 @@ bool HInductionVarAnalysis::IsFinite(InductionInfo* upper_expr, int64_t stride_value, Primitive::Type type, IfCondition cmp) { - const int64_t min = type == Primitive::kPrimInt - ? std::numeric_limits<int32_t>::min() - : std::numeric_limits<int64_t>::min(); - const int64_t max = type == Primitive::kPrimInt - ? std::numeric_limits<int32_t>::max() - : std::numeric_limits<int64_t>::max(); + const int64_t min = type == Primitive::kPrimInt ? std::numeric_limits<int32_t>::min() + : std::numeric_limits<int64_t>::min(); + const int64_t max = type == Primitive::kPrimInt ? std::numeric_limits<int32_t>::max() + : std::numeric_limits<int64_t>::max(); // Some rules under which it is certain at compile-time that the loop is finite. int64_t value; switch (cmp) { case kCondLT: return stride_value == 1 || - (IsIntAndGet(upper_expr, &value) && value <= (max - stride_value + 1)); + (IsAtMost(upper_expr, &value) && value <= (max - stride_value + 1)); case kCondLE: - return (IsIntAndGet(upper_expr, &value) && value <= (max - stride_value)); + return (IsAtMost(upper_expr, &value) && value <= (max - stride_value)); case kCondGT: return stride_value == -1 || - (IsIntAndGet(upper_expr, &value) && value >= (min - stride_value - 1)); + (IsAtLeast(upper_expr, &value) && value >= (min - stride_value - 1)); case kCondGE: - return (IsIntAndGet(upper_expr, &value) && value >= (min - stride_value)); + return (IsAtLeast(upper_expr, &value) && value >= (min - stride_value)); default: LOG(FATAL) << "CONDITION UNREACHABLE"; } @@ -733,7 +746,7 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::CreateSimplifiedInv // More exhaustive simplifications are done by later phases once induction nodes are // translated back into HIR code (e.g. by loop optimizations or BCE). int64_t value = -1; - if (IsIntAndGet(a, &value)) { + if (IsExact(a, &value)) { if (value == 0) { // Simplify 0 + b = b, 0 * b = 0. if (op == kAdd) { @@ -750,7 +763,7 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::CreateSimplifiedInv } } } - if (IsIntAndGet(b, &value)) { + if (IsExact(b, &value)) { if (value == 0) { // Simplify a + 0 = a, a - 0 = a, a * 0 = 0, -0 = 0. if (op == kAdd || op == kSub) { @@ -784,29 +797,16 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::CreateSimplifiedInv return new (graph_->GetArena()) InductionInfo(kInvariant, op, a, b, nullptr); } -bool HInductionVarAnalysis::IsIntAndGet(InductionInfo* info, int64_t* value) { - if (info != nullptr && info->induction_class == kInvariant) { - // A direct constant fetch. - if (info->operation == kFetch) { - DCHECK(info->fetch); - if (info->fetch->IsIntConstant()) { - *value = info->fetch->AsIntConstant()->GetValue(); - return true; - } else if (info->fetch->IsLongConstant()) { - *value = info->fetch->AsLongConstant()->GetValue(); - return true; - } - } - // Use range analysis to resolve compound values. - InductionVarRange range(this); - int32_t min_val = 0; - int32_t max_val = 0; - if (range.IsConstantRange(info, &min_val, &max_val) && min_val == max_val) { - *value = min_val; - return true; - } - } - return false; +bool HInductionVarAnalysis::IsExact(InductionInfo* info, int64_t* value) { + return InductionVarRange(this).IsConstant(info, InductionVarRange::kExact, value); +} + +bool HInductionVarAnalysis::IsAtMost(InductionInfo* info, int64_t* value) { + return InductionVarRange(this).IsConstant(info, InductionVarRange::kAtMost, value); +} + +bool HInductionVarAnalysis::IsAtLeast(InductionInfo* info, int64_t* value) { + return InductionVarRange(this).IsConstant(info, InductionVarRange::kAtLeast, value); } bool HInductionVarAnalysis::InductionEqual(InductionInfo* info1, diff --git a/compiler/optimizing/induction_var_analysis.h b/compiler/optimizing/induction_var_analysis.h index 84d5d82568..94d2646aec 100644 --- a/compiler/optimizing/induction_var_analysis.h +++ b/compiler/optimizing/induction_var_analysis.h @@ -189,7 +189,9 @@ class HInductionVarAnalysis : public HOptimization { InductionInfo* CreateSimplifiedInvariant(InductionOp op, InductionInfo* a, InductionInfo* b); // Constants. - bool IsIntAndGet(InductionInfo* info, int64_t* value); + bool IsExact(InductionInfo* info, /*out*/ int64_t* value); + bool IsAtMost(InductionInfo* info, /*out*/ int64_t* value); + bool IsAtLeast(InductionInfo* info, /*out*/ int64_t* value); // Helpers. static bool InductionEqual(InductionInfo* info1, InductionInfo* info2); diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc index 9566c29adf..f9b6910acd 100644 --- a/compiler/optimizing/induction_var_range.cc +++ b/compiler/optimizing/induction_var_range.cc @@ -45,17 +45,14 @@ static bool IsSafeDiv(int32_t c1, int32_t c2) { return c2 != 0 && CanLongValueFitIntoInt(static_cast<int64_t>(c1) / static_cast<int64_t>(c2)); } -/** Returns true for 32/64-bit integral constant. */ -static bool IsIntAndGet(HInstruction* instruction, int32_t* value) { +/** Returns true for 32/64-bit constant instruction. */ +static bool IsIntAndGet(HInstruction* instruction, int64_t* value) { if (instruction->IsIntConstant()) { *value = instruction->AsIntConstant()->GetValue(); return true; } else if (instruction->IsLongConstant()) { - const int64_t c = instruction->AsLongConstant()->GetValue(); - if (CanLongValueFitIntoInt(c)) { - *value = static_cast<int32_t>(c); - return true; - } + *value = instruction->AsLongConstant()->GetValue(); + return true; } return false; } @@ -65,8 +62,9 @@ static bool IsIntAndGet(HInstruction* instruction, int32_t* value) { * because length >= 0 is true. This makes it more likely the bound is useful to clients. */ static InductionVarRange::Value SimplifyMax(InductionVarRange::Value v) { - int32_t value; - if (v.a_constant > 1 && + int64_t value; + if (v.is_known && + v.a_constant > 1 && v.instruction->IsDiv() && v.instruction->InputAt(0)->IsArrayLength() && IsIntAndGet(v.instruction->InputAt(1), &value) && v.a_constant == value) { @@ -75,6 +73,16 @@ static InductionVarRange::Value SimplifyMax(InductionVarRange::Value v) { return v; } +/** Helper method to test for a constant value. */ +static bool IsConstantValue(InductionVarRange::Value v) { + return v.is_known && v.a_constant == 0; +} + +/** Helper method to test for same constant value. */ +static bool IsSameConstantValue(InductionVarRange::Value v1, InductionVarRange::Value v2) { + return IsConstantValue(v1) && IsConstantValue(v2) && v1.b_constant == v2.b_constant; +} + /** Helper method to insert an instruction. */ static HInstruction* Insert(HBasicBlock* block, HInstruction* instruction) { DCHECK(block != nullptr); @@ -99,29 +107,45 @@ bool InductionVarRange::GetInductionRange(HInstruction* context, /*out*/Value* max_val, /*out*/bool* needs_finite_test) { HLoopInformation* loop = context->GetBlock()->GetLoopInformation(); // closest enveloping loop - if (loop != nullptr) { - // Set up loop information. - HBasicBlock* header = loop->GetHeader(); - bool in_body = context->GetBlock() != header; - HInductionVarAnalysis::InductionInfo* info = - induction_analysis_->LookupInfo(loop, instruction); - HInductionVarAnalysis::InductionInfo* trip = - induction_analysis_->LookupInfo(loop, header->GetLastInstruction()); - // Find range. - *min_val = GetVal(info, trip, in_body, /* is_min */ true); - *max_val = SimplifyMax(GetVal(info, trip, in_body, /* is_min */ false)); - *needs_finite_test = NeedsTripCount(info) && IsUnsafeTripCount(trip); - return true; + if (loop == nullptr) { + return false; // no loop + } + HInductionVarAnalysis::InductionInfo* info = induction_analysis_->LookupInfo(loop, instruction); + if (info == nullptr) { + return false; // no induction information } - return false; // Nothing known + // Set up loop information. + HBasicBlock* header = loop->GetHeader(); + bool in_body = context->GetBlock() != header; + HInductionVarAnalysis::InductionInfo* trip = + induction_analysis_->LookupInfo(loop, header->GetLastInstruction()); + // Find range. + *min_val = GetVal(info, trip, in_body, /* is_min */ true); + *max_val = SimplifyMax(GetVal(info, trip, in_body, /* is_min */ false)); + *needs_finite_test = NeedsTripCount(info) && IsUnsafeTripCount(trip); + return true; } -bool InductionVarRange::RefineOuter(/*in-out*/Value* min_val, /*in-out*/Value* max_val) const { - Value v1 = RefineOuter(*min_val, /* is_min */ true); - Value v2 = RefineOuter(*max_val, /* is_min */ false); - if (v1.instruction != min_val->instruction || v2.instruction != max_val->instruction) { - *min_val = v1; - *max_val = v2; +bool InductionVarRange::RefineOuter(/*in-out*/ Value* min_val, + /*in-out*/ Value* max_val) const { + Value v1_min = RefineOuter(*min_val, /* is_min */ true); + Value v2_max = RefineOuter(*max_val, /* is_min */ false); + // The refined range is safe if both sides refine the same instruction. Otherwise, since two + // different ranges are combined, the new refined range is safe to pass back to the client if + // the extremes of the computed ranges ensure no arithmetic wrap-around anomalies occur. + if (min_val->instruction != max_val->instruction) { + Value v1_max = RefineOuter(*min_val, /* is_min */ false); + Value v2_min = RefineOuter(*max_val, /* is_min */ true); + if (!IsConstantValue(v1_max) || + !IsConstantValue(v2_min) || + v1_max.b_constant > v2_min.b_constant) { + return false; + } + } + // Did something change? + if (v1_min.instruction != min_val->instruction || v2_max.instruction != max_val->instruction) { + *min_val = v1_min; + *max_val = v2_max; return true; } return false; @@ -164,6 +188,46 @@ void InductionVarRange::GenerateTakenTest(HInstruction* context, // Private class methods. // +bool InductionVarRange::IsConstant(HInductionVarAnalysis::InductionInfo* info, + ConstantRequest request, + /*out*/ int64_t *value) const { + if (info != nullptr) { + // A direct 32-bit or 64-bit constant fetch. This immediately satisfies + // any of the three requests (kExact, kAtMost, and KAtLeast). + if (info->induction_class == HInductionVarAnalysis::kInvariant && + info->operation == HInductionVarAnalysis::kFetch) { + if (IsIntAndGet(info->fetch, value)) { + return true; + } + } + // Try range analysis while traversing outward on loops. + bool in_body = true; // no known trip count + Value v_min = GetVal(info, nullptr, in_body, /* is_min */ true); + Value v_max = GetVal(info, nullptr, in_body, /* is_min */ false); + do { + // Make sure *both* extremes are known to avoid arithmetic wrap-around anomalies. + if (IsConstantValue(v_min) && IsConstantValue(v_max) && v_min.b_constant <= v_max.b_constant) { + if ((request == kExact && v_min.b_constant == v_max.b_constant) || request == kAtMost) { + *value = v_max.b_constant; + return true; + } else if (request == kAtLeast) { + *value = v_min.b_constant; + return true; + } + } + } while (RefineOuter(&v_min, &v_max)); + // Exploit array length + c >= c, with c <= 0 to avoid arithmetic wrap-around anomalies + // (e.g. array length == maxint and c == 1 would yield minint). + if (request == kAtLeast) { + if (v_min.a_constant == 1 && v_min.b_constant <= 0 && v_min.instruction->IsArrayLength()) { + *value = v_min.b_constant; + return true; + } + } + } + return false; +} + bool InductionVarRange::NeedsTripCount(HInductionVarAnalysis::InductionInfo* info) const { if (info != nullptr) { if (info->induction_class == HInductionVarAnalysis::kLinear) { @@ -206,12 +270,10 @@ InductionVarRange::Value InductionVarRange::GetLinear(HInductionVarAnalysis::Ind if (trip != nullptr) { HInductionVarAnalysis::InductionInfo* trip_expr = trip->op_a; if (trip_expr->operation == HInductionVarAnalysis::kSub) { - int32_t min_value = 0; - int32_t stride_value = 0; - if (IsConstantRange(info->op_a, &min_value, &stride_value) && min_value == stride_value) { + int64_t stride_value = 0; + if (IsConstant(info->op_a, kExact, &stride_value)) { if (!is_min && stride_value == 1) { - // Test original trip's negative operand (trip_expr->op_b) against - // the offset of the linear induction. + // Test original trip's negative operand (trip_expr->op_b) against offset of induction. if (HInductionVarAnalysis::InductionEqual(trip_expr->op_b, info->op_b)) { // Analyze cancelled trip with just the positive operand (trip_expr->op_a). HInductionVarAnalysis::InductionInfo cancelled_trip( @@ -219,8 +281,7 @@ InductionVarRange::Value InductionVarRange::GetLinear(HInductionVarAnalysis::Ind return GetVal(&cancelled_trip, trip, in_body, is_min); } } else if (is_min && stride_value == -1) { - // Test original trip's positive operand (trip_expr->op_a) against - // the offset of the linear induction. + // Test original trip's positive operand (trip_expr->op_a) against offset of induction. if (HInductionVarAnalysis::InductionEqual(trip_expr->op_a, info->op_b)) { // Analyze cancelled trip with just the negative operand (trip_expr->op_b). HInductionVarAnalysis::InductionInfo neg( @@ -248,14 +309,16 @@ InductionVarRange::Value InductionVarRange::GetFetch(HInstruction* instruction, bool is_min) const { // Detect constants and chase the fetch a bit deeper into the HIR tree, so that it becomes // more likely range analysis will compare the same instructions as terminal nodes. - int32_t value; - if (IsIntAndGet(instruction, &value)) { - return Value(value); + int64_t value; + if (IsIntAndGet(instruction, &value) && CanLongValueFitIntoInt(value)) { + return Value(static_cast<int32_t>(value)); } else if (instruction->IsAdd()) { - if (IsIntAndGet(instruction->InputAt(0), &value)) { - return AddValue(Value(value), GetFetch(instruction->InputAt(1), trip, in_body, is_min)); - } else if (IsIntAndGet(instruction->InputAt(1), &value)) { - return AddValue(GetFetch(instruction->InputAt(0), trip, in_body, is_min), Value(value)); + if (IsIntAndGet(instruction->InputAt(0), &value) && CanLongValueFitIntoInt(value)) { + return AddValue(Value(static_cast<int32_t>(value)), + GetFetch(instruction->InputAt(1), trip, in_body, is_min)); + } else if (IsIntAndGet(instruction->InputAt(1), &value) && CanLongValueFitIntoInt(value)) { + return AddValue(GetFetch(instruction->InputAt(0), trip, in_body, is_min), + Value(static_cast<int32_t>(value))); } } else if (instruction->IsArrayLength() && instruction->InputAt(0)->IsNewArray()) { return GetFetch(instruction->InputAt(0)->InputAt(0), trip, in_body, is_min); @@ -331,29 +394,30 @@ InductionVarRange::Value InductionVarRange::GetMul(HInductionVarAnalysis::Induct Value v1_max = GetVal(info1, trip, in_body, /* is_min */ false); Value v2_min = GetVal(info2, trip, in_body, /* is_min */ true); Value v2_max = GetVal(info2, trip, in_body, /* is_min */ false); - // Try to refine certain failure. - if (v1_min.a_constant && v1_max.a_constant) { - v1_min = RefineOuter(v1_min, /* is_min */ true); - v1_max = RefineOuter(v1_max, /* is_min */ false); - } - // Positive or negative range? - if (v1_min.is_known && v1_min.a_constant == 0 && v1_min.b_constant >= 0) { - // Positive range vs. positive or negative range. - if (v2_min.is_known && v2_min.a_constant == 0 && v2_min.b_constant >= 0) { - return is_min ? MulValue(v1_min, v2_min) - : MulValue(v1_max, v2_max); - } else if (v2_max.is_known && v2_max.a_constant == 0 && v2_max.b_constant <= 0) { - return is_min ? MulValue(v1_max, v2_min) - : MulValue(v1_min, v2_max); + // Try to refine first operand. + if (!IsConstantValue(v1_min) && !IsConstantValue(v1_max)) { + RefineOuter(&v1_min, &v1_max); + } + // Constant times range. + if (IsSameConstantValue(v1_min, v1_max)) { + return MulRangeAndConstant(v2_min, v2_max, v1_min, is_min); + } else if (IsSameConstantValue(v2_min, v2_max)) { + return MulRangeAndConstant(v1_min, v1_max, v2_min, is_min); + } + // Positive range vs. positive or negative range. + if (IsConstantValue(v1_min) && v1_min.b_constant >= 0) { + if (IsConstantValue(v2_min) && v2_min.b_constant >= 0) { + return is_min ? MulValue(v1_min, v2_min) : MulValue(v1_max, v2_max); + } else if (IsConstantValue(v2_max) && v2_max.b_constant <= 0) { + return is_min ? MulValue(v1_max, v2_min) : MulValue(v1_min, v2_max); } - } else if (v1_max.is_known && v1_max.a_constant == 0 && v1_max.b_constant <= 0) { - // Negative range vs. positive or negative range. - if (v2_min.is_known && v2_min.a_constant == 0 && v2_min.b_constant >= 0) { - return is_min ? MulValue(v1_min, v2_max) - : MulValue(v1_max, v2_min); - } else if (v2_max.is_known && v2_max.a_constant == 0 && v2_max.b_constant <= 0) { - return is_min ? MulValue(v1_max, v2_max) - : MulValue(v1_min, v2_min); + } + // Negative range vs. positive or negative range. + if (IsConstantValue(v1_max) && v1_max.b_constant <= 0) { + if (IsConstantValue(v2_min) && v2_min.b_constant >= 0) { + return is_min ? MulValue(v1_min, v2_max) : MulValue(v1_max, v2_min); + } else if (IsConstantValue(v2_max) && v2_max.b_constant <= 0) { + return is_min ? MulValue(v1_max, v2_max) : MulValue(v1_min, v2_min); } } return Value(); @@ -368,43 +432,41 @@ InductionVarRange::Value InductionVarRange::GetDiv(HInductionVarAnalysis::Induct Value v1_max = GetVal(info1, trip, in_body, /* is_min */ false); Value v2_min = GetVal(info2, trip, in_body, /* is_min */ true); Value v2_max = GetVal(info2, trip, in_body, /* is_min */ false); - // Positive or negative range? - if (v1_min.is_known && v1_min.a_constant == 0 && v1_min.b_constant >= 0) { - // Positive range vs. positive or negative range. - if (v2_min.is_known && v2_min.a_constant == 0 && v2_min.b_constant >= 0) { - return is_min ? DivValue(v1_min, v2_max) - : DivValue(v1_max, v2_min); - } else if (v2_max.is_known && v2_max.a_constant == 0 && v2_max.b_constant <= 0) { - return is_min ? DivValue(v1_max, v2_max) - : DivValue(v1_min, v2_min); + // Range divided by constant. + if (IsSameConstantValue(v2_min, v2_max)) { + return DivRangeAndConstant(v1_min, v1_max, v2_min, is_min); + } + // Positive range vs. positive or negative range. + if (IsConstantValue(v1_min) && v1_min.b_constant >= 0) { + if (IsConstantValue(v2_min) && v2_min.b_constant >= 0) { + return is_min ? DivValue(v1_min, v2_max) : DivValue(v1_max, v2_min); + } else if (IsConstantValue(v2_max) && v2_max.b_constant <= 0) { + return is_min ? DivValue(v1_max, v2_max) : DivValue(v1_min, v2_min); } - } else if (v1_max.is_known && v1_max.a_constant == 0 && v1_max.b_constant <= 0) { - // Negative range vs. positive or negative range. - if (v2_min.is_known && v2_min.a_constant == 0 && v2_min.b_constant >= 0) { - return is_min ? DivValue(v1_min, v2_min) - : DivValue(v1_max, v2_max); - } else if (v2_max.is_known && v2_max.a_constant == 0 && v2_max.b_constant <= 0) { - return is_min ? DivValue(v1_max, v2_min) - : DivValue(v1_min, v2_max); + } + // Negative range vs. positive or negative range. + if (IsConstantValue(v1_max) && v1_max.b_constant <= 0) { + if (IsConstantValue(v2_min) && v2_min.b_constant >= 0) { + return is_min ? DivValue(v1_min, v2_min) : DivValue(v1_max, v2_max); + } else if (IsConstantValue(v2_max) && v2_max.b_constant <= 0) { + return is_min ? DivValue(v1_max, v2_min) : DivValue(v1_min, v2_max); } } return Value(); } -bool InductionVarRange::IsConstantRange(HInductionVarAnalysis::InductionInfo* info, - int32_t *min_value, - int32_t *max_value) const { - bool in_body = true; // no known trip count - Value v_min = GetVal(info, nullptr, in_body, /* is_min */ true); - Value v_max = GetVal(info, nullptr, in_body, /* is_min */ false); - do { - if (v_min.is_known && v_min.a_constant == 0 && v_max.is_known && v_max.a_constant == 0) { - *min_value = v_min.b_constant; - *max_value = v_max.b_constant; - return true; - } - } while (RefineOuter(&v_min, &v_max)); - return false; +InductionVarRange::Value InductionVarRange::MulRangeAndConstant(Value v_min, + Value v_max, + Value c, + bool is_min) const { + return is_min == (c.b_constant >= 0) ? MulValue(v_min, c) : MulValue(v_max, c); +} + +InductionVarRange::Value InductionVarRange::DivRangeAndConstant(Value v_min, + Value v_max, + Value c, + bool is_min) const { + return is_min == (c.b_constant >= 0) ? DivValue(v_min, c) : DivValue(v_max, c); } InductionVarRange::Value InductionVarRange::AddValue(Value v1, Value v2) const { @@ -471,22 +533,25 @@ InductionVarRange::Value InductionVarRange::MergeVal(Value v1, Value v2, bool is } InductionVarRange::Value InductionVarRange::RefineOuter(Value v, bool is_min) const { - if (v.instruction != nullptr) { - HLoopInformation* loop = - v.instruction->GetBlock()->GetLoopInformation(); // closest enveloping loop - if (loop != nullptr) { - // Set up loop information. - bool in_body = true; // use is always in body of outer loop - HInductionVarAnalysis::InductionInfo* info = - induction_analysis_->LookupInfo(loop, v.instruction); - HInductionVarAnalysis::InductionInfo* trip = - induction_analysis_->LookupInfo(loop, loop->GetHeader()->GetLastInstruction()); - // Try to refine "a x instruction + b" with outer loop range information on instruction. - return AddValue(MulValue(Value(v.a_constant), GetVal(info, trip, in_body, is_min)), - Value(v.b_constant)); - } + if (v.instruction == nullptr) { + return v; // nothing to refine } - return v; + HLoopInformation* loop = + v.instruction->GetBlock()->GetLoopInformation(); // closest enveloping loop + if (loop == nullptr) { + return v; // no loop + } + HInductionVarAnalysis::InductionInfo* info = induction_analysis_->LookupInfo(loop, v.instruction); + if (info == nullptr) { + return v; // no induction information + } + // Set up loop information. + HBasicBlock* header = loop->GetHeader(); + bool in_body = true; // inner always in more outer + HInductionVarAnalysis::InductionInfo* trip = + induction_analysis_->LookupInfo(loop, header->GetLastInstruction()); + // Try to refine "a x instruction + b" with outer loop range information on instruction. + return AddValue(MulValue(Value(v.a_constant), GetVal(info, trip, in_body, is_min)), Value(v.b_constant)); } bool InductionVarRange::GenerateCode(HInstruction* context, @@ -499,44 +564,45 @@ bool InductionVarRange::GenerateCode(HInstruction* context, /*out*/bool* needs_finite_test, /*out*/bool* needs_taken_test) const { HLoopInformation* loop = context->GetBlock()->GetLoopInformation(); // closest enveloping loop - if (loop != nullptr) { - // Set up loop information. - HBasicBlock* header = loop->GetHeader(); - bool in_body = context->GetBlock() != header; - HInductionVarAnalysis::InductionInfo* info = - induction_analysis_->LookupInfo(loop, instruction); - if (info == nullptr) { - return false; // nothing to analyze - } - HInductionVarAnalysis::InductionInfo* trip = - induction_analysis_->LookupInfo(loop, header->GetLastInstruction()); - // Determine what tests are needed. A finite test is needed if the evaluation code uses the - // trip-count and the loop maybe unsafe (because in such cases, the index could "overshoot" - // the computed range). A taken test is needed for any unknown trip-count, even if evaluation - // code does not use the trip-count explicitly (since there could be an implicit relation - // between e.g. an invariant subscript and a not-taken condition). - *needs_finite_test = NeedsTripCount(info) && IsUnsafeTripCount(trip); - *needs_taken_test = IsBodyTripCount(trip); - // Code generation for taken test: generate the code when requested or otherwise analyze - // if code generation is feasible when taken test is needed. - if (taken_test != nullptr) { - return GenerateCode( - trip->op_b, nullptr, graph, block, taken_test, in_body, /* is_min */ false); - } else if (*needs_taken_test) { - if (!GenerateCode( - trip->op_b, nullptr, nullptr, nullptr, nullptr, in_body, /* is_min */ false)) { - return false; - } + if (loop == nullptr) { + return false; // no loop + } + HInductionVarAnalysis::InductionInfo* info = induction_analysis_->LookupInfo(loop, instruction); + if (info == nullptr) { + return false; // no induction information + } + // Set up loop information. + HBasicBlock* header = loop->GetHeader(); + bool in_body = context->GetBlock() != header; + HInductionVarAnalysis::InductionInfo* trip = + induction_analysis_->LookupInfo(loop, header->GetLastInstruction()); + if (trip == nullptr) { + return false; // codegen relies on trip count + } + // Determine what tests are needed. A finite test is needed if the evaluation code uses the + // trip-count and the loop maybe unsafe (because in such cases, the index could "overshoot" + // the computed range). A taken test is needed for any unknown trip-count, even if evaluation + // code does not use the trip-count explicitly (since there could be an implicit relation + // between e.g. an invariant subscript and a not-taken condition). + *needs_finite_test = NeedsTripCount(info) && IsUnsafeTripCount(trip); + *needs_taken_test = IsBodyTripCount(trip); + // Code generation for taken test: generate the code when requested or otherwise analyze + // if code generation is feasible when taken test is needed. + if (taken_test != nullptr) { + return GenerateCode(trip->op_b, nullptr, graph, block, taken_test, in_body, /* is_min */ false); + } else if (*needs_taken_test) { + if (!GenerateCode( + trip->op_b, nullptr, nullptr, nullptr, nullptr, in_body, /* is_min */ false)) { + return false; } - // Code generation for lower and upper. - return - // Success on lower if invariant (not set), or code can be generated. - ((info->induction_class == HInductionVarAnalysis::kInvariant) || - GenerateCode(info, trip, graph, block, lower, in_body, /* is_min */ true)) && - // And success on upper. - GenerateCode(info, trip, graph, block, upper, in_body, /* is_min */ false); } - return false; + // Code generation for lower and upper. + return + // Success on lower if invariant (not set), or code can be generated. + ((info->induction_class == HInductionVarAnalysis::kInvariant) || + GenerateCode(info, trip, graph, block, lower, in_body, /* is_min */ true)) && + // And success on upper. + GenerateCode(info, trip, graph, block, upper, in_body, /* is_min */ false); } bool InductionVarRange::GenerateCode(HInductionVarAnalysis::InductionInfo* info, @@ -639,9 +705,8 @@ bool InductionVarRange::GenerateCode(HInductionVarAnalysis::InductionInfo* info, case HInductionVarAnalysis::kLinear: { // Linear induction a * i + b, for normalized 0 <= i < TC. Restrict to unit stride only // to avoid arithmetic wrap-around situations that are hard to guard against. - int32_t min_value = 0; - int32_t stride_value = 0; - if (IsConstantRange(info->op_a, &min_value, &stride_value) && min_value == stride_value) { + int64_t stride_value = 0; + if (IsConstant(info->op_a, kExact, &stride_value)) { if (stride_value == 1 || stride_value == -1) { const bool is_min_a = stride_value == 1 ? is_min : !is_min; if (GenerateCode(trip, trip, graph, block, &opa, in_body, is_min_a) && @@ -666,7 +731,7 @@ bool InductionVarRange::GenerateCode(HInductionVarAnalysis::InductionInfo* info, // Wrap-around and periodic inductions are restricted to constants only, so that extreme // values are easy to test at runtime without complications of arithmetic wrap-around. Value extreme = GetVal(info, trip, in_body, is_min); - if (extreme.is_known && extreme.a_constant == 0) { + if (IsConstantValue(extreme)) { if (graph != nullptr) { *result = graph->GetIntConstant(extreme.b_constant); } diff --git a/compiler/optimizing/induction_var_range.h b/compiler/optimizing/induction_var_range.h index 3cb7b4bfd5..0af41560ff 100644 --- a/compiler/optimizing/induction_var_range.h +++ b/compiler/optimizing/induction_var_range.h @@ -69,7 +69,8 @@ class InductionVarRange { /*out*/ bool* needs_finite_test); /** Refines the values with induction of next outer loop. Returns true on change. */ - bool RefineOuter(/*in-out*/Value* min_val, /*in-out*/Value* max_val) const; + bool RefineOuter(/*in-out*/ Value* min_val, + /*in-out*/ Value* max_val) const; /** * Returns true if range analysis is able to generate code for the lower and upper @@ -116,6 +117,23 @@ class InductionVarRange { /*out*/ HInstruction** taken_test); private: + /* + * Enum used in IsConstant() request. + */ + enum ConstantRequest { + kExact, + kAtMost, + kAtLeast + }; + + /** + * Returns true if exact or upper/lower bound on the given induction + * information is known as a 64-bit constant, which is returned in value. + */ + bool IsConstant(HInductionVarAnalysis::InductionInfo* info, + ConstantRequest request, + /*out*/ int64_t *value) const; + bool NeedsTripCount(HInductionVarAnalysis::InductionInfo* info) const; bool IsBodyTripCount(HInductionVarAnalysis::InductionInfo* trip) const; bool IsUnsafeTripCount(HInductionVarAnalysis::InductionInfo* trip) const; @@ -143,9 +161,8 @@ class InductionVarRange { bool in_body, bool is_min) const; - bool IsConstantRange(HInductionVarAnalysis::InductionInfo* info, - int32_t *min_value, - int32_t *max_value) const; + Value MulRangeAndConstant(Value v1, Value v2, Value c, bool is_min) const; + Value DivRangeAndConstant(Value v1, Value v2, Value c, bool is_min) const; Value AddValue(Value v1, Value v2) const; Value SubValue(Value v1, Value v2) const; diff --git a/compiler/optimizing/induction_var_range_test.cc b/compiler/optimizing/induction_var_range_test.cc index 55a654e301..c5c33bd9bc 100644 --- a/compiler/optimizing/induction_var_range_test.cc +++ b/compiler/optimizing/induction_var_range_test.cc @@ -215,10 +215,16 @@ class InductionVarRangeTest : public CommonCompilerTest { return range_.GetDiv(info1, info2, nullptr, /* in_body */ true, is_min); } - bool IsConstantRange(HInductionVarAnalysis::InductionInfo* info, - int32_t* min_value, - int32_t* max_value) { - return range_.IsConstantRange(info, min_value, max_value); + bool IsExact(HInductionVarAnalysis::InductionInfo* info, int64_t* value) { + return range_.IsConstant(info, InductionVarRange::kExact, value); + } + + bool IsAtMost(HInductionVarAnalysis::InductionInfo* info, int64_t* value) { + return range_.IsConstant(info, InductionVarRange::kAtMost, value); + } + + bool IsAtLeast(HInductionVarAnalysis::InductionInfo* info, int64_t* value) { + return range_.IsConstant(info, InductionVarRange::kAtLeast, value); } Value AddValue(Value v1, Value v2) { return range_.AddValue(v1, v2); } @@ -249,6 +255,34 @@ class InductionVarRangeTest : public CommonCompilerTest { // Tests on private methods. // +TEST_F(InductionVarRangeTest, IsConstant) { + int64_t value; + // Constant. + EXPECT_TRUE(IsExact(CreateConst(12345), &value)); + EXPECT_EQ(12345, value); + EXPECT_TRUE(IsAtMost(CreateConst(12345), &value)); + EXPECT_EQ(12345, value); + EXPECT_TRUE(IsAtLeast(CreateConst(12345), &value)); + EXPECT_EQ(12345, value); + // Constant trivial range. + EXPECT_TRUE(IsExact(CreateRange(111, 111), &value)); + EXPECT_EQ(111, value); + EXPECT_TRUE(IsAtMost(CreateRange(111, 111), &value)); + EXPECT_EQ(111, value); + EXPECT_TRUE(IsAtLeast(CreateRange(111, 111), &value)); + EXPECT_EQ(111, value); + // Constant non-trivial range. + EXPECT_FALSE(IsExact(CreateRange(11, 22), &value)); + EXPECT_TRUE(IsAtMost(CreateRange(11, 22), &value)); + EXPECT_EQ(22, value); + EXPECT_TRUE(IsAtLeast(CreateRange(11, 22), &value)); + EXPECT_EQ(11, value); + // Symbolic. + EXPECT_FALSE(IsExact(CreateFetch(x_), &value)); + EXPECT_FALSE(IsAtMost(CreateFetch(x_), &value)); + EXPECT_FALSE(IsAtLeast(CreateFetch(x_), &value)); +} + TEST_F(InductionVarRangeTest, TripCountProperties) { EXPECT_FALSE(NeedsTripCount(nullptr)); EXPECT_FALSE(NeedsTripCount(CreateConst(1))); @@ -367,6 +401,10 @@ TEST_F(InductionVarRangeTest, GetMinMaxPeriodic) { } TEST_F(InductionVarRangeTest, GetMulMin) { + ExpectEqual(Value(-14), GetMul(CreateConst(2), CreateRange(-7, 8), true)); + ExpectEqual(Value(-16), GetMul(CreateConst(-2), CreateRange(-7, 8), true)); + ExpectEqual(Value(-14), GetMul(CreateRange(-7, 8), CreateConst(2), true)); + ExpectEqual(Value(-16), GetMul(CreateRange(-7, 8), CreateConst(-2), true)); ExpectEqual(Value(6), GetMul(CreateRange(2, 10), CreateRange(3, 5), true)); ExpectEqual(Value(-50), GetMul(CreateRange(2, 10), CreateRange(-5, -3), true)); ExpectEqual(Value(), GetMul(CreateRange(2, 10), CreateRange(-1, 1), true)); @@ -379,6 +417,10 @@ TEST_F(InductionVarRangeTest, GetMulMin) { } TEST_F(InductionVarRangeTest, GetMulMax) { + ExpectEqual(Value(16), GetMul(CreateConst(2), CreateRange(-7, 8), false)); + ExpectEqual(Value(14), GetMul(CreateConst(-2), CreateRange(-7, 8), false)); + ExpectEqual(Value(16), GetMul(CreateRange(-7, 8), CreateConst(2), false)); + ExpectEqual(Value(14), GetMul(CreateRange(-7, 8), CreateConst(-2), false)); ExpectEqual(Value(50), GetMul(CreateRange(2, 10), CreateRange(3, 5), false)); ExpectEqual(Value(-6), GetMul(CreateRange(2, 10), CreateRange(-5, -3), false)); ExpectEqual(Value(), GetMul(CreateRange(2, 10), CreateRange(-1, 1), false)); @@ -391,6 +433,8 @@ TEST_F(InductionVarRangeTest, GetMulMax) { } TEST_F(InductionVarRangeTest, GetDivMin) { + ExpectEqual(Value(-5), GetDiv(CreateRange(-10, 20), CreateConst(2), true)); + ExpectEqual(Value(-10), GetDiv(CreateRange(-10, 20), CreateConst(-2), true)); ExpectEqual(Value(10), GetDiv(CreateRange(40, 1000), CreateRange(2, 4), true)); ExpectEqual(Value(-500), GetDiv(CreateRange(40, 1000), CreateRange(-4, -2), true)); ExpectEqual(Value(), GetDiv(CreateRange(40, 1000), CreateRange(-1, 1), true)); @@ -403,6 +447,8 @@ TEST_F(InductionVarRangeTest, GetDivMin) { } TEST_F(InductionVarRangeTest, GetDivMax) { + ExpectEqual(Value(10), GetDiv(CreateRange(-10, 20), CreateConst(2), false)); + ExpectEqual(Value(5), GetDiv(CreateRange(-10, 20), CreateConst(-2), false)); ExpectEqual(Value(500), GetDiv(CreateRange(40, 1000), CreateRange(2, 4), false)); ExpectEqual(Value(-10), GetDiv(CreateRange(40, 1000), CreateRange(-4, -2), false)); ExpectEqual(Value(), GetDiv(CreateRange(40, 1000), CreateRange(-1, 1), false)); @@ -414,18 +460,6 @@ TEST_F(InductionVarRangeTest, GetDivMax) { ExpectEqual(Value(), GetDiv(CreateRange(-1, 1), CreateRange(-1, 1), false)); } -TEST_F(InductionVarRangeTest, IsConstantRange) { - int32_t min_value; - int32_t max_value; - ASSERT_TRUE(IsConstantRange(CreateConst(12345), &min_value, &max_value)); - EXPECT_EQ(12345, min_value); - EXPECT_EQ(12345, max_value); - ASSERT_TRUE(IsConstantRange(CreateRange(1, 2), &min_value, &max_value)); - EXPECT_EQ(1, min_value); - EXPECT_EQ(2, max_value); - EXPECT_FALSE(IsConstantRange(CreateFetch(x_), &min_value, &max_value)); -} - TEST_F(InductionVarRangeTest, AddValue) { ExpectEqual(Value(110), AddValue(Value(10), Value(100))); ExpectEqual(Value(-5), AddValue(Value(x_, 1, -4), Value(x_, -1, -1))); @@ -459,6 +493,24 @@ TEST_F(InductionVarRangeTest, MulValue) { ExpectEqual(Value(), MulValue(Value(90000), Value(-90000))); // unsafe } +TEST_F(InductionVarRangeTest, MulValueSpecial) { + const int32_t min_value = std::numeric_limits<int32_t>::min(); + const int32_t max_value = std::numeric_limits<int32_t>::max(); + + // Unsafe. + ExpectEqual(Value(), MulValue(Value(min_value), Value(min_value))); + ExpectEqual(Value(), MulValue(Value(min_value), Value(-1))); + ExpectEqual(Value(), MulValue(Value(min_value), Value(max_value))); + ExpectEqual(Value(), MulValue(Value(max_value), Value(max_value))); + + // Safe. + ExpectEqual(Value(min_value), MulValue(Value(min_value), Value(1))); + ExpectEqual(Value(max_value), MulValue(Value(max_value), Value(1))); + ExpectEqual(Value(-max_value), MulValue(Value(max_value), Value(-1))); + ExpectEqual(Value(-1), MulValue(Value(1), Value(-1))); + ExpectEqual(Value(1), MulValue(Value(-1), Value(-1))); +} + TEST_F(InductionVarRangeTest, DivValue) { ExpectEqual(Value(25), DivValue(Value(100), Value(4))); ExpectEqual(Value(), DivValue(Value(x_, 1, -4), Value(x_, 1, -1))); @@ -468,6 +520,23 @@ TEST_F(InductionVarRangeTest, DivValue) { ExpectEqual(Value(), DivValue(Value(1), Value(0))); // unsafe } +TEST_F(InductionVarRangeTest, DivValueSpecial) { + const int32_t min_value = std::numeric_limits<int32_t>::min(); + const int32_t max_value = std::numeric_limits<int32_t>::max(); + + // Unsafe. + ExpectEqual(Value(), DivValue(Value(min_value), Value(-1))); + + // Safe. + ExpectEqual(Value(1), DivValue(Value(min_value), Value(min_value))); + ExpectEqual(Value(1), DivValue(Value(max_value), Value(max_value))); + ExpectEqual(Value(min_value), DivValue(Value(min_value), Value(1))); + ExpectEqual(Value(max_value), DivValue(Value(max_value), Value(1))); + ExpectEqual(Value(-max_value), DivValue(Value(max_value), Value(-1))); + ExpectEqual(Value(-1), DivValue(Value(1), Value(-1))); + ExpectEqual(Value(1), DivValue(Value(-1), Value(-1))); +} + TEST_F(InductionVarRangeTest, MinValue) { ExpectEqual(Value(10), MinValue(Value(10), Value(100))); ExpectEqual(Value(x_, 1, -4), MinValue(Value(x_, 1, -4), Value(x_, 1, -1))); diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 02a1acc240..3f67e481f9 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -562,31 +562,16 @@ void HInliner::CreateDiamondPatternForPolymorphicInline(HInstruction* compare, graph_->reverse_post_order_[++index] = otherwise; graph_->reverse_post_order_[++index] = merge; - // Set the loop information of the newly created blocks. - HLoopInformation* loop_info = cursor_block->GetLoopInformation(); - if (loop_info != nullptr) { - then->SetLoopInformation(cursor_block->GetLoopInformation()); - merge->SetLoopInformation(cursor_block->GetLoopInformation()); - otherwise->SetLoopInformation(cursor_block->GetLoopInformation()); - for (HLoopInformationOutwardIterator loop_it(*cursor_block); - !loop_it.Done(); - loop_it.Advance()) { - loop_it.Current()->Add(then); - loop_it.Current()->Add(merge); - loop_it.Current()->Add(otherwise); - } - // In case the original invoke location was a back edge, we need to update - // the loop to now have the merge block as a back edge. - if (loop_info->IsBackEdge(*original_invoke_block)) { - loop_info->RemoveBackEdge(original_invoke_block); - loop_info->AddBackEdge(merge); - } - } - // Set the try/catch information of the newly created blocks. - then->SetTryCatchInformation(cursor_block->GetTryCatchInformation()); - merge->SetTryCatchInformation(cursor_block->GetTryCatchInformation()); - otherwise->SetTryCatchInformation(cursor_block->GetTryCatchInformation()); + graph_->UpdateLoopAndTryInformationOfNewBlock( + then, original_invoke_block, /* replace_if_back_edge */ false); + graph_->UpdateLoopAndTryInformationOfNewBlock( + otherwise, original_invoke_block, /* replace_if_back_edge */ false); + + // In case the original invoke location was a back edge, we need to update + // the loop to now have the merge block as a back edge. + graph_->UpdateLoopAndTryInformationOfNewBlock( + merge, original_invoke_block, /* replace_if_back_edge */ true); } bool HInliner::TryInlinePolymorphicCallToSameTarget(HInvoke* invoke_instruction, @@ -603,6 +588,10 @@ bool HInliner::TryInlinePolymorphicCallToSameTarget(HInvoke* invoke_instruction, DCHECK(resolved_method != nullptr); ArtMethod* actual_method = nullptr; + size_t method_index = invoke_instruction->IsInvokeVirtual() + ? invoke_instruction->AsInvokeVirtual()->GetVTableIndex() + : invoke_instruction->AsInvokeInterface()->GetImtIndex(); + // Check whether we are actually calling the same method among // the different types seen. for (size_t i = 0; i < InlineCache::kIndividualCacheSize; ++i) { @@ -611,13 +600,18 @@ bool HInliner::TryInlinePolymorphicCallToSameTarget(HInvoke* invoke_instruction, } ArtMethod* new_method = nullptr; if (invoke_instruction->IsInvokeInterface()) { - new_method = ic.GetTypeAt(i)->FindVirtualMethodForInterface( - resolved_method, pointer_size); + new_method = ic.GetTypeAt(i)->GetEmbeddedImTableEntry( + method_index % mirror::Class::kImtSize, pointer_size); + if (new_method->IsRuntimeMethod()) { + // Bail out as soon as we see a conflict trampoline in one of the target's + // interface table. + return false; + } } else { DCHECK(invoke_instruction->IsInvokeVirtual()); - new_method = ic.GetTypeAt(i)->FindVirtualMethodForVirtual( - resolved_method, pointer_size); + new_method = ic.GetTypeAt(i)->GetEmbeddedVTableEntry(method_index, pointer_size); } + DCHECK(new_method != nullptr); if (actual_method == nullptr) { actual_method = new_method; } else if (actual_method != new_method) { @@ -641,18 +635,15 @@ bool HInliner::TryInlinePolymorphicCallToSameTarget(HInvoke* invoke_instruction, HInstanceFieldGet* receiver_class = BuildGetReceiverClass( class_linker, receiver, invoke_instruction->GetDexPc()); - size_t method_offset = invoke_instruction->IsInvokeVirtual() - ? actual_method->GetVtableIndex() - : invoke_instruction->AsInvokeInterface()->GetImtIndex(); - Primitive::Type type = Is64BitInstructionSet(graph_->GetInstructionSet()) ? Primitive::kPrimLong : Primitive::kPrimInt; HClassTableGet* class_table_get = new (graph_->GetArena()) HClassTableGet( receiver_class, type, - invoke_instruction->IsInvokeVirtual() ? HClassTableGet::kVTable : HClassTableGet::kIMTable, - method_offset, + invoke_instruction->IsInvokeVirtual() ? HClassTableGet::TableKind::kVTable + : HClassTableGet::TableKind::kIMTable, + method_index, invoke_instruction->GetDexPc()); HConstant* constant; diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 13d3f752c3..f8a9a94e62 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -93,6 +93,7 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { void SimplifyStringEquals(HInvoke* invoke); void SimplifyCompare(HInvoke* invoke, bool has_zero_op); void SimplifyIsNaN(HInvoke* invoke); + void SimplifyFP2Int(HInvoke* invoke); OptimizingCompilerStats* stats_; bool simplification_occurred_ = false; @@ -1562,26 +1563,71 @@ void InstructionSimplifierVisitor::SimplifyIsNaN(HInvoke* invoke) { invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, condition); } +void InstructionSimplifierVisitor::SimplifyFP2Int(HInvoke* invoke) { + DCHECK(invoke->IsInvokeStaticOrDirect()); + uint32_t dex_pc = invoke->GetDexPc(); + HInstruction* x = invoke->InputAt(0); + Primitive::Type type = x->GetType(); + // Set proper bit pattern for NaN and replace intrinsic with raw version. + HInstruction* nan; + if (type == Primitive::kPrimDouble) { + nan = GetGraph()->GetLongConstant(0x7ff8000000000000L); + invoke->SetIntrinsic(Intrinsics::kDoubleDoubleToRawLongBits, + kNeedsEnvironmentOrCache, + kNoSideEffects, + kNoThrow); + } else { + DCHECK_EQ(type, Primitive::kPrimFloat); + nan = GetGraph()->GetIntConstant(0x7fc00000); + invoke->SetIntrinsic(Intrinsics::kFloatFloatToRawIntBits, + kNeedsEnvironmentOrCache, + kNoSideEffects, + kNoThrow); + } + // Test IsNaN(x), which is the same as x != x. + HCondition* condition = new (GetGraph()->GetArena()) HNotEqual(x, x, dex_pc); + condition->SetBias(ComparisonBias::kLtBias); + invoke->GetBlock()->InsertInstructionBefore(condition, invoke->GetNext()); + // Select between the two. + HInstruction* select = new (GetGraph()->GetArena()) HSelect(condition, nan, invoke, dex_pc); + invoke->GetBlock()->InsertInstructionBefore(select, condition->GetNext()); + invoke->ReplaceWithExceptInReplacementAtIndex(select, 0); // false at index 0 +} + void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) { - if (instruction->GetIntrinsic() == Intrinsics::kStringEquals) { - SimplifyStringEquals(instruction); - } else if (instruction->GetIntrinsic() == Intrinsics::kSystemArrayCopy) { - SimplifySystemArrayCopy(instruction); - } else if (instruction->GetIntrinsic() == Intrinsics::kIntegerRotateRight || - instruction->GetIntrinsic() == Intrinsics::kLongRotateRight) { - SimplifyRotate(instruction, false); - } else if (instruction->GetIntrinsic() == Intrinsics::kIntegerRotateLeft || - instruction->GetIntrinsic() == Intrinsics::kLongRotateLeft) { - SimplifyRotate(instruction, true); - } else if (instruction->GetIntrinsic() == Intrinsics::kIntegerCompare || - instruction->GetIntrinsic() == Intrinsics::kLongCompare) { - SimplifyCompare(instruction, /* is_signum */ false); - } else if (instruction->GetIntrinsic() == Intrinsics::kIntegerSignum || - instruction->GetIntrinsic() == Intrinsics::kLongSignum) { - SimplifyCompare(instruction, /* is_signum */ true); - } else if (instruction->GetIntrinsic() == Intrinsics::kFloatIsNaN || - instruction->GetIntrinsic() == Intrinsics::kDoubleIsNaN) { - SimplifyIsNaN(instruction); + switch (instruction->GetIntrinsic()) { + case Intrinsics::kStringEquals: + SimplifyStringEquals(instruction); + break; + case Intrinsics::kSystemArrayCopy: + SimplifySystemArrayCopy(instruction); + break; + case Intrinsics::kIntegerRotateRight: + case Intrinsics::kLongRotateRight: + SimplifyRotate(instruction, false); + break; + case Intrinsics::kIntegerRotateLeft: + case Intrinsics::kLongRotateLeft: + SimplifyRotate(instruction, true); + break; + case Intrinsics::kIntegerCompare: + case Intrinsics::kLongCompare: + SimplifyCompare(instruction, /* is_signum */ false); + break; + case Intrinsics::kIntegerSignum: + case Intrinsics::kLongSignum: + SimplifyCompare(instruction, /* is_signum */ true); + break; + case Intrinsics::kFloatIsNaN: + case Intrinsics::kDoubleIsNaN: + SimplifyIsNaN(instruction); + break; + case Intrinsics::kFloatFloatToIntBits: + case Intrinsics::kDoubleDoubleToLongBits: + SimplifyFP2Int(instruction); + break; + default: + break; } } diff --git a/compiler/optimizing/instruction_simplifier_arm.cc b/compiler/optimizing/instruction_simplifier_arm.cc new file mode 100644 index 0000000000..db1f9a79aa --- /dev/null +++ b/compiler/optimizing/instruction_simplifier_arm.cc @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2015 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. + */ + +#include "instruction_simplifier_arm.h" +#include "instruction_simplifier_shared.h" + +namespace art { +namespace arm { + +void InstructionSimplifierArmVisitor::VisitMul(HMul* instruction) { + if (TryCombineMultiplyAccumulate(instruction, kArm)) { + RecordSimplification(); + } +} + +} // namespace arm +} // namespace art diff --git a/compiler/optimizing/instruction_simplifier_arm.h b/compiler/optimizing/instruction_simplifier_arm.h new file mode 100644 index 0000000000..379b95d6ae --- /dev/null +++ b/compiler/optimizing/instruction_simplifier_arm.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_ARM_H_ +#define ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_ARM_H_ + +#include "nodes.h" +#include "optimization.h" + +namespace art { +namespace arm { + +class InstructionSimplifierArmVisitor : public HGraphVisitor { + public: + InstructionSimplifierArmVisitor(HGraph* graph, OptimizingCompilerStats* stats) + : HGraphVisitor(graph), stats_(stats) {} + + private: + void RecordSimplification() { + if (stats_ != nullptr) { + stats_->RecordStat(kInstructionSimplificationsArch); + } + } + + void VisitMul(HMul* instruction) OVERRIDE; + + OptimizingCompilerStats* stats_; +}; + + +class InstructionSimplifierArm : public HOptimization { + public: + InstructionSimplifierArm(HGraph* graph, OptimizingCompilerStats* stats) + : HOptimization(graph, "instruction_simplifier_arm", stats) {} + + void Run() OVERRIDE { + InstructionSimplifierArmVisitor visitor(graph_, stats_); + visitor.VisitReversePostOrder(); + } +}; + +} // namespace arm +} // namespace art + +#endif // ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_ARM_H_ diff --git a/compiler/optimizing/instruction_simplifier_arm64.cc b/compiler/optimizing/instruction_simplifier_arm64.cc index 4bcfc54791..c2bbdccc29 100644 --- a/compiler/optimizing/instruction_simplifier_arm64.cc +++ b/compiler/optimizing/instruction_simplifier_arm64.cc @@ -17,6 +17,7 @@ #include "instruction_simplifier_arm64.h" #include "common_arm64.h" +#include "instruction_simplifier_shared.h" #include "mirror/array-inl.h" namespace art { @@ -179,67 +180,53 @@ bool InstructionSimplifierArm64Visitor::TryMergeIntoUsersShifterOperand(HInstruc return true; } -bool InstructionSimplifierArm64Visitor::TrySimpleMultiplyAccumulatePatterns( - HMul* mul, HBinaryOperation* input_binop, HInstruction* input_other) { - DCHECK(Primitive::IsIntOrLongType(mul->GetType())); - DCHECK(input_binop->IsAdd() || input_binop->IsSub()); - DCHECK_NE(input_binop, input_other); - if (!input_binop->HasOnlyOneNonEnvironmentUse()) { - return false; - } - - // Try to interpret patterns like - // a * (b <+/-> 1) - // as - // (a * b) <+/-> a - HInstruction* input_a = input_other; - HInstruction* input_b = nullptr; // Set to a non-null value if we found a pattern to optimize. - HInstruction::InstructionKind op_kind; - - if (input_binop->IsAdd()) { - if ((input_binop->GetConstantRight() != nullptr) && input_binop->GetConstantRight()->IsOne()) { - // Interpret - // a * (b + 1) - // as - // (a * b) + a - input_b = input_binop->GetLeastConstantLeft(); - op_kind = HInstruction::kAdd; - } - } else { - DCHECK(input_binop->IsSub()); - if (input_binop->GetRight()->IsConstant() && - input_binop->GetRight()->AsConstant()->IsMinusOne()) { - // Interpret - // a * (b - (-1)) - // as - // a + (a * b) - input_b = input_binop->GetLeft(); - op_kind = HInstruction::kAdd; - } else if (input_binop->GetLeft()->IsConstant() && - input_binop->GetLeft()->AsConstant()->IsOne()) { - // Interpret - // a * (1 - b) - // as - // a - (a * b) - input_b = input_binop->GetRight(); - op_kind = HInstruction::kSub; +bool InstructionSimplifierArm64Visitor::TryMergeNegatedInput(HBinaryOperation* op) { + DCHECK(op->IsAnd() || op->IsOr() || op->IsXor()) << op->DebugName(); + HInstruction* left = op->GetLeft(); + HInstruction* right = op->GetRight(); + + // Only consider the case where there is exactly one Not, with 2 Not's De + // Morgan's laws should be applied instead. + if (left->IsNot() ^ right->IsNot()) { + HInstruction* hnot = (left->IsNot() ? left : right); + HInstruction* hother = (left->IsNot() ? right : left); + + // Only do the simplification if the Not has only one use and can thus be + // safely removed. Even though ARM64 negated bitwise operations do not have + // an immediate variant (only register), we still do the simplification when + // `hother` is a constant, because it removes an instruction if the constant + // cannot be encoded as an immediate: + // mov r0, #large_constant + // neg r2, r1 + // and r0, r0, r2 + // becomes: + // mov r0, #large_constant + // bic r0, r0, r1 + if (hnot->HasOnlyOneNonEnvironmentUse()) { + // Replace code looking like + // NOT tmp, mask + // AND dst, src, tmp (respectively ORR, EOR) + // with + // BIC dst, src, mask (respectively ORN, EON) + HInstruction* src = hnot->AsNot()->GetInput(); + + HArm64BitwiseNegatedRight* neg_op = new (GetGraph()->GetArena()) + HArm64BitwiseNegatedRight(op->GetType(), op->GetKind(), hother, src, op->GetDexPc()); + + op->GetBlock()->ReplaceAndRemoveInstructionWith(op, neg_op); + hnot->GetBlock()->RemoveInstruction(hnot); + RecordSimplification(); + return true; } } - if (input_b == nullptr) { - // We did not find a pattern we can optimize. - return false; - } - - HArm64MultiplyAccumulate* mulacc = new(GetGraph()->GetArena()) HArm64MultiplyAccumulate( - mul->GetType(), op_kind, input_a, input_a, input_b, mul->GetDexPc()); - - mul->GetBlock()->ReplaceAndRemoveInstructionWith(mul, mulacc); - input_binop->GetBlock()->RemoveInstruction(input_binop); - return false; } +void InstructionSimplifierArm64Visitor::VisitAnd(HAnd* instruction) { + TryMergeNegatedInput(instruction); +} + void InstructionSimplifierArm64Visitor::VisitArrayGet(HArrayGet* instruction) { TryExtractArrayAccessAddress(instruction, instruction->GetArray(), @@ -255,76 +242,13 @@ void InstructionSimplifierArm64Visitor::VisitArraySet(HArraySet* instruction) { } void InstructionSimplifierArm64Visitor::VisitMul(HMul* instruction) { - Primitive::Type type = instruction->GetType(); - if (!Primitive::IsIntOrLongType(type)) { - return; - } - - HInstruction* use = instruction->HasNonEnvironmentUses() - ? instruction->GetUses().GetFirst()->GetUser() - : nullptr; - - if (instruction->HasOnlyOneNonEnvironmentUse() && (use->IsAdd() || use->IsSub())) { - // Replace code looking like - // MUL tmp, x, y - // SUB dst, acc, tmp - // with - // MULSUB dst, acc, x, y - // Note that we do not want to (unconditionally) perform the merge when the - // multiplication has multiple uses and it can be merged in all of them. - // Multiple uses could happen on the same control-flow path, and we would - // then increase the amount of work. In the future we could try to evaluate - // whether all uses are on different control-flow paths (using dominance and - // reverse-dominance information) and only perform the merge when they are. - HInstruction* accumulator = nullptr; - HBinaryOperation* binop = use->AsBinaryOperation(); - HInstruction* binop_left = binop->GetLeft(); - HInstruction* binop_right = binop->GetRight(); - // Be careful after GVN. This should not happen since the `HMul` has only - // one use. - DCHECK_NE(binop_left, binop_right); - if (binop_right == instruction) { - accumulator = binop_left; - } else if (use->IsAdd()) { - DCHECK_EQ(binop_left, instruction); - accumulator = binop_right; - } - - if (accumulator != nullptr) { - HArm64MultiplyAccumulate* mulacc = - new (GetGraph()->GetArena()) HArm64MultiplyAccumulate(type, - binop->GetKind(), - accumulator, - instruction->GetLeft(), - instruction->GetRight()); - - binop->GetBlock()->ReplaceAndRemoveInstructionWith(binop, mulacc); - DCHECK(!instruction->HasUses()); - instruction->GetBlock()->RemoveInstruction(instruction); - RecordSimplification(); - return; - } - } - - // Use multiply accumulate instruction for a few simple patterns. - // We prefer not applying the following transformations if the left and - // right inputs perform the same operation. - // We rely on GVN having squashed the inputs if appropriate. However the - // results are still correct even if that did not happen. - if (instruction->GetLeft() == instruction->GetRight()) { - return; + if (TryCombineMultiplyAccumulate(instruction, kArm64)) { + RecordSimplification(); } +} - HInstruction* left = instruction->GetLeft(); - HInstruction* right = instruction->GetRight(); - if ((right->IsAdd() || right->IsSub()) && - TrySimpleMultiplyAccumulatePatterns(instruction, right->AsBinaryOperation(), left)) { - return; - } - if ((left->IsAdd() || left->IsSub()) && - TrySimpleMultiplyAccumulatePatterns(instruction, left->AsBinaryOperation(), right)) { - return; - } +void InstructionSimplifierArm64Visitor::VisitOr(HOr* instruction) { + TryMergeNegatedInput(instruction); } void InstructionSimplifierArm64Visitor::VisitShl(HShl* instruction) { @@ -359,5 +283,9 @@ void InstructionSimplifierArm64Visitor::VisitUShr(HUShr* instruction) { } } +void InstructionSimplifierArm64Visitor::VisitXor(HXor* instruction) { + TryMergeNegatedInput(instruction); +} + } // namespace arm64 } // namespace art diff --git a/compiler/optimizing/instruction_simplifier_arm64.h b/compiler/optimizing/instruction_simplifier_arm64.h index b7f490bb8c..cf8458713f 100644 --- a/compiler/optimizing/instruction_simplifier_arm64.h +++ b/compiler/optimizing/instruction_simplifier_arm64.h @@ -51,18 +51,21 @@ class InstructionSimplifierArm64Visitor : public HGraphVisitor { return TryMergeIntoShifterOperand(use, bitfield_op, true); } - bool TrySimpleMultiplyAccumulatePatterns(HMul* mul, - HBinaryOperation* input_binop, - HInstruction* input_other); + // For bitwise operations (And/Or/Xor) with a negated input, try to use + // a negated bitwise instruction. + bool TryMergeNegatedInput(HBinaryOperation* op); // HInstruction visitors, sorted alphabetically. + void VisitAnd(HAnd* instruction) OVERRIDE; void VisitArrayGet(HArrayGet* instruction) OVERRIDE; void VisitArraySet(HArraySet* instruction) OVERRIDE; void VisitMul(HMul* instruction) OVERRIDE; + void VisitOr(HOr* instruction) OVERRIDE; void VisitShl(HShl* instruction) OVERRIDE; void VisitShr(HShr* instruction) OVERRIDE; void VisitTypeConversion(HTypeConversion* instruction) OVERRIDE; void VisitUShr(HUShr* instruction) OVERRIDE; + void VisitXor(HXor* instruction) OVERRIDE; OptimizingCompilerStats* stats_; }; diff --git a/compiler/optimizing/instruction_simplifier_shared.cc b/compiler/optimizing/instruction_simplifier_shared.cc new file mode 100644 index 0000000000..45d196fa6d --- /dev/null +++ b/compiler/optimizing/instruction_simplifier_shared.cc @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2015 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. + */ + +#include "instruction_simplifier_shared.h" + +namespace art { + +namespace { + +bool TrySimpleMultiplyAccumulatePatterns(HMul* mul, + HBinaryOperation* input_binop, + HInstruction* input_other) { + DCHECK(Primitive::IsIntOrLongType(mul->GetType())); + DCHECK(input_binop->IsAdd() || input_binop->IsSub()); + DCHECK_NE(input_binop, input_other); + if (!input_binop->HasOnlyOneNonEnvironmentUse()) { + return false; + } + + // Try to interpret patterns like + // a * (b <+/-> 1) + // as + // (a * b) <+/-> a + HInstruction* input_a = input_other; + HInstruction* input_b = nullptr; // Set to a non-null value if we found a pattern to optimize. + HInstruction::InstructionKind op_kind; + + if (input_binop->IsAdd()) { + if ((input_binop->GetConstantRight() != nullptr) && input_binop->GetConstantRight()->IsOne()) { + // Interpret + // a * (b + 1) + // as + // (a * b) + a + input_b = input_binop->GetLeastConstantLeft(); + op_kind = HInstruction::kAdd; + } + } else { + DCHECK(input_binop->IsSub()); + if (input_binop->GetRight()->IsConstant() && + input_binop->GetRight()->AsConstant()->IsMinusOne()) { + // Interpret + // a * (b - (-1)) + // as + // a + (a * b) + input_b = input_binop->GetLeft(); + op_kind = HInstruction::kAdd; + } else if (input_binop->GetLeft()->IsConstant() && + input_binop->GetLeft()->AsConstant()->IsOne()) { + // Interpret + // a * (1 - b) + // as + // a - (a * b) + input_b = input_binop->GetRight(); + op_kind = HInstruction::kSub; + } + } + + if (input_b == nullptr) { + // We did not find a pattern we can optimize. + return false; + } + + ArenaAllocator* arena = mul->GetBlock()->GetGraph()->GetArena(); + HMultiplyAccumulate* mulacc = new(arena) HMultiplyAccumulate( + mul->GetType(), op_kind, input_a, input_a, input_b, mul->GetDexPc()); + + mul->GetBlock()->ReplaceAndRemoveInstructionWith(mul, mulacc); + input_binop->GetBlock()->RemoveInstruction(input_binop); + + return true; +} + +} // namespace + +bool TryCombineMultiplyAccumulate(HMul* mul, InstructionSet isa) { + Primitive::Type type = mul->GetType(); + switch (isa) { + case kArm: + case kThumb2: + if (type != Primitive::kPrimInt) { + return false; + } + break; + case kArm64: + if (!Primitive::IsIntOrLongType(type)) { + return false; + } + break; + default: + return false; + } + + HInstruction* use = mul->HasNonEnvironmentUses() + ? mul->GetUses().GetFirst()->GetUser() + : nullptr; + + ArenaAllocator* arena = mul->GetBlock()->GetGraph()->GetArena(); + + if (mul->HasOnlyOneNonEnvironmentUse()) { + if (use->IsAdd() || use->IsSub()) { + // Replace code looking like + // MUL tmp, x, y + // SUB dst, acc, tmp + // with + // MULSUB dst, acc, x, y + // Note that we do not want to (unconditionally) perform the merge when the + // multiplication has multiple uses and it can be merged in all of them. + // Multiple uses could happen on the same control-flow path, and we would + // then increase the amount of work. In the future we could try to evaluate + // whether all uses are on different control-flow paths (using dominance and + // reverse-dominance information) and only perform the merge when they are. + HInstruction* accumulator = nullptr; + HBinaryOperation* binop = use->AsBinaryOperation(); + HInstruction* binop_left = binop->GetLeft(); + HInstruction* binop_right = binop->GetRight(); + // Be careful after GVN. This should not happen since the `HMul` has only + // one use. + DCHECK_NE(binop_left, binop_right); + if (binop_right == mul) { + accumulator = binop_left; + } else if (use->IsAdd()) { + DCHECK_EQ(binop_left, mul); + accumulator = binop_right; + } + + if (accumulator != nullptr) { + HMultiplyAccumulate* mulacc = + new (arena) HMultiplyAccumulate(type, + binop->GetKind(), + accumulator, + mul->GetLeft(), + mul->GetRight()); + + binop->GetBlock()->ReplaceAndRemoveInstructionWith(binop, mulacc); + DCHECK(!mul->HasUses()); + mul->GetBlock()->RemoveInstruction(mul); + return true; + } + } else if (use->IsNeg() && isa != kArm) { + HMultiplyAccumulate* mulacc = + new (arena) HMultiplyAccumulate(type, + HInstruction::kSub, + mul->GetBlock()->GetGraph()->GetConstant(type, 0), + mul->GetLeft(), + mul->GetRight()); + + use->GetBlock()->ReplaceAndRemoveInstructionWith(use, mulacc); + DCHECK(!mul->HasUses()); + mul->GetBlock()->RemoveInstruction(mul); + return true; + } + } + + // Use multiply accumulate instruction for a few simple patterns. + // We prefer not applying the following transformations if the left and + // right inputs perform the same operation. + // We rely on GVN having squashed the inputs if appropriate. However the + // results are still correct even if that did not happen. + if (mul->GetLeft() == mul->GetRight()) { + return false; + } + + HInstruction* left = mul->GetLeft(); + HInstruction* right = mul->GetRight(); + if ((right->IsAdd() || right->IsSub()) && + TrySimpleMultiplyAccumulatePatterns(mul, right->AsBinaryOperation(), left)) { + return true; + } + if ((left->IsAdd() || left->IsSub()) && + TrySimpleMultiplyAccumulatePatterns(mul, left->AsBinaryOperation(), right)) { + return true; + } + return false; +} + +} // namespace art diff --git a/compiler/optimizing/instruction_simplifier_shared.h b/compiler/optimizing/instruction_simplifier_shared.h new file mode 100644 index 0000000000..9832ecc058 --- /dev/null +++ b/compiler/optimizing/instruction_simplifier_shared.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_SHARED_H_ +#define ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_SHARED_H_ + +#include "nodes.h" + +namespace art { + +bool TryCombineMultiplyAccumulate(HMul* mul, InstructionSet isa); + +} // namespace art + +#endif // ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_SHARED_H_ diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc index 316e86b4c9..3ed0278871 100644 --- a/compiler/optimizing/intrinsics.cc +++ b/compiler/optimizing/intrinsics.cc @@ -130,6 +130,10 @@ static Intrinsics GetIntrinsic(InlineMethod method) { case kIntrinsicFloatCvt: return ((method.d.data & kIntrinsicFlagToFloatingPoint) == 0) ? Intrinsics::kFloatFloatToRawIntBits : Intrinsics::kFloatIntBitsToFloat; + case kIntrinsicFloat2Int: + return Intrinsics::kFloatFloatToIntBits; + case kIntrinsicDouble2Long: + return Intrinsics::kDoubleDoubleToLongBits; // Floating-point tests. case kIntrinsicFloatIsInfinite: diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc index 8cbdcbbcaf..4ce919ee39 100644 --- a/compiler/optimizing/intrinsics_arm.cc +++ b/compiler/optimizing/intrinsics_arm.cc @@ -1909,6 +1909,69 @@ void IntrinsicCodeGeneratorARM::VisitShortReverseBytes(HInvoke* invoke) { __ revsh(out, in); } +void IntrinsicLocationsBuilderARM::VisitStringGetCharsNoCheck(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetInAt(2, Location::RequiresRegister()); + locations->SetInAt(3, Location::RequiresRegister()); + locations->SetInAt(4, Location::RequiresRegister()); + + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); +} + +void IntrinsicCodeGeneratorARM::VisitStringGetCharsNoCheck(HInvoke* invoke) { + ArmAssembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + // Check assumption that sizeof(Char) is 2 (used in scaling below). + const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar); + DCHECK_EQ(char_size, 2u); + + // Location of data in char array buffer. + const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value(); + + // Location of char array data in string. + const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value(); + + // void getCharsNoCheck(int srcBegin, int srcEnd, char[] dst, int dstBegin); + // Since getChars() calls getCharsNoCheck() - we use registers rather than constants. + Register srcObj = locations->InAt(0).AsRegister<Register>(); + Register srcBegin = locations->InAt(1).AsRegister<Register>(); + Register srcEnd = locations->InAt(2).AsRegister<Register>(); + Register dstObj = locations->InAt(3).AsRegister<Register>(); + Register dstBegin = locations->InAt(4).AsRegister<Register>(); + + Register src_ptr = locations->GetTemp(0).AsRegister<Register>(); + Register src_ptr_end = locations->GetTemp(1).AsRegister<Register>(); + Register dst_ptr = locations->GetTemp(2).AsRegister<Register>(); + Register tmp = locations->GetTemp(3).AsRegister<Register>(); + + // src range to copy. + __ add(src_ptr, srcObj, ShifterOperand(value_offset)); + __ add(src_ptr_end, src_ptr, ShifterOperand(srcEnd, LSL, 1)); + __ add(src_ptr, src_ptr, ShifterOperand(srcBegin, LSL, 1)); + + // dst to be copied. + __ add(dst_ptr, dstObj, ShifterOperand(data_offset)); + __ add(dst_ptr, dst_ptr, ShifterOperand(dstBegin, LSL, 1)); + + // Do the copy. + Label loop, done; + __ Bind(&loop); + __ cmp(src_ptr, ShifterOperand(src_ptr_end)); + __ b(&done, EQ); + __ ldrh(tmp, Address(src_ptr, char_size, Address::PostIndex)); + __ strh(tmp, Address(dst_ptr, char_size, Address::PostIndex)); + __ b(&loop); + __ Bind(&done); +} + // Unimplemented intrinsics. #define UNIMPLEMENTED_INTRINSIC(Name) \ @@ -1933,7 +1996,6 @@ UNIMPLEMENTED_INTRINSIC(MathRoundFloat) // Could be done by changing rounding UNIMPLEMENTED_INTRINSIC(UnsafeCASLong) // High register pressure. UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) -UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck) UNIMPLEMENTED_INTRINSIC(FloatIsInfinite) UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite) @@ -1944,6 +2006,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) // Handled as HIR instructions. +UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits) +UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits) UNIMPLEMENTED_INTRINSIC(FloatIsNaN) UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft) diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index b5f15fe22d..4be1695a94 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -1602,6 +1602,69 @@ void IntrinsicCodeGeneratorARM64::VisitMathNextAfter(HInvoke* invoke) { GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickNextAfter); } +void IntrinsicLocationsBuilderARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetInAt(2, Location::RequiresRegister()); + locations->SetInAt(3, Location::RequiresRegister()); + locations->SetInAt(4, Location::RequiresRegister()); + + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); +} + +void IntrinsicCodeGeneratorARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) { + vixl::MacroAssembler* masm = GetVIXLAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + // Check assumption that sizeof(Char) is 2 (used in scaling below). + const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar); + DCHECK_EQ(char_size, 2u); + + // Location of data in char array buffer. + const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value(); + + // Location of char array data in string. + const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value(); + + // void getCharsNoCheck(int srcBegin, int srcEnd, char[] dst, int dstBegin); + // Since getChars() calls getCharsNoCheck() - we use registers rather than constants. + Register srcObj = XRegisterFrom(locations->InAt(0)); + Register srcBegin = XRegisterFrom(locations->InAt(1)); + Register srcEnd = XRegisterFrom(locations->InAt(2)); + Register dstObj = XRegisterFrom(locations->InAt(3)); + Register dstBegin = XRegisterFrom(locations->InAt(4)); + + Register src_ptr = XRegisterFrom(locations->GetTemp(0)); + Register src_ptr_end = XRegisterFrom(locations->GetTemp(1)); + + UseScratchRegisterScope temps(masm); + Register dst_ptr = temps.AcquireX(); + Register tmp = temps.AcquireW(); + + // src range to copy. + __ Add(src_ptr, srcObj, Operand(value_offset)); + __ Add(src_ptr_end, src_ptr, Operand(srcEnd, LSL, 1)); + __ Add(src_ptr, src_ptr, Operand(srcBegin, LSL, 1)); + + // dst to be copied. + __ Add(dst_ptr, dstObj, Operand(data_offset)); + __ Add(dst_ptr, dst_ptr, Operand(dstBegin, LSL, 1)); + + // Do the copy. + vixl::Label loop, done; + __ Bind(&loop); + __ Cmp(src_ptr, src_ptr_end); + __ B(&done, eq); + __ Ldrh(tmp, MemOperand(src_ptr, char_size, vixl::PostIndex)); + __ Strh(tmp, MemOperand(dst_ptr, char_size, vixl::PostIndex)); + __ B(&loop); + __ Bind(&done); +} + // Unimplemented intrinsics. #define UNIMPLEMENTED_INTRINSIC(Name) \ @@ -1615,7 +1678,6 @@ UNIMPLEMENTED_INTRINSIC(LongBitCount) UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) UNIMPLEMENTED_INTRINSIC(SystemArrayCopy) UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) -UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck) UNIMPLEMENTED_INTRINSIC(FloatIsInfinite) UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite) @@ -1626,6 +1688,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) // Handled as HIR instructions. +UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits) +UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits) UNIMPLEMENTED_INTRINSIC(FloatIsNaN) UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft) diff --git a/compiler/optimizing/intrinsics_list.h b/compiler/optimizing/intrinsics_list.h index 88217b308e..e1aea924cf 100644 --- a/compiler/optimizing/intrinsics_list.h +++ b/compiler/optimizing/intrinsics_list.h @@ -23,10 +23,12 @@ #define INTRINSICS_LIST(V) \ V(DoubleDoubleToRawLongBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ + V(DoubleDoubleToLongBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(DoubleIsInfinite, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(DoubleIsNaN, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(DoubleLongBitsToDouble, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(FloatFloatToRawIntBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ + V(FloatFloatToIntBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(FloatIsInfinite, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(FloatIsNaN, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(FloatIntBitsToFloat, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index 2f183c3a62..a737d8100a 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -1772,6 +1772,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) // Handled as HIR instructions. +UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits) +UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits) UNIMPLEMENTED_INTRINSIC(FloatIsNaN) UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) UNIMPLEMENTED_INTRINSIC(IntegerCompare) diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index bd4f5329da..ca2652b74a 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -1824,6 +1824,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) // Handled as HIR instructions. +UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits) +UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits) UNIMPLEMENTED_INTRINSIC(FloatIsNaN) UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) UNIMPLEMENTED_INTRINSIC(IntegerCompare) diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index 260a8773fb..0df4553f56 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -2642,6 +2642,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) // Handled as HIR instructions. +UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits) +UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits) UNIMPLEMENTED_INTRINSIC(FloatIsNaN) UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft) diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 93e8c00e5a..2a9e684d11 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -2719,6 +2719,8 @@ UNIMPLEMENTED_INTRINSIC(FloatIsInfinite) UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite) // Handled as HIR instructions. +UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits) +UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits) UNIMPLEMENTED_INTRINSIC(FloatIsNaN) UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft) diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index f9acb089ee..87b9c022df 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -1877,6 +1877,42 @@ void HGraph::DeleteDeadEmptyBlock(HBasicBlock* block) { blocks_[block->GetBlockId()] = nullptr; } +void HGraph::UpdateLoopAndTryInformationOfNewBlock(HBasicBlock* block, + HBasicBlock* reference, + bool replace_if_back_edge) { + if (block->IsLoopHeader()) { + // Clear the information of which blocks are contained in that loop. Since the + // information is stored as a bit vector based on block ids, we have to update + // it, as those block ids were specific to the callee graph and we are now adding + // these blocks to the caller graph. + block->GetLoopInformation()->ClearAllBlocks(); + } + + // If not already in a loop, update the loop information. + if (!block->IsInLoop()) { + block->SetLoopInformation(reference->GetLoopInformation()); + } + + // If the block is in a loop, update all its outward loops. + HLoopInformation* loop_info = block->GetLoopInformation(); + if (loop_info != nullptr) { + for (HLoopInformationOutwardIterator loop_it(*block); + !loop_it.Done(); + loop_it.Advance()) { + loop_it.Current()->Add(block); + } + if (replace_if_back_edge && loop_info->IsBackEdge(*reference)) { + loop_info->ReplaceBackEdge(reference, block); + } + } + + // Copy TryCatchInformation if `reference` is a try block, not if it is a catch block. + TryCatchInformation* try_catch_info = reference->IsTryBlock() + ? reference->GetTryCatchInformation() + : nullptr; + block->SetTryCatchInformation(try_catch_info); +} + HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { DCHECK(HasExitBlock()) << "Unimplemented scenario"; // Update the environments in this graph to have the invoke's environment @@ -1991,10 +2027,6 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { size_t index_of_at = IndexOfElement(outer_graph->reverse_post_order_, at); MakeRoomFor(&outer_graph->reverse_post_order_, blocks_added, index_of_at); - HLoopInformation* loop_info = at->GetLoopInformation(); - // Copy TryCatchInformation if `at` is a try block, not if it is a catch block. - TryCatchInformation* try_catch_info = at->IsTryBlock() ? at->GetTryCatchInformation() : nullptr; - // Do a reverse post order of the blocks in the callee and do (1), (2), (3) // and (4) to the blocks that apply. for (HReversePostOrderIterator it(*this); !it.Done(); it.Advance()) { @@ -2005,23 +2037,7 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { current->SetGraph(outer_graph); outer_graph->AddBlock(current); outer_graph->reverse_post_order_[++index_of_at] = current; - if (!current->IsInLoop()) { - current->SetLoopInformation(loop_info); - } else if (current->IsLoopHeader()) { - // Clear the information of which blocks are contained in that loop. Since the - // information is stored as a bit vector based on block ids, we have to update - // it, as those block ids were specific to the callee graph and we are now adding - // these blocks to the caller graph. - current->GetLoopInformation()->ClearAllBlocks(); - } - if (current->IsInLoop()) { - for (HLoopInformationOutwardIterator loop_it(*current); - !loop_it.Done(); - loop_it.Advance()) { - loop_it.Current()->Add(current); - } - } - current->SetTryCatchInformation(try_catch_info); + UpdateLoopAndTryInformationOfNewBlock(current, at, /* replace_if_back_edge */ false); } } @@ -2029,20 +2045,9 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { to->SetGraph(outer_graph); outer_graph->AddBlock(to); outer_graph->reverse_post_order_[++index_of_at] = to; - if (loop_info != nullptr) { - if (!to->IsInLoop()) { - to->SetLoopInformation(loop_info); - } - for (HLoopInformationOutwardIterator loop_it(*at); !loop_it.Done(); loop_it.Advance()) { - loop_it.Current()->Add(to); - } - if (loop_info->IsBackEdge(*at)) { - // Only `to` can become a back edge, as the inlined blocks - // are predecessors of `to`. - loop_info->ReplaceBackEdge(at, to); - } - } - to->SetTryCatchInformation(try_catch_info); + // Only `to` can become a back edge, as the inlined blocks + // are predecessors of `to`. + UpdateLoopAndTryInformationOfNewBlock(to, at, /* replace_if_back_edge */ true); } // Update the next instruction id of the outer graph, so that instructions @@ -2157,32 +2162,17 @@ void HGraph::TransformLoopHeaderForBCE(HBasicBlock* header) { reverse_post_order_[index_of_header++] = false_block; reverse_post_order_[index_of_header++] = new_pre_header; - // Fix loop information. - HLoopInformation* loop_info = old_pre_header->GetLoopInformation(); - if (loop_info != nullptr) { - if_block->SetLoopInformation(loop_info); - true_block->SetLoopInformation(loop_info); - false_block->SetLoopInformation(loop_info); - new_pre_header->SetLoopInformation(loop_info); - // Add blocks to all enveloping loops. - for (HLoopInformationOutwardIterator loop_it(*old_pre_header); - !loop_it.Done(); - loop_it.Advance()) { - loop_it.Current()->Add(if_block); - loop_it.Current()->Add(true_block); - loop_it.Current()->Add(false_block); - loop_it.Current()->Add(new_pre_header); - } - } - - // Fix try/catch information. - TryCatchInformation* try_catch_info = old_pre_header->IsTryBlock() - ? old_pre_header->GetTryCatchInformation() - : nullptr; - if_block->SetTryCatchInformation(try_catch_info); - true_block->SetTryCatchInformation(try_catch_info); - false_block->SetTryCatchInformation(try_catch_info); - new_pre_header->SetTryCatchInformation(try_catch_info); + // The pre_header can never be a back edge of a loop. + DCHECK((old_pre_header->GetLoopInformation() == nullptr) || + !old_pre_header->GetLoopInformation()->IsBackEdge(*old_pre_header)); + UpdateLoopAndTryInformationOfNewBlock( + if_block, old_pre_header, /* replace_if_back_edge */ false); + UpdateLoopAndTryInformationOfNewBlock( + true_block, old_pre_header, /* replace_if_back_edge */ false); + UpdateLoopAndTryInformationOfNewBlock( + false_block, old_pre_header, /* replace_if_back_edge */ false); + UpdateLoopAndTryInformationOfNewBlock( + new_pre_header, old_pre_header, /* replace_if_back_edge */ false); } static void CheckAgainstUpperBound(ReferenceTypeInfo rti, ReferenceTypeInfo upper_bound_rti) @@ -2206,7 +2196,8 @@ void HInstruction::SetReferenceTypeInfo(ReferenceTypeInfo rti) { CheckAgainstUpperBound(rti, AsBoundType()->GetUpperBound()); } } - reference_type_info_ = rti; + reference_type_handle_ = rti.GetTypeHandle(); + SetPackedFlag<kFlagReferenceTypeIsExact>(rti.IsExact()); } void HBoundType::SetUpperBound(const ReferenceTypeInfo& upper_bound, bool can_be_null) { @@ -2217,17 +2208,15 @@ void HBoundType::SetUpperBound(const ReferenceTypeInfo& upper_bound, bool can_be CheckAgainstUpperBound(GetReferenceTypeInfo(), upper_bound); } upper_bound_ = upper_bound; - upper_can_be_null_ = can_be_null; + SetPackedFlag<kFlagUpperCanBeNull>(can_be_null); } -ReferenceTypeInfo::ReferenceTypeInfo() : type_handle_(TypeHandle()), is_exact_(false) {} - -ReferenceTypeInfo::ReferenceTypeInfo(TypeHandle type_handle, bool is_exact) - : type_handle_(type_handle), is_exact_(is_exact) { +ReferenceTypeInfo ReferenceTypeInfo::Create(TypeHandle type_handle, bool is_exact) { if (kIsDebugBuild) { ScopedObjectAccess soa(Thread::Current()); DCHECK(IsValidHandle(type_handle)); } + return ReferenceTypeInfo(type_handle, is_exact); } std::ostream& operator<<(std::ostream& os, const ReferenceTypeInfo& rhs) { diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index b355883a72..e3dbe16547 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -154,8 +154,9 @@ class ReferenceTypeInfo : ValueObject { public: typedef Handle<mirror::Class> TypeHandle; - static ReferenceTypeInfo Create(TypeHandle type_handle, bool is_exact) { - // The constructor will check that the type_handle is valid. + static ReferenceTypeInfo Create(TypeHandle type_handle, bool is_exact); + + static ReferenceTypeInfo CreateUnchecked(TypeHandle type_handle, bool is_exact) { return ReferenceTypeInfo(type_handle, is_exact); } @@ -254,8 +255,9 @@ class ReferenceTypeInfo : ValueObject { } private: - ReferenceTypeInfo(); - ReferenceTypeInfo(TypeHandle type_handle, bool is_exact); + ReferenceTypeInfo() : type_handle_(TypeHandle()), is_exact_(false) {} + ReferenceTypeInfo(TypeHandle type_handle, bool is_exact) + : type_handle_(type_handle), is_exact_(is_exact) { } // The class of the object. TypeHandle type_handle_; @@ -351,6 +353,13 @@ class HGraph : public ArenaObject<kArenaAllocGraph> { // and removing the invoke instruction. HInstruction* InlineInto(HGraph* outer_graph, HInvoke* invoke); + // Update the loop and try membership of `block`, which was spawned from `reference`. + // In case `reference` is a back edge, `replace_if_back_edge` notifies whether `block` + // should be the new back edge. + void UpdateLoopAndTryInformationOfNewBlock(HBasicBlock* block, + HBasicBlock* reference, + bool replace_if_back_edge); + // Need to add a couple of blocks to test if the loop body is entered and // put deoptimization instructions, etc. void TransformLoopHeaderForBCE(HBasicBlock* header); @@ -1247,6 +1256,16 @@ class HLoopInformationOutwardIterator : public ValueObject { M(UShr, BinaryOperation) \ M(Xor, BinaryOperation) \ +/* + * Instructions, shared across several (not all) architectures. + */ +#if !defined(ART_ENABLE_CODEGEN_arm) && !defined(ART_ENABLE_CODEGEN_arm64) +#define FOR_EACH_CONCRETE_INSTRUCTION_SHARED(M) +#else +#define FOR_EACH_CONCRETE_INSTRUCTION_SHARED(M) \ + M(MultiplyAccumulate, Instruction) +#endif + #ifndef ART_ENABLE_CODEGEN_arm #define FOR_EACH_CONCRETE_INSTRUCTION_ARM(M) #else @@ -1258,9 +1277,9 @@ class HLoopInformationOutwardIterator : public ValueObject { #define FOR_EACH_CONCRETE_INSTRUCTION_ARM64(M) #else #define FOR_EACH_CONCRETE_INSTRUCTION_ARM64(M) \ + M(Arm64BitwiseNegatedRight, Instruction) \ M(Arm64DataProcWithShifterOp, Instruction) \ - M(Arm64IntermediateAddress, Instruction) \ - M(Arm64MultiplyAccumulate, Instruction) + M(Arm64IntermediateAddress, Instruction) #endif #define FOR_EACH_CONCRETE_INSTRUCTION_MIPS(M) @@ -1281,6 +1300,7 @@ class HLoopInformationOutwardIterator : public ValueObject { #define FOR_EACH_CONCRETE_INSTRUCTION(M) \ FOR_EACH_CONCRETE_INSTRUCTION_COMMON(M) \ + FOR_EACH_CONCRETE_INSTRUCTION_SHARED(M) \ FOR_EACH_CONCRETE_INSTRUCTION_ARM(M) \ FOR_EACH_CONCRETE_INSTRUCTION_ARM64(M) \ FOR_EACH_CONCRETE_INSTRUCTION_MIPS(M) \ @@ -1837,13 +1857,15 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> { dex_pc_(dex_pc), id_(-1), ssa_index_(-1), - emitted_at_use_site_(false), + packed_fields_(0u), environment_(nullptr), locations_(nullptr), live_interval_(nullptr), lifetime_position_(kNoLifetime), side_effects_(side_effects), - reference_type_info_(ReferenceTypeInfo::CreateInvalid()) {} + reference_type_handle_(ReferenceTypeInfo::CreateInvalid().GetTypeHandle()) { + SetPackedFlag<kFlagReferenceTypeIsExact>(ReferenceTypeInfo::CreateInvalid().IsExact()); + } virtual ~HInstruction() {} @@ -1912,7 +1934,8 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> { ReferenceTypeInfo GetReferenceTypeInfo() const { DCHECK_EQ(GetType(), Primitive::kPrimNot); - return reference_type_info_; + return ReferenceTypeInfo::CreateUnchecked(reference_type_handle_, + GetPackedFlag<kFlagReferenceTypeIsExact>());; } void AddUseAt(HInstruction* user, size_t index) { @@ -2101,13 +2124,45 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> { // The caller must ensure that this is safe to do. void RemoveEnvironmentUsers(); - bool IsEmittedAtUseSite() const { return emitted_at_use_site_; } - void MarkEmittedAtUseSite() { emitted_at_use_site_ = true; } + bool IsEmittedAtUseSite() const { return GetPackedFlag<kFlagEmittedAtUseSite>(); } + void MarkEmittedAtUseSite() { SetPackedFlag<kFlagEmittedAtUseSite>(true); } protected: + // If set, the machine code for this instruction is assumed to be generated by + // its users. Used by liveness analysis to compute use positions accordingly. + static constexpr size_t kFlagEmittedAtUseSite = 0u; + static constexpr size_t kFlagReferenceTypeIsExact = kFlagEmittedAtUseSite + 1; + static constexpr size_t kNumberOfGenericPackedBits = kFlagReferenceTypeIsExact + 1; + static constexpr size_t kMaxNumberOfPackedBits = sizeof(uint32_t) * kBitsPerByte; + virtual const HUserRecord<HInstruction*> InputRecordAt(size_t i) const = 0; virtual void SetRawInputRecordAt(size_t index, const HUserRecord<HInstruction*>& input) = 0; + uint32_t GetPackedFields() const { + return packed_fields_; + } + + template <size_t flag> + bool GetPackedFlag() const { + return (packed_fields_ & (1u << flag)) != 0u; + } + + template <size_t flag> + void SetPackedFlag(bool value = true) { + packed_fields_ = (packed_fields_ & ~(1u << flag)) | ((value ? 1u : 0u) << flag); + } + + template <typename BitFieldType> + typename BitFieldType::value_type GetPackedField() const { + return BitFieldType::Decode(packed_fields_); + } + + template <typename BitFieldType> + void SetPackedField(typename BitFieldType::value_type value) { + DCHECK(IsUint<BitFieldType::size>(static_cast<uintptr_t>(value))); + packed_fields_ = BitFieldType::Update(value, packed_fields_); + } + private: void RemoveEnvironmentUser(HUseListNode<HEnvironment*>* use_node) { env_uses_.Remove(use_node); } @@ -2124,9 +2179,8 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> { // When doing liveness analysis, instructions that have uses get an SSA index. int ssa_index_; - // If set, the machine code for this instruction is assumed to be generated by - // its users. Used by liveness analysis to compute use positions accordingly. - bool emitted_at_use_site_; + // Packed fields. + uint32_t packed_fields_; // List of instructions that have this instruction as input. HUseList<HInstruction*> uses_; @@ -2150,8 +2204,10 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> { SideEffects side_effects_; + // The reference handle part of the reference type info. + // The IsExact() flag is stored in packed fields. // TODO: for primitive types this should be marked as invalid. - ReferenceTypeInfo reference_type_info_; + ReferenceTypeInfo::TypeHandle reference_type_handle_; friend class GraphChecker; friend class HBasicBlock; @@ -2277,13 +2333,23 @@ template<intptr_t N> class HExpression : public HTemplateInstruction<N> { public: HExpression<N>(Primitive::Type type, SideEffects side_effects, uint32_t dex_pc) - : HTemplateInstruction<N>(side_effects, dex_pc), type_(type) {} + : HTemplateInstruction<N>(side_effects, dex_pc) { + this->template SetPackedField<TypeField>(type); + } virtual ~HExpression() {} - Primitive::Type GetType() const OVERRIDE { return type_; } + Primitive::Type GetType() const OVERRIDE { + return TypeField::Decode(this->GetPackedFields()); + } protected: - Primitive::Type type_; + static constexpr size_t kFieldType = HInstruction::kNumberOfGenericPackedBits; + static constexpr size_t kFieldTypeSize = + MinimumBitsToStore(static_cast<size_t>(Primitive::kPrimLast)); + static constexpr size_t kNumberOfExpressionPackedBits = kFieldType + kFieldTypeSize; + static_assert(kNumberOfExpressionPackedBits <= HInstruction::kMaxNumberOfPackedBits, + "Too many packed fields."); + using TypeField = BitField<Primitive::Type, kFieldType, kFieldTypeSize>; }; // Represents dex's RETURN_VOID opcode. A HReturnVoid is a control flow @@ -2573,13 +2639,16 @@ class HIf : public HTemplateInstruction<1> { // higher indices in no particular order. class HTryBoundary : public HTemplateInstruction<0> { public: - enum BoundaryKind { + enum class BoundaryKind { kEntry, kExit, + kLast = kExit }; explicit HTryBoundary(BoundaryKind kind, uint32_t dex_pc = kNoDexPc) - : HTemplateInstruction(SideEffects::None(), dex_pc), kind_(kind) {} + : HTemplateInstruction(SideEffects::None(), dex_pc) { + SetPackedField<BoundaryKindField>(kind); + } bool IsControlFlow() const OVERRIDE { return true; } @@ -2605,14 +2674,22 @@ class HTryBoundary : public HTemplateInstruction<0> { } } - bool IsEntry() const { return kind_ == BoundaryKind::kEntry; } + BoundaryKind GetBoundaryKind() const { return GetPackedField<BoundaryKindField>(); } + bool IsEntry() const { return GetBoundaryKind() == BoundaryKind::kEntry; } bool HasSameExceptionHandlersAs(const HTryBoundary& other) const; DECLARE_INSTRUCTION(TryBoundary); private: - const BoundaryKind kind_; + static constexpr size_t kFieldBoundaryKind = kNumberOfGenericPackedBits; + static constexpr size_t kFieldBoundaryKindSize = + MinimumBitsToStore(static_cast<size_t>(BoundaryKind::kLast)); + static constexpr size_t kNumberOfTryBoundaryPackedBits = + kFieldBoundaryKind + kFieldBoundaryKindSize; + static_assert(kNumberOfTryBoundaryPackedBits <= kMaxNumberOfPackedBits, + "Too many packed fields."); + using BoundaryKindField = BitField<BoundaryKind, kFieldBoundaryKind, kFieldBoundaryKindSize>; DISALLOW_COPY_AND_ASSIGN(HTryBoundary); }; @@ -2658,9 +2735,10 @@ class HCurrentMethod : public HExpression<0> { // of a class. class HClassTableGet : public HExpression<1> { public: - enum TableKind { + enum class TableKind { kVTable, kIMTable, + kLast = kIMTable }; HClassTableGet(HInstruction* cls, Primitive::Type type, @@ -2668,26 +2746,33 @@ class HClassTableGet : public HExpression<1> { size_t index, uint32_t dex_pc) : HExpression(type, SideEffects::None(), dex_pc), - index_(index), - table_kind_(kind) { + index_(index) { + SetPackedField<TableKindField>(kind); SetRawInputAt(0, cls); } bool CanBeMoved() const OVERRIDE { return true; } bool InstructionDataEquals(HInstruction* other) const OVERRIDE { return other->AsClassTableGet()->GetIndex() == index_ && - other->AsClassTableGet()->GetTableKind() == table_kind_; + other->AsClassTableGet()->GetPackedFields() == GetPackedFields(); } - TableKind GetTableKind() const { return table_kind_; } + TableKind GetTableKind() const { return GetPackedField<TableKindField>(); } size_t GetIndex() const { return index_; } DECLARE_INSTRUCTION(ClassTableGet); private: + static constexpr size_t kFieldTableKind = kNumberOfExpressionPackedBits; + static constexpr size_t kFieldTableKindSize = + MinimumBitsToStore(static_cast<size_t>(TableKind::kLast)); + static constexpr size_t kNumberOfClassTableGetPackedBits = kFieldTableKind + kFieldTableKindSize; + static_assert(kNumberOfClassTableGetPackedBits <= kMaxNumberOfPackedBits, + "Too many packed fields."); + using TableKindField = BitField<TableKind, kFieldTableKind, kFieldTableKind>; + // The index of the ArtMethod in the table. const size_t index_; - const TableKind table_kind_; DISALLOW_COPY_AND_ASSIGN(HClassTableGet); }; @@ -2854,6 +2939,7 @@ enum class ComparisonBias { kNoBias, // bias is not applicable (i.e. for long operation) kGtBias, // return 1 for NaN comparisons kLtBias, // return -1 for NaN comparisons + kLast = kLtBias }; std::ostream& operator<<(std::ostream& os, const ComparisonBias& rhs); @@ -2861,8 +2947,9 @@ std::ostream& operator<<(std::ostream& os, const ComparisonBias& rhs); class HCondition : public HBinaryOperation { public: HCondition(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc) - : HBinaryOperation(Primitive::kPrimBoolean, first, second, SideEffects::None(), dex_pc), - bias_(ComparisonBias::kNoBias) {} + : HBinaryOperation(Primitive::kPrimBoolean, first, second, SideEffects::None(), dex_pc) { + SetPackedField<ComparisonBiasField>(ComparisonBias::kNoBias); + } // For code generation purposes, returns whether this instruction is just before // `instruction`, and disregard moves in between. @@ -2874,12 +2961,12 @@ class HCondition : public HBinaryOperation { virtual IfCondition GetOppositeCondition() const = 0; - bool IsGtBias() const { return bias_ == ComparisonBias::kGtBias; } - ComparisonBias GetBias() const { return bias_; } - void SetBias(ComparisonBias bias) { bias_ = bias; } + bool IsGtBias() const { return GetBias() == ComparisonBias::kGtBias; } + ComparisonBias GetBias() const { return GetPackedField<ComparisonBiasField>(); } + void SetBias(ComparisonBias bias) { SetPackedField<ComparisonBiasField>(bias); } bool InstructionDataEquals(HInstruction* other) const OVERRIDE { - return bias_ == other->AsCondition()->bias_; + return GetPackedFields() == other->AsCondition()->GetPackedFields(); } bool IsFPConditionTrueIfNaN() const { @@ -2895,6 +2982,16 @@ class HCondition : public HBinaryOperation { } protected: + // Needed if we merge a HCompare into a HCondition. + static constexpr size_t kFieldComparisonBias = kNumberOfExpressionPackedBits; + static constexpr size_t kFieldComparisonBiasSize = + MinimumBitsToStore(static_cast<size_t>(ComparisonBias::kLast)); + static constexpr size_t kNumberOfConditionPackedBits = + kFieldComparisonBias + kFieldComparisonBiasSize; + static_assert(kNumberOfConditionPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + using ComparisonBiasField = + BitField<ComparisonBias, kFieldComparisonBias, kFieldComparisonBiasSize>; + template <typename T> int32_t Compare(T x, T y) const { return x > y ? 1 : (x < y ? -1 : 0); } @@ -2912,9 +3009,6 @@ class HCondition : public HBinaryOperation { } private: - // Needed if we merge a HCompare into a HCondition. - ComparisonBias bias_; - DISALLOW_COPY_AND_ASSIGN(HCondition); }; @@ -3327,8 +3421,8 @@ class HCompare : public HBinaryOperation { first, second, SideEffectsForArchRuntimeCalls(type), - dex_pc), - bias_(bias) { + dex_pc) { + SetPackedField<ComparisonBiasField>(bias); DCHECK_EQ(type, first->GetType()); DCHECK_EQ(type, second->GetType()); } @@ -3363,16 +3457,16 @@ class HCompare : public HBinaryOperation { } bool InstructionDataEquals(HInstruction* other) const OVERRIDE { - return bias_ == other->AsCompare()->bias_; + return GetPackedFields() == other->AsCompare()->GetPackedFields(); } - ComparisonBias GetBias() const { return bias_; } + ComparisonBias GetBias() const { return GetPackedField<ComparisonBiasField>(); } // Does this compare instruction have a "gt bias" (vs an "lt bias")? - // Only meaninfgul for floating-point comparisons. + // Only meaningful for floating-point comparisons. bool IsGtBias() const { DCHECK(Primitive::IsFloatingPointType(InputAt(0)->GetType())) << InputAt(0)->GetType(); - return bias_ == ComparisonBias::kGtBias; + return GetBias() == ComparisonBias::kGtBias; } static SideEffects SideEffectsForArchRuntimeCalls(Primitive::Type type) { @@ -3383,6 +3477,15 @@ class HCompare : public HBinaryOperation { DECLARE_INSTRUCTION(Compare); protected: + static constexpr size_t kFieldComparisonBias = kNumberOfExpressionPackedBits; + static constexpr size_t kFieldComparisonBiasSize = + MinimumBitsToStore(static_cast<size_t>(ComparisonBias::kLast)); + static constexpr size_t kNumberOfComparePackedBits = + kFieldComparisonBias + kFieldComparisonBiasSize; + static_assert(kNumberOfComparePackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + using ComparisonBiasField = + BitField<ComparisonBias, kFieldComparisonBias, kFieldComparisonBiasSize>; + // Return an integer constant containing the result of a comparison evaluated at compile time. HIntConstant* MakeConstantComparison(int32_t value, uint32_t dex_pc) const { DCHECK(value == -1 || value == 0 || value == 1) << value; @@ -3390,8 +3493,6 @@ class HCompare : public HBinaryOperation { } private: - const ComparisonBias bias_; - DISALLOW_COPY_AND_ASSIGN(HCompare); }; @@ -3459,9 +3560,9 @@ class HNewInstance : public HExpression<2> { : HExpression(Primitive::kPrimNot, SideEffects::CanTriggerGC(), dex_pc), type_index_(type_index), dex_file_(dex_file), - can_throw_(can_throw), - finalizable_(finalizable), entrypoint_(entrypoint) { + SetPackedFlag<kFlagCanThrow>(can_throw); + SetPackedFlag<kFlagFinalizable>(finalizable); SetRawInputAt(0, cls); SetRawInputAt(1, current_method); } @@ -3475,9 +3576,9 @@ class HNewInstance : public HExpression<2> { // It may throw when called on type that's not instantiable/accessible. // It can throw OOME. // TODO: distinguish between the two cases so we can for example allow allocation elimination. - bool CanThrow() const OVERRIDE { return can_throw_ || true; } + bool CanThrow() const OVERRIDE { return GetPackedFlag<kFlagCanThrow>() || true; } - bool IsFinalizable() const { return finalizable_; } + bool IsFinalizable() const { return GetPackedFlag<kFlagFinalizable>(); } bool CanBeNull() const OVERRIDE { return false; } @@ -3492,10 +3593,14 @@ class HNewInstance : public HExpression<2> { DECLARE_INSTRUCTION(NewInstance); private: + static constexpr size_t kFlagCanThrow = kNumberOfExpressionPackedBits; + static constexpr size_t kFlagFinalizable = kFlagCanThrow + 1; + static constexpr size_t kNumberOfNewInstancePackedBits = kFlagFinalizable + 1; + static_assert(kNumberOfNewInstancePackedBits <= kMaxNumberOfPackedBits, + "Too many packed fields."); + const uint16_t type_index_; const DexFile& dex_file_; - const bool can_throw_; - const bool finalizable_; QuickEntrypointEnum entrypoint_; DISALLOW_COPY_AND_ASSIGN(HNewInstance); @@ -3545,12 +3650,14 @@ class HInvoke : public HInstruction { // inputs at the end of their list of inputs. uint32_t GetNumberOfArguments() const { return number_of_arguments_; } - Primitive::Type GetType() const OVERRIDE { return return_type_; } + Primitive::Type GetType() const OVERRIDE { return GetPackedField<ReturnTypeField>(); } uint32_t GetDexMethodIndex() const { return dex_method_index_; } const DexFile& GetDexFile() const { return GetEnvironment()->GetDexFile(); } - InvokeType GetOriginalInvokeType() const { return original_invoke_type_; } + InvokeType GetOriginalInvokeType() const { + return GetPackedField<OriginalInvokeTypeField>(); + } Intrinsics GetIntrinsic() const { return intrinsic_; @@ -3565,7 +3672,7 @@ class HInvoke : public HInstruction { return GetEnvironment()->IsFromInlinedInvoke(); } - bool CanThrow() const OVERRIDE { return can_throw_; } + bool CanThrow() const OVERRIDE { return GetPackedFlag<kFlagCanThrow>(); } bool CanBeMoved() const OVERRIDE { return IsIntrinsic(); } @@ -3586,6 +3693,20 @@ class HInvoke : public HInstruction { DECLARE_ABSTRACT_INSTRUCTION(Invoke); protected: + static constexpr size_t kFieldOriginalInvokeType = kNumberOfGenericPackedBits; + static constexpr size_t kFieldOriginalInvokeTypeSize = + MinimumBitsToStore(static_cast<size_t>(kMaxInvokeType)); + static constexpr size_t kFieldReturnType = + kFieldOriginalInvokeType + kFieldOriginalInvokeTypeSize; + static constexpr size_t kFieldReturnTypeSize = + MinimumBitsToStore(static_cast<size_t>(Primitive::kPrimLast)); + static constexpr size_t kFlagCanThrow = kFieldReturnType + kFieldReturnTypeSize; + static constexpr size_t kNumberOfInvokePackedBits = kFlagCanThrow + 1; + static_assert(kNumberOfInvokePackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + using OriginalInvokeTypeField = + BitField<InvokeType, kFieldOriginalInvokeType, kFieldOriginalInvokeTypeSize>; + using ReturnTypeField = BitField<Primitive::Type, kFieldReturnType, kFieldReturnTypeSize>; + HInvoke(ArenaAllocator* arena, uint32_t number_of_arguments, uint32_t number_of_other_inputs, @@ -3598,12 +3719,12 @@ class HInvoke : public HInstruction { number_of_arguments_(number_of_arguments), inputs_(number_of_arguments + number_of_other_inputs, arena->Adapter(kArenaAllocInvokeInputs)), - return_type_(return_type), dex_method_index_(dex_method_index), - original_invoke_type_(original_invoke_type), - can_throw_(true), intrinsic_(Intrinsics::kNone), intrinsic_optimizations_(0) { + SetPackedField<ReturnTypeField>(return_type); + SetPackedField<OriginalInvokeTypeField>(original_invoke_type); + SetPackedFlag<kFlagCanThrow>(true); } const HUserRecord<HInstruction*> InputRecordAt(size_t index) const OVERRIDE { @@ -3614,14 +3735,11 @@ class HInvoke : public HInstruction { inputs_[index] = input; } - void SetCanThrow(bool can_throw) { can_throw_ = can_throw; } + void SetCanThrow(bool can_throw) { SetPackedFlag<kFlagCanThrow>(can_throw); } uint32_t number_of_arguments_; ArenaVector<HUserRecord<HInstruction*>> inputs_; - const Primitive::Type return_type_; const uint32_t dex_method_index_; - const InvokeType original_invoke_type_; - bool can_throw_; Intrinsics intrinsic_; // A magic word holding optimizations for intrinsics. See intrinsics.h. @@ -3662,6 +3780,7 @@ class HInvokeStaticOrDirect : public HInvoke { kNone, // Class already initialized. kExplicit, // Static call having explicit clinit check as last input. kImplicit, // Static call implicitly requiring a clinit check. + kLast = kImplicit }; // Determines how to load the target ArtMethod*. @@ -3682,7 +3801,7 @@ class HInvokeStaticOrDirect : public HInvoke { // the image relocatable or not. kDirectAddressWithFixup, - // Load from resoved methods array in the dex cache using a PC-relative load. + // Load from resolved methods array in the dex cache using a PC-relative load. // Used when we need to use the dex cache, for example for invoke-static that // may cause class initialization (the entry may point to a resolution method), // and we know that we can access the dex cache arrays using a PC-relative load. @@ -3754,10 +3873,11 @@ class HInvokeStaticOrDirect : public HInvoke { dex_pc, method_index, original_invoke_type), - optimized_invoke_type_(optimized_invoke_type), - clinit_check_requirement_(clinit_check_requirement), target_method_(target_method), - dispatch_info_(dispatch_info) { } + dispatch_info_(dispatch_info) { + SetPackedField<OptimizedInvokeTypeField>(optimized_invoke_type); + SetPackedField<ClinitCheckRequirementField>(clinit_check_requirement); + } void SetDispatchInfo(const DispatchInfo& dispatch_info) { bool had_current_method_input = HasCurrentMethodInput(); @@ -3789,7 +3909,7 @@ class HInvokeStaticOrDirect : public HInvoke { } bool CanBeNull() const OVERRIDE { - return return_type_ == Primitive::kPrimNot && !IsStringInit(); + return GetPackedField<ReturnTypeField>() == Primitive::kPrimNot && !IsStringInit(); } // Get the index of the special input, if any. @@ -3800,9 +3920,12 @@ class HInvokeStaticOrDirect : public HInvoke { uint32_t GetSpecialInputIndex() const { return GetNumberOfArguments(); } bool HasSpecialInput() const { return GetNumberOfArguments() != InputCount(); } - InvokeType GetOptimizedInvokeType() const { return optimized_invoke_type_; } + InvokeType GetOptimizedInvokeType() const { + return GetPackedField<OptimizedInvokeTypeField>(); + } + void SetOptimizedInvokeType(InvokeType invoke_type) { - optimized_invoke_type_ = invoke_type; + SetPackedField<OptimizedInvokeTypeField>(invoke_type); } MethodLoadKind GetMethodLoadKind() const { return dispatch_info_.method_load_kind; } @@ -3849,7 +3972,9 @@ class HInvokeStaticOrDirect : public HInvoke { return dispatch_info_.direct_code_ptr; } - ClinitCheckRequirement GetClinitCheckRequirement() const { return clinit_check_requirement_; } + ClinitCheckRequirement GetClinitCheckRequirement() const { + return GetPackedField<ClinitCheckRequirementField>(); + } // Is this instruction a call to a static method? bool IsStatic() const { @@ -3867,7 +3992,7 @@ class HInvokeStaticOrDirect : public HInvoke { DCHECK(last_input->IsLoadClass() || last_input->IsClinitCheck()) << last_input->DebugName(); RemoveAsUserOfInput(last_input_index); inputs_.pop_back(); - clinit_check_requirement_ = new_requirement; + SetPackedField<ClinitCheckRequirementField>(new_requirement); DCHECK(!IsStaticWithExplicitClinitCheck()); } @@ -3883,13 +4008,13 @@ class HInvokeStaticOrDirect : public HInvoke { // Is this a call to a static method whose declaring class has an // explicit initialization check in the graph? bool IsStaticWithExplicitClinitCheck() const { - return IsStatic() && (clinit_check_requirement_ == ClinitCheckRequirement::kExplicit); + return IsStatic() && (GetClinitCheckRequirement() == ClinitCheckRequirement::kExplicit); } // Is this a call to a static method whose declaring class has an // implicit intialization check requirement? bool IsStaticWithImplicitClinitCheck() const { - return IsStatic() && (clinit_check_requirement_ == ClinitCheckRequirement::kImplicit); + return IsStatic() && (GetClinitCheckRequirement() == ClinitCheckRequirement::kImplicit); } // Does this method load kind need the current method as an input? @@ -3918,8 +4043,23 @@ class HInvokeStaticOrDirect : public HInvoke { void RemoveInputAt(size_t index); private: - InvokeType optimized_invoke_type_; - ClinitCheckRequirement clinit_check_requirement_; + static constexpr size_t kFieldOptimizedInvokeType = kNumberOfInvokePackedBits; + static constexpr size_t kFieldOptimizedInvokeTypeSize = + MinimumBitsToStore(static_cast<size_t>(kMaxInvokeType)); + static constexpr size_t kFieldClinitCheckRequirement = + kFieldOptimizedInvokeType + kFieldOptimizedInvokeTypeSize; + static constexpr size_t kFieldClinitCheckRequirementSize = + MinimumBitsToStore(static_cast<size_t>(ClinitCheckRequirement::kLast)); + static constexpr size_t kNumberOfInvokeStaticOrDirectPackedBits = + kFieldClinitCheckRequirement + kFieldClinitCheckRequirementSize; + static_assert(kNumberOfInvokeStaticOrDirectPackedBits <= kMaxNumberOfPackedBits, + "Too many packed fields."); + using OptimizedInvokeTypeField = + BitField<InvokeType, kFieldOptimizedInvokeType, kFieldOptimizedInvokeTypeSize>; + using ClinitCheckRequirementField = BitField<ClinitCheckRequirement, + kFieldClinitCheckRequirement, + kFieldClinitCheckRequirementSize>; + // The target method may refer to different dex file or method index than the original // invoke. This happens for sharpened calls and for calls where a method was redeclared // in derived class to increase visibility. @@ -4585,32 +4725,35 @@ class HParameterValue : public HExpression<0> { : HExpression(parameter_type, SideEffects::None(), kNoDexPc), dex_file_(dex_file), type_index_(type_index), - index_(index), - is_this_(is_this), - can_be_null_(!is_this) {} + index_(index) { + SetPackedFlag<kFlagIsThis>(is_this); + SetPackedFlag<kFlagCanBeNull>(!is_this); + } const DexFile& GetDexFile() const { return dex_file_; } uint16_t GetTypeIndex() const { return type_index_; } uint8_t GetIndex() const { return index_; } - bool IsThis() const { return is_this_; } + bool IsThis() const { return GetPackedFlag<kFlagIsThis>(); } - bool CanBeNull() const OVERRIDE { return can_be_null_; } - void SetCanBeNull(bool can_be_null) { can_be_null_ = can_be_null; } + bool CanBeNull() const OVERRIDE { return GetPackedFlag<kFlagCanBeNull>(); } + void SetCanBeNull(bool can_be_null) { SetPackedFlag<kFlagCanBeNull>(can_be_null); } DECLARE_INSTRUCTION(ParameterValue); private: + // Whether or not the parameter value corresponds to 'this' argument. + static constexpr size_t kFlagIsThis = kNumberOfExpressionPackedBits; + static constexpr size_t kFlagCanBeNull = kFlagIsThis + 1; + static constexpr size_t kNumberOfParameterValuePackedBits = kFlagCanBeNull + 1; + static_assert(kNumberOfParameterValuePackedBits <= kMaxNumberOfPackedBits, + "Too many packed fields."); + const DexFile& dex_file_; const uint16_t type_index_; // The index of this parameter in the parameters list. Must be less // than HGraph::number_of_in_vregs_. const uint8_t index_; - // Whether or not the parameter value corresponds to 'this' argument. - const bool is_this_; - - bool can_be_null_; - DISALLOW_COPY_AND_ASSIGN(HParameterValue); }; @@ -4735,14 +4878,14 @@ class HPhi : public HInstruction { uint32_t dex_pc = kNoDexPc) : HInstruction(SideEffects::None(), dex_pc), inputs_(number_of_inputs, arena->Adapter(kArenaAllocPhiInputs)), - reg_number_(reg_number), - type_(ToPhiType(type)), - // Phis are constructed live and marked dead if conflicting or unused. - // Individual steps of SsaBuilder should assume that if a phi has been - // marked dead, it can be ignored and will be removed by SsaPhiElimination. - is_live_(true), - can_be_null_(true) { - DCHECK_NE(type_, Primitive::kPrimVoid); + reg_number_(reg_number) { + SetPackedField<TypeField>(ToPhiType(type)); + DCHECK_NE(GetType(), Primitive::kPrimVoid); + // Phis are constructed live and marked dead if conflicting or unused. + // Individual steps of SsaBuilder should assume that if a phi has been + // marked dead, it can be ignored and will be removed by SsaPhiElimination. + SetPackedFlag<kFlagIsLive>(true); + SetPackedFlag<kFlagCanBeNull>(true); } // Returns a type equivalent to the given `type`, but that a `HPhi` can hold. @@ -4765,27 +4908,27 @@ class HPhi : public HInstruction { void AddInput(HInstruction* input); void RemoveInputAt(size_t index); - Primitive::Type GetType() const OVERRIDE { return type_; } + Primitive::Type GetType() const OVERRIDE { return GetPackedField<TypeField>(); } void SetType(Primitive::Type new_type) { // Make sure that only valid type changes occur. The following are allowed: // (1) int -> float/ref (primitive type propagation), // (2) long -> double (primitive type propagation). - DCHECK(type_ == new_type || - (type_ == Primitive::kPrimInt && new_type == Primitive::kPrimFloat) || - (type_ == Primitive::kPrimInt && new_type == Primitive::kPrimNot) || - (type_ == Primitive::kPrimLong && new_type == Primitive::kPrimDouble)); - type_ = new_type; + DCHECK(GetType() == new_type || + (GetType() == Primitive::kPrimInt && new_type == Primitive::kPrimFloat) || + (GetType() == Primitive::kPrimInt && new_type == Primitive::kPrimNot) || + (GetType() == Primitive::kPrimLong && new_type == Primitive::kPrimDouble)); + SetPackedField<TypeField>(new_type); } - bool CanBeNull() const OVERRIDE { return can_be_null_; } - void SetCanBeNull(bool can_be_null) { can_be_null_ = can_be_null; } + bool CanBeNull() const OVERRIDE { return GetPackedFlag<kFlagCanBeNull>(); } + void SetCanBeNull(bool can_be_null) { SetPackedFlag<kFlagCanBeNull>(can_be_null); } uint32_t GetRegNumber() const { return reg_number_; } - void SetDead() { is_live_ = false; } - void SetLive() { is_live_ = true; } - bool IsDead() const { return !is_live_; } - bool IsLive() const { return is_live_; } + void SetDead() { SetPackedFlag<kFlagIsLive>(false); } + void SetLive() { SetPackedFlag<kFlagIsLive>(true); } + bool IsDead() const { return !IsLive(); } + bool IsLive() const { return GetPackedFlag<kFlagIsLive>(); } bool IsVRegEquivalentOf(HInstruction* other) const { return other != nullptr @@ -4820,11 +4963,17 @@ class HPhi : public HInstruction { } private: + static constexpr size_t kFieldType = HInstruction::kNumberOfGenericPackedBits; + static constexpr size_t kFieldTypeSize = + MinimumBitsToStore(static_cast<size_t>(Primitive::kPrimLast)); + static constexpr size_t kFlagIsLive = kFieldType + kFieldTypeSize; + static constexpr size_t kFlagCanBeNull = kFlagIsLive + 1; + static constexpr size_t kNumberOfPhiPackedBits = kFlagCanBeNull + 1; + static_assert(kNumberOfPhiPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + using TypeField = BitField<Primitive::Type, kFieldType, kFieldTypeSize>; + ArenaVector<HUserRecord<HInstruction*> > inputs_; const uint32_t reg_number_; - Primitive::Type type_; - bool is_live_; - bool can_be_null_; DISALLOW_COPY_AND_ASSIGN(HPhi); }; @@ -4963,8 +5112,8 @@ class HInstanceFieldSet : public HTemplateInstruction<2> { field_idx, declaring_class_def_index, dex_file, - dex_cache), - value_can_be_null_(true) { + dex_cache) { + SetPackedFlag<kFlagValueCanBeNull>(true); SetRawInputAt(0, object); SetRawInputAt(1, value); } @@ -4978,14 +5127,18 @@ class HInstanceFieldSet : public HTemplateInstruction<2> { Primitive::Type GetFieldType() const { return field_info_.GetFieldType(); } bool IsVolatile() const { return field_info_.IsVolatile(); } HInstruction* GetValue() const { return InputAt(1); } - bool GetValueCanBeNull() const { return value_can_be_null_; } - void ClearValueCanBeNull() { value_can_be_null_ = false; } + bool GetValueCanBeNull() const { return GetPackedFlag<kFlagValueCanBeNull>(); } + void ClearValueCanBeNull() { SetPackedFlag<kFlagValueCanBeNull>(false); } DECLARE_INSTRUCTION(InstanceFieldSet); private: + static constexpr size_t kFlagValueCanBeNull = kNumberOfGenericPackedBits; + static constexpr size_t kNumberOfInstanceFieldSetPackedBits = kFlagValueCanBeNull + 1; + static_assert(kNumberOfInstanceFieldSetPackedBits <= kMaxNumberOfPackedBits, + "Too many packed fields."); + const FieldInfo field_info_; - bool value_can_be_null_; DISALLOW_COPY_AND_ASSIGN(HInstanceFieldSet); }; @@ -5054,11 +5207,11 @@ class HArraySet : public HTemplateInstruction<3> { SideEffects::ArrayWriteOfType(expected_component_type).Union( SideEffectsForArchRuntimeCalls(value->GetType())).Union( additional_side_effects), - dex_pc), - expected_component_type_(expected_component_type), - needs_type_check_(value->GetType() == Primitive::kPrimNot), - value_can_be_null_(true), - static_type_of_array_is_object_array_(false) { + dex_pc) { + SetPackedField<ExpectedComponentTypeField>(expected_component_type); + SetPackedFlag<kFlagNeedsTypeCheck>(value->GetType() == Primitive::kPrimNot); + SetPackedFlag<kFlagValueCanBeNull>(true); + SetPackedFlag<kFlagStaticTypeOfArrayIsObjectArray>(false); SetRawInputAt(0, array); SetRawInputAt(1, index); SetRawInputAt(2, value); @@ -5066,11 +5219,11 @@ class HArraySet : public HTemplateInstruction<3> { bool NeedsEnvironment() const OVERRIDE { // We call a runtime method to throw ArrayStoreException. - return needs_type_check_; + return NeedsTypeCheck(); } // Can throw ArrayStoreException. - bool CanThrow() const OVERRIDE { return needs_type_check_; } + bool CanThrow() const OVERRIDE { return NeedsTypeCheck(); } bool CanDoImplicitNullCheckOn(HInstruction* obj ATTRIBUTE_UNUSED) const OVERRIDE { // TODO: Same as for ArrayGet. @@ -5078,20 +5231,22 @@ class HArraySet : public HTemplateInstruction<3> { } void ClearNeedsTypeCheck() { - needs_type_check_ = false; + SetPackedFlag<kFlagNeedsTypeCheck>(false); } void ClearValueCanBeNull() { - value_can_be_null_ = false; + SetPackedFlag<kFlagValueCanBeNull>(false); } void SetStaticTypeOfArrayIsObjectArray() { - static_type_of_array_is_object_array_ = true; + SetPackedFlag<kFlagStaticTypeOfArrayIsObjectArray>(true); } - bool GetValueCanBeNull() const { return value_can_be_null_; } - bool NeedsTypeCheck() const { return needs_type_check_; } - bool StaticTypeOfArrayIsObjectArray() const { return static_type_of_array_is_object_array_; } + bool GetValueCanBeNull() const { return GetPackedFlag<kFlagValueCanBeNull>(); } + bool NeedsTypeCheck() const { return GetPackedFlag<kFlagNeedsTypeCheck>(); } + bool StaticTypeOfArrayIsObjectArray() const { + return GetPackedFlag<kFlagStaticTypeOfArrayIsObjectArray>(); + } HInstruction* GetArray() const { return InputAt(0); } HInstruction* GetIndex() const { return InputAt(1); } @@ -5105,11 +5260,11 @@ class HArraySet : public HTemplateInstruction<3> { Primitive::Type value_type = GetValue()->GetType(); return ((value_type == Primitive::kPrimFloat) || (value_type == Primitive::kPrimDouble)) ? value_type - : expected_component_type_; + : GetRawExpectedComponentType(); } Primitive::Type GetRawExpectedComponentType() const { - return expected_component_type_; + return GetPackedField<ExpectedComponentTypeField>(); } static SideEffects SideEffectsForArchRuntimeCalls(Primitive::Type value_type) { @@ -5119,12 +5274,20 @@ class HArraySet : public HTemplateInstruction<3> { DECLARE_INSTRUCTION(ArraySet); private: - const Primitive::Type expected_component_type_; - bool needs_type_check_; - bool value_can_be_null_; + static constexpr size_t kFieldExpectedComponentType = kNumberOfGenericPackedBits; + static constexpr size_t kFieldExpectedComponentTypeSize = + MinimumBitsToStore(static_cast<size_t>(Primitive::kPrimLast)); + static constexpr size_t kFlagNeedsTypeCheck = + kFieldExpectedComponentType + kFieldExpectedComponentTypeSize; + static constexpr size_t kFlagValueCanBeNull = kFlagNeedsTypeCheck + 1; // Cached information for the reference_type_info_ so that codegen // does not need to inspect the static type. - bool static_type_of_array_is_object_array_; + static constexpr size_t kFlagStaticTypeOfArrayIsObjectArray = kFlagValueCanBeNull + 1; + static constexpr size_t kNumberOfArraySetPackedBits = + kFlagStaticTypeOfArrayIsObjectArray + 1; + static_assert(kNumberOfArraySetPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + using ExpectedComponentTypeField = + BitField<Primitive::Type, kFieldExpectedComponentType, kFieldExpectedComponentTypeSize>; DISALLOW_COPY_AND_ASSIGN(HArraySet); }; @@ -5234,14 +5397,15 @@ class HLoadClass : public HExpression<1> { : HExpression(Primitive::kPrimNot, SideEffectsForArchRuntimeCalls(), dex_pc), type_index_(type_index), dex_file_(dex_file), - is_referrers_class_(is_referrers_class), - generate_clinit_check_(false), - needs_access_check_(needs_access_check), - is_in_dex_cache_(is_in_dex_cache), loaded_class_rti_(ReferenceTypeInfo::CreateInvalid()) { // Referrers class should not need access check. We never inline unverified // methods so we can't possibly end up in this situation. - DCHECK(!is_referrers_class_ || !needs_access_check_); + DCHECK(!is_referrers_class || !needs_access_check); + + SetPackedFlag<kFlagIsReferrersClass>(is_referrers_class); + SetPackedFlag<kFlagNeedsAccessCheck>(needs_access_check); + SetPackedFlag<kFlagIsInDexCache>(is_in_dex_cache); + SetPackedFlag<kFlagGenerateClInitCheck>(false); SetRawInputAt(0, current_method); } @@ -5252,39 +5416,31 @@ class HLoadClass : public HExpression<1> { // Whether or not we need to generate the clinit check is processed in // prepare_for_register_allocator based on existing HInvokes and HClinitChecks. return other->AsLoadClass()->type_index_ == type_index_ && - other->AsLoadClass()->needs_access_check_ == needs_access_check_; + other->AsLoadClass()->GetPackedFields() == GetPackedFields(); } size_t ComputeHashCode() const OVERRIDE { return type_index_; } uint16_t GetTypeIndex() const { return type_index_; } - bool IsReferrersClass() const { return is_referrers_class_; } bool CanBeNull() const OVERRIDE { return false; } bool NeedsEnvironment() const OVERRIDE { return CanCallRuntime(); } - bool MustGenerateClinitCheck() const { - return generate_clinit_check_; - } - void SetMustGenerateClinitCheck(bool generate_clinit_check) { // The entrypoint the code generator is going to call does not do // clinit of the class. DCHECK(!NeedsAccessCheck()); - generate_clinit_check_ = generate_clinit_check; + SetPackedFlag<kFlagGenerateClInitCheck>(generate_clinit_check); } bool CanCallRuntime() const { return MustGenerateClinitCheck() || - (!is_referrers_class_ && !is_in_dex_cache_) || - needs_access_check_; + (!IsReferrersClass() && !IsInDexCache()) || + NeedsAccessCheck(); } - bool NeedsAccessCheck() const { - return needs_access_check_; - } bool CanThrow() const OVERRIDE { return CanCallRuntime(); @@ -5302,25 +5458,31 @@ class HLoadClass : public HExpression<1> { const DexFile& GetDexFile() { return dex_file_; } - bool NeedsDexCacheOfDeclaringClass() const OVERRIDE { return !is_referrers_class_; } + bool NeedsDexCacheOfDeclaringClass() const OVERRIDE { return !IsReferrersClass(); } static SideEffects SideEffectsForArchRuntimeCalls() { return SideEffects::CanTriggerGC(); } - bool IsInDexCache() const { return is_in_dex_cache_; } + bool IsReferrersClass() const { return GetPackedFlag<kFlagIsReferrersClass>(); } + bool NeedsAccessCheck() const { return GetPackedFlag<kFlagNeedsAccessCheck>(); } + bool IsInDexCache() const { return GetPackedFlag<kFlagIsInDexCache>(); } + bool MustGenerateClinitCheck() const { return GetPackedFlag<kFlagGenerateClInitCheck>(); } DECLARE_INSTRUCTION(LoadClass); private: - const uint16_t type_index_; - const DexFile& dex_file_; - const bool is_referrers_class_; + static constexpr size_t kFlagIsReferrersClass = kNumberOfExpressionPackedBits; + static constexpr size_t kFlagNeedsAccessCheck = kFlagIsReferrersClass + 1; + static constexpr size_t kFlagIsInDexCache = kFlagNeedsAccessCheck + 1; // Whether this instruction must generate the initialization check. // Used for code generation. - bool generate_clinit_check_; - const bool needs_access_check_; - const bool is_in_dex_cache_; + static constexpr size_t kFlagGenerateClInitCheck = kFlagIsInDexCache + 1; + static constexpr size_t kNumberOfLoadClassPackedBits = kFlagGenerateClInitCheck + 1; + static_assert(kNumberOfLoadClassPackedBits < kMaxNumberOfPackedBits, "Too many packed fields."); + + const uint16_t type_index_; + const DexFile& dex_file_; ReferenceTypeInfo loaded_class_rti_; @@ -5334,8 +5496,8 @@ class HLoadString : public HExpression<1> { uint32_t dex_pc, bool is_in_dex_cache) : HExpression(Primitive::kPrimNot, SideEffectsForArchRuntimeCalls(), dex_pc), - string_index_(string_index), - is_in_dex_cache_(is_in_dex_cache) { + string_index_(string_index) { + SetPackedFlag<kFlagIsInDexCache>(is_in_dex_cache); SetRawInputAt(0, current_method); } @@ -5354,18 +5516,22 @@ class HLoadString : public HExpression<1> { bool NeedsDexCacheOfDeclaringClass() const OVERRIDE { return true; } bool CanBeNull() const OVERRIDE { return false; } - bool IsInDexCache() const { return is_in_dex_cache_; } bool CanThrow() const OVERRIDE { return !IsInDexCache(); } static SideEffects SideEffectsForArchRuntimeCalls() { return SideEffects::CanTriggerGC(); } + bool IsInDexCache() const { return GetPackedFlag<kFlagIsInDexCache>(); } + DECLARE_INSTRUCTION(LoadString); private: + static constexpr size_t kFlagIsInDexCache = kNumberOfExpressionPackedBits; + static constexpr size_t kNumberOfLoadStringPackedBits = kFlagIsInDexCache + 1; + static_assert(kNumberOfLoadStringPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + const uint32_t string_index_; - const bool is_in_dex_cache_; DISALLOW_COPY_AND_ASSIGN(HLoadString); }; @@ -5472,8 +5638,8 @@ class HStaticFieldSet : public HTemplateInstruction<2> { field_idx, declaring_class_def_index, dex_file, - dex_cache), - value_can_be_null_(true) { + dex_cache) { + SetPackedFlag<kFlagValueCanBeNull>(true); SetRawInputAt(0, cls); SetRawInputAt(1, value); } @@ -5484,14 +5650,18 @@ class HStaticFieldSet : public HTemplateInstruction<2> { bool IsVolatile() const { return field_info_.IsVolatile(); } HInstruction* GetValue() const { return InputAt(1); } - bool GetValueCanBeNull() const { return value_can_be_null_; } - void ClearValueCanBeNull() { value_can_be_null_ = false; } + bool GetValueCanBeNull() const { return GetPackedFlag<kFlagValueCanBeNull>(); } + void ClearValueCanBeNull() { SetPackedFlag<kFlagValueCanBeNull>(false); } DECLARE_INSTRUCTION(StaticFieldSet); private: + static constexpr size_t kFlagValueCanBeNull = kNumberOfGenericPackedBits; + static constexpr size_t kNumberOfStaticFieldSetPackedBits = kFlagValueCanBeNull + 1; + static_assert(kNumberOfStaticFieldSetPackedBits <= kMaxNumberOfPackedBits, + "Too many packed fields."); + const FieldInfo field_info_; - bool value_can_be_null_; DISALLOW_COPY_AND_ASSIGN(HStaticFieldSet); }; @@ -5529,8 +5699,8 @@ class HUnresolvedInstanceFieldSet : public HTemplateInstruction<2> { uint32_t field_index, uint32_t dex_pc) : HTemplateInstruction(SideEffects::AllExceptGCDependency(), dex_pc), - field_type_(field_type), field_index_(field_index) { + SetPackedField<FieldTypeField>(field_type); DCHECK_EQ(field_type, value->GetType()); SetRawInputAt(0, obj); SetRawInputAt(1, value); @@ -5539,13 +5709,21 @@ class HUnresolvedInstanceFieldSet : public HTemplateInstruction<2> { bool NeedsEnvironment() const OVERRIDE { return true; } bool CanThrow() const OVERRIDE { return true; } - Primitive::Type GetFieldType() const { return field_type_; } + Primitive::Type GetFieldType() const { return GetPackedField<FieldTypeField>(); } uint32_t GetFieldIndex() const { return field_index_; } DECLARE_INSTRUCTION(UnresolvedInstanceFieldSet); private: - const Primitive::Type field_type_; + static constexpr size_t kFieldFieldType = HInstruction::kNumberOfGenericPackedBits; + static constexpr size_t kFieldFieldTypeSize = + MinimumBitsToStore(static_cast<size_t>(Primitive::kPrimLast)); + static constexpr size_t kNumberOfUnresolvedStaticFieldSetPackedBits = + kFieldFieldType + kFieldFieldTypeSize; + static_assert(kNumberOfUnresolvedStaticFieldSetPackedBits <= HInstruction::kMaxNumberOfPackedBits, + "Too many packed fields."); + using FieldTypeField = BitField<Primitive::Type, kFieldFieldType, kFieldFieldTypeSize>; + const uint32_t field_index_; DISALLOW_COPY_AND_ASSIGN(HUnresolvedInstanceFieldSet); @@ -5581,8 +5759,8 @@ class HUnresolvedStaticFieldSet : public HTemplateInstruction<1> { uint32_t field_index, uint32_t dex_pc) : HTemplateInstruction(SideEffects::AllExceptGCDependency(), dex_pc), - field_type_(field_type), field_index_(field_index) { + SetPackedField<FieldTypeField>(field_type); DCHECK_EQ(field_type, value->GetType()); SetRawInputAt(0, value); } @@ -5590,13 +5768,21 @@ class HUnresolvedStaticFieldSet : public HTemplateInstruction<1> { bool NeedsEnvironment() const OVERRIDE { return true; } bool CanThrow() const OVERRIDE { return true; } - Primitive::Type GetFieldType() const { return field_type_; } + Primitive::Type GetFieldType() const { return GetPackedField<FieldTypeField>(); } uint32_t GetFieldIndex() const { return field_index_; } DECLARE_INSTRUCTION(UnresolvedStaticFieldSet); private: - const Primitive::Type field_type_; + static constexpr size_t kFieldFieldType = HInstruction::kNumberOfGenericPackedBits; + static constexpr size_t kFieldFieldTypeSize = + MinimumBitsToStore(static_cast<size_t>(Primitive::kPrimLast)); + static constexpr size_t kNumberOfUnresolvedStaticFieldSetPackedBits = + kFieldFieldType + kFieldFieldTypeSize; + static_assert(kNumberOfUnresolvedStaticFieldSetPackedBits <= HInstruction::kMaxNumberOfPackedBits, + "Too many packed fields."); + using FieldTypeField = BitField<Primitive::Type, kFieldFieldType, kFieldFieldTypeSize>; + const uint32_t field_index_; DISALLOW_COPY_AND_ASSIGN(HUnresolvedStaticFieldSet); @@ -5660,7 +5846,8 @@ enum class TypeCheckKind { kAbstractClassCheck, // Can just walk the super class chain, starting one up. kInterfaceCheck, // No optimization yet when checking against an interface. kArrayObjectCheck, // Can just check if the array is not primitive. - kArrayCheck // No optimization yet when checking against a generic array. + kArrayCheck, // No optimization yet when checking against a generic array. + kLast = kArrayCheck }; std::ostream& operator<<(std::ostream& os, TypeCheckKind rhs); @@ -5673,9 +5860,9 @@ class HInstanceOf : public HExpression<2> { uint32_t dex_pc) : HExpression(Primitive::kPrimBoolean, SideEffectsForArchRuntimeCalls(check_kind), - dex_pc), - check_kind_(check_kind), - must_do_null_check_(true) { + dex_pc) { + SetPackedField<TypeCheckKindField>(check_kind); + SetPackedFlag<kFlagMustDoNullCheck>(true); SetRawInputAt(0, object); SetRawInputAt(1, constant); } @@ -5687,16 +5874,14 @@ class HInstanceOf : public HExpression<2> { } bool NeedsEnvironment() const OVERRIDE { - return CanCallRuntime(check_kind_); + return CanCallRuntime(GetTypeCheckKind()); } - bool IsExactCheck() const { return check_kind_ == TypeCheckKind::kExactCheck; } - - TypeCheckKind GetTypeCheckKind() const { return check_kind_; } - // Used only in code generation. - bool MustDoNullCheck() const { return must_do_null_check_; } - void ClearMustDoNullCheck() { must_do_null_check_ = false; } + bool MustDoNullCheck() const { return GetPackedFlag<kFlagMustDoNullCheck>(); } + void ClearMustDoNullCheck() { SetPackedFlag<kFlagMustDoNullCheck>(false); } + TypeCheckKind GetTypeCheckKind() const { return GetPackedField<TypeCheckKindField>(); } + bool IsExactCheck() const { return GetTypeCheckKind() == TypeCheckKind::kExactCheck; } static bool CanCallRuntime(TypeCheckKind check_kind) { // Mips currently does runtime calls for any other checks. @@ -5710,8 +5895,13 @@ class HInstanceOf : public HExpression<2> { DECLARE_INSTRUCTION(InstanceOf); private: - const TypeCheckKind check_kind_; - bool must_do_null_check_; + static constexpr size_t kFieldTypeCheckKind = kNumberOfExpressionPackedBits; + static constexpr size_t kFieldTypeCheckKindSize = + MinimumBitsToStore(static_cast<size_t>(TypeCheckKind::kLast)); + static constexpr size_t kFlagMustDoNullCheck = kFieldTypeCheckKind + kFieldTypeCheckKindSize; + static constexpr size_t kNumberOfInstanceOfPackedBits = kFlagMustDoNullCheck + 1; + static_assert(kNumberOfInstanceOfPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + using TypeCheckKindField = BitField<TypeCheckKind, kFieldTypeCheckKind, kFieldTypeCheckKindSize>; DISALLOW_COPY_AND_ASSIGN(HInstanceOf); }; @@ -5720,28 +5910,35 @@ class HBoundType : public HExpression<1> { public: HBoundType(HInstruction* input, uint32_t dex_pc = kNoDexPc) : HExpression(Primitive::kPrimNot, SideEffects::None(), dex_pc), - upper_bound_(ReferenceTypeInfo::CreateInvalid()), - upper_can_be_null_(true), - can_be_null_(true) { + upper_bound_(ReferenceTypeInfo::CreateInvalid()) { + SetPackedFlag<kFlagUpperCanBeNull>(true); + SetPackedFlag<kFlagCanBeNull>(true); DCHECK_EQ(input->GetType(), Primitive::kPrimNot); SetRawInputAt(0, input); } // {Get,Set}Upper* should only be used in reference type propagation. const ReferenceTypeInfo& GetUpperBound() const { return upper_bound_; } - bool GetUpperCanBeNull() const { return upper_can_be_null_; } + bool GetUpperCanBeNull() const { return GetPackedFlag<kFlagUpperCanBeNull>(); } void SetUpperBound(const ReferenceTypeInfo& upper_bound, bool can_be_null); void SetCanBeNull(bool can_be_null) { - DCHECK(upper_can_be_null_ || !can_be_null); - can_be_null_ = can_be_null; + DCHECK(GetUpperCanBeNull() || !can_be_null); + SetPackedFlag<kFlagCanBeNull>(can_be_null); } - bool CanBeNull() const OVERRIDE { return can_be_null_; } + bool CanBeNull() const OVERRIDE { return GetPackedFlag<kFlagCanBeNull>(); } DECLARE_INSTRUCTION(BoundType); private: + // Represents the top constraint that can_be_null_ cannot exceed (i.e. if this + // is false then CanBeNull() cannot be true). + static constexpr size_t kFlagUpperCanBeNull = kNumberOfExpressionPackedBits; + static constexpr size_t kFlagCanBeNull = kFlagUpperCanBeNull + 1; + static constexpr size_t kNumberOfBoundTypePackedBits = kFlagCanBeNull + 1; + static_assert(kNumberOfBoundTypePackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + // Encodes the most upper class that this instruction can have. In other words // it is always the case that GetUpperBound().IsSupertypeOf(GetReferenceType()). // It is used to bound the type in cases like: @@ -5749,10 +5946,6 @@ class HBoundType : public HExpression<1> { // // uper_bound_ will be ClassX // } ReferenceTypeInfo upper_bound_; - // Represents the top constraint that can_be_null_ cannot exceed (i.e. if this - // is false then can_be_null_ cannot be true). - bool upper_can_be_null_; - bool can_be_null_; DISALLOW_COPY_AND_ASSIGN(HBoundType); }; @@ -5763,9 +5956,9 @@ class HCheckCast : public HTemplateInstruction<2> { HLoadClass* constant, TypeCheckKind check_kind, uint32_t dex_pc) - : HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc), - check_kind_(check_kind), - must_do_null_check_(true) { + : HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc) { + SetPackedField<TypeCheckKindField>(check_kind); + SetPackedFlag<kFlagMustDoNullCheck>(true); SetRawInputAt(0, object); SetRawInputAt(1, constant); } @@ -5783,17 +5976,21 @@ class HCheckCast : public HTemplateInstruction<2> { bool CanThrow() const OVERRIDE { return true; } - bool MustDoNullCheck() const { return must_do_null_check_; } - void ClearMustDoNullCheck() { must_do_null_check_ = false; } - TypeCheckKind GetTypeCheckKind() const { return check_kind_; } - - bool IsExactCheck() const { return check_kind_ == TypeCheckKind::kExactCheck; } + bool MustDoNullCheck() const { return GetPackedFlag<kFlagMustDoNullCheck>(); } + void ClearMustDoNullCheck() { SetPackedFlag<kFlagMustDoNullCheck>(false); } + TypeCheckKind GetTypeCheckKind() const { return GetPackedField<TypeCheckKindField>(); } + bool IsExactCheck() const { return GetTypeCheckKind() == TypeCheckKind::kExactCheck; } DECLARE_INSTRUCTION(CheckCast); private: - const TypeCheckKind check_kind_; - bool must_do_null_check_; + static constexpr size_t kFieldTypeCheckKind = kNumberOfGenericPackedBits; + static constexpr size_t kFieldTypeCheckKindSize = + MinimumBitsToStore(static_cast<size_t>(TypeCheckKind::kLast)); + static constexpr size_t kFlagMustDoNullCheck = kFieldTypeCheckKind + kFieldTypeCheckKindSize; + static constexpr size_t kNumberOfCheckCastPackedBits = kFlagMustDoNullCheck + 1; + static_assert(kNumberOfCheckCastPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + using TypeCheckKindField = BitField<TypeCheckKind, kFieldTypeCheckKind, kFieldTypeCheckKindSize>; DISALLOW_COPY_AND_ASSIGN(HCheckCast); }; @@ -5802,30 +5999,40 @@ class HMemoryBarrier : public HTemplateInstruction<0> { public: explicit HMemoryBarrier(MemBarrierKind barrier_kind, uint32_t dex_pc = kNoDexPc) : HTemplateInstruction( - SideEffects::AllWritesAndReads(), dex_pc), // Assume write/read on all fields/arrays. - barrier_kind_(barrier_kind) {} + SideEffects::AllWritesAndReads(), dex_pc) { // Assume write/read on all fields/arrays. + SetPackedField<BarrierKindField>(barrier_kind); + } - MemBarrierKind GetBarrierKind() { return barrier_kind_; } + MemBarrierKind GetBarrierKind() { return GetPackedField<BarrierKindField>(); } DECLARE_INSTRUCTION(MemoryBarrier); private: - const MemBarrierKind barrier_kind_; + static constexpr size_t kFieldBarrierKind = HInstruction::kNumberOfGenericPackedBits; + static constexpr size_t kFieldBarrierKindSize = + MinimumBitsToStore(static_cast<size_t>(kLastBarrierKind)); + static constexpr size_t kNumberOfMemoryBarrierPackedBits = + kFieldBarrierKind + kFieldBarrierKindSize; + static_assert(kNumberOfMemoryBarrierPackedBits <= kMaxNumberOfPackedBits, + "Too many packed fields."); + using BarrierKindField = BitField<MemBarrierKind, kFieldBarrierKind, kFieldBarrierKindSize>; DISALLOW_COPY_AND_ASSIGN(HMemoryBarrier); }; class HMonitorOperation : public HTemplateInstruction<1> { public: - enum OperationKind { + enum class OperationKind { kEnter, kExit, + kLast = kExit }; HMonitorOperation(HInstruction* object, OperationKind kind, uint32_t dex_pc) : HTemplateInstruction( - SideEffects::AllExceptGCDependency(), dex_pc), // Assume write/read on all fields/arrays. - kind_(kind) { + SideEffects::AllExceptGCDependency(), // Assume write/read on all fields/arrays. + dex_pc) { + SetPackedField<OperationKindField>(kind); SetRawInputAt(0, object); } @@ -5839,13 +6046,20 @@ class HMonitorOperation : public HTemplateInstruction<1> { return IsEnter(); } - - bool IsEnter() const { return kind_ == kEnter; } + OperationKind GetOperationKind() const { return GetPackedField<OperationKindField>(); } + bool IsEnter() const { return GetOperationKind() == OperationKind::kEnter; } DECLARE_INSTRUCTION(MonitorOperation); private: - const OperationKind kind_; + static constexpr size_t kFieldOperationKind = HInstruction::kNumberOfGenericPackedBits; + static constexpr size_t kFieldOperationKindSize = + MinimumBitsToStore(static_cast<size_t>(OperationKind::kLast)); + static constexpr size_t kNumberOfMonitorOperationPackedBits = + kFieldOperationKind + kFieldOperationKindSize; + static_assert(kNumberOfMonitorOperationPackedBits <= HInstruction::kMaxNumberOfPackedBits, + "Too many packed fields."); + using OperationKindField = BitField<OperationKind, kFieldOperationKind, kFieldOperationKindSize>; private: DISALLOW_COPY_AND_ASSIGN(HMonitorOperation); @@ -6022,6 +6236,9 @@ class HParallelMove : public HTemplateInstruction<0> { } // namespace art +#if defined(ART_ENABLE_CODEGEN_arm) || defined(ART_ENABLE_CODEGEN_arm64) +#include "nodes_shared.h" +#endif #ifdef ART_ENABLE_CODEGEN_arm #include "nodes_arm.h" #endif diff --git a/compiler/optimizing/nodes_arm64.h b/compiler/optimizing/nodes_arm64.h index 445cdab191..75a71e78b8 100644 --- a/compiler/optimizing/nodes_arm64.h +++ b/compiler/optimizing/nodes_arm64.h @@ -118,38 +118,64 @@ class HArm64IntermediateAddress : public HExpression<2> { DISALLOW_COPY_AND_ASSIGN(HArm64IntermediateAddress); }; -class HArm64MultiplyAccumulate : public HExpression<3> { +class HArm64BitwiseNegatedRight : public HBinaryOperation { public: - HArm64MultiplyAccumulate(Primitive::Type type, - InstructionKind op, - HInstruction* accumulator, - HInstruction* mul_left, - HInstruction* mul_right, - uint32_t dex_pc = kNoDexPc) - : HExpression(type, SideEffects::None(), dex_pc), op_kind_(op) { - SetRawInputAt(kInputAccumulatorIndex, accumulator); - SetRawInputAt(kInputMulLeftIndex, mul_left); - SetRawInputAt(kInputMulRightIndex, mul_right); + HArm64BitwiseNegatedRight(Primitive::Type result_type, + InstructionKind op, + HInstruction* left, + HInstruction* right, + uint32_t dex_pc = kNoDexPc) + : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc), + op_kind_(op) { + DCHECK(op == HInstruction::kAnd || op == HInstruction::kOr || op == HInstruction::kXor) << op; } - static constexpr int kInputAccumulatorIndex = 0; - static constexpr int kInputMulLeftIndex = 1; - static constexpr int kInputMulRightIndex = 2; + template <typename T, typename U> + auto Compute(T x, U y) const -> decltype(x & ~y) { + static_assert(std::is_same<decltype(x & ~y), decltype(x | ~y)>::value && + std::is_same<decltype(x & ~y), decltype(x ^ ~y)>::value, + "Inconsistent negated bitwise types"); + switch (op_kind_) { + case HInstruction::kAnd: + return x & ~y; + case HInstruction::kOr: + return x | ~y; + case HInstruction::kXor: + return x ^ ~y; + default: + LOG(FATAL) << "Unreachable"; + UNREACHABLE(); + } + } - bool CanBeMoved() const OVERRIDE { return true; } - bool InstructionDataEquals(HInstruction* other) const OVERRIDE { - return op_kind_ == other->AsArm64MultiplyAccumulate()->op_kind_; + HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant( + Compute(x->GetValue(), y->GetValue()), GetDexPc()); + } + HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetLongConstant( + Compute(x->GetValue(), y->GetValue()), GetDexPc()); + } + HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED, + HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for float values"; + UNREACHABLE(); + } + HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED, + HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for double values"; + UNREACHABLE(); } InstructionKind GetOpKind() const { return op_kind_; } - DECLARE_INSTRUCTION(Arm64MultiplyAccumulate); + DECLARE_INSTRUCTION(Arm64BitwiseNegatedRight); private: - // Indicates if this is a MADD or MSUB. - InstructionKind op_kind_; + // Specifies the bitwise operation, which will be then negated. + const InstructionKind op_kind_; - DISALLOW_COPY_AND_ASSIGN(HArm64MultiplyAccumulate); + DISALLOW_COPY_AND_ASSIGN(HArm64BitwiseNegatedRight); }; } // namespace art diff --git a/compiler/optimizing/nodes_shared.h b/compiler/optimizing/nodes_shared.h new file mode 100644 index 0000000000..b04b622838 --- /dev/null +++ b/compiler/optimizing/nodes_shared.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef ART_COMPILER_OPTIMIZING_NODES_SHARED_H_ +#define ART_COMPILER_OPTIMIZING_NODES_SHARED_H_ + +namespace art { + +class HMultiplyAccumulate : public HExpression<3> { + public: + HMultiplyAccumulate(Primitive::Type type, + InstructionKind op, + HInstruction* accumulator, + HInstruction* mul_left, + HInstruction* mul_right, + uint32_t dex_pc = kNoDexPc) + : HExpression(type, SideEffects::None(), dex_pc), op_kind_(op) { + SetRawInputAt(kInputAccumulatorIndex, accumulator); + SetRawInputAt(kInputMulLeftIndex, mul_left); + SetRawInputAt(kInputMulRightIndex, mul_right); + } + + static constexpr int kInputAccumulatorIndex = 0; + static constexpr int kInputMulLeftIndex = 1; + static constexpr int kInputMulRightIndex = 2; + + bool CanBeMoved() const OVERRIDE { return true; } + bool InstructionDataEquals(HInstruction* other) const OVERRIDE { + return op_kind_ == other->AsMultiplyAccumulate()->op_kind_; + } + + InstructionKind GetOpKind() const { return op_kind_; } + + DECLARE_INSTRUCTION(MultiplyAccumulate); + + private: + // Indicates if this is a MADD or MSUB. + const InstructionKind op_kind_; + + DISALLOW_COPY_AND_ASSIGN(HMultiplyAccumulate); +}; + +} // namespace art + +#endif // ART_COMPILER_OPTIMIZING_NODES_SHARED_H_ diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index b1891c979e..5a9f2583fd 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -60,6 +60,7 @@ #include "induction_var_analysis.h" #include "inliner.h" #include "instruction_simplifier.h" +#include "instruction_simplifier_arm.h" #include "intrinsics.h" #include "jit/debugger_interface.h" #include "jit/jit_code_cache.h" @@ -438,7 +439,10 @@ static void RunArchOptimizations(InstructionSet instruction_set, case kThumb2: case kArm: { arm::DexCacheArrayFixups* fixups = new (arena) arm::DexCacheArrayFixups(graph, stats); + arm::InstructionSimplifierArm* simplifier = + new (arena) arm::InstructionSimplifierArm(graph, stats); HOptimization* arm_optimizations[] = { + simplifier, fixups }; RunOptimizations(arm_optimizations, arraysize(arm_optimizations), pass_observer); diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index 4784de1380..54cbdf8b66 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -63,8 +63,7 @@ void StackMapStream::EndStackMapEntry() { void StackMapStream::AddDexRegisterEntry(DexRegisterLocation::Kind kind, int32_t value) { if (kind != DexRegisterLocation::Kind::kNone) { // Ensure we only use non-compressed location kind at this stage. - DCHECK(DexRegisterLocation::IsShortLocationKind(kind)) - << DexRegisterLocation::PrettyDescriptor(kind); + DCHECK(DexRegisterLocation::IsShortLocationKind(kind)) << kind; DexRegisterLocation location(kind, value); // Look for Dex register `location` in the location catalog (using the @@ -257,6 +256,7 @@ void StackMapStream::FillIn(MemoryRegion region) { // Ensure we reached the end of the Dex registers location_catalog. DCHECK_EQ(location_catalog_offset, dex_register_location_catalog_region.size()); + ArenaBitVector empty_bitmask(allocator_, 0, /* expandable */ false); uintptr_t next_dex_register_map_offset = 0; uintptr_t next_inline_info_offset = 0; for (size_t i = 0, e = stack_maps_.size(); i < e; ++i) { @@ -268,6 +268,9 @@ void StackMapStream::FillIn(MemoryRegion region) { stack_map.SetRegisterMask(stack_map_encoding_, entry.register_mask); if (entry.sp_mask != nullptr) { stack_map.SetStackMask(stack_map_encoding_, *entry.sp_mask); + } else { + // The MemoryRegion does not have to be zeroed, so make sure we clear the bits. + stack_map.SetStackMask(stack_map_encoding_, empty_bitmask); } if (entry.num_dex_registers == 0 || (entry.live_dex_registers_mask->NumSetBits() == 0)) { @@ -344,6 +347,11 @@ void StackMapStream::FillIn(MemoryRegion region) { } } } + + // Verify all written data in debug build. + if (kIsDebugBuild) { + CheckCodeInfo(region); + } } void StackMapStream::FillInDexRegisterMap(DexRegisterMap dex_register_map, @@ -423,4 +431,90 @@ bool StackMapStream::HaveTheSameDexMaps(const StackMapEntry& a, const StackMapEn return true; } +// Helper for CheckCodeInfo - check that register map has the expected content. +void StackMapStream::CheckDexRegisterMap(const CodeInfo& code_info, + const DexRegisterMap& dex_register_map, + size_t num_dex_registers, + BitVector* live_dex_registers_mask, + size_t dex_register_locations_index) const { + StackMapEncoding encoding = code_info.ExtractEncoding(); + for (size_t reg = 0; reg < num_dex_registers; reg++) { + // Find the location we tried to encode. + DexRegisterLocation expected = DexRegisterLocation::None(); + if (live_dex_registers_mask->IsBitSet(reg)) { + size_t catalog_index = dex_register_locations_[dex_register_locations_index++]; + expected = location_catalog_entries_[catalog_index]; + } + // Compare to the seen location. + if (expected.GetKind() == DexRegisterLocation::Kind::kNone) { + DCHECK(!dex_register_map.IsValid() || !dex_register_map.IsDexRegisterLive(reg)); + } else { + DCHECK(dex_register_map.IsDexRegisterLive(reg)); + DexRegisterLocation seen = dex_register_map.GetDexRegisterLocation( + reg, num_dex_registers, code_info, encoding); + DCHECK_EQ(expected.GetKind(), seen.GetKind()); + DCHECK_EQ(expected.GetValue(), seen.GetValue()); + } + } + if (num_dex_registers == 0) { + DCHECK(!dex_register_map.IsValid()); + } +} + +// Check that all StackMapStream inputs are correctly encoded by trying to read them back. +void StackMapStream::CheckCodeInfo(MemoryRegion region) const { + CodeInfo code_info(region); + StackMapEncoding encoding = code_info.ExtractEncoding(); + DCHECK_EQ(code_info.GetNumberOfStackMaps(), stack_maps_.size()); + for (size_t s = 0; s < stack_maps_.size(); ++s) { + const StackMap stack_map = code_info.GetStackMapAt(s, encoding); + StackMapEntry entry = stack_maps_[s]; + + // Check main stack map fields. + DCHECK_EQ(stack_map.GetNativePcOffset(encoding), entry.native_pc_offset); + DCHECK_EQ(stack_map.GetDexPc(encoding), entry.dex_pc); + DCHECK_EQ(stack_map.GetRegisterMask(encoding), entry.register_mask); + MemoryRegion stack_mask = stack_map.GetStackMask(encoding); + if (entry.sp_mask != nullptr) { + DCHECK_GE(stack_mask.size_in_bits(), entry.sp_mask->GetNumberOfBits()); + for (size_t b = 0; b < stack_mask.size_in_bits(); b++) { + DCHECK_EQ(stack_mask.LoadBit(b), entry.sp_mask->IsBitSet(b)); + } + } else { + for (size_t b = 0; b < stack_mask.size_in_bits(); b++) { + DCHECK_EQ(stack_mask.LoadBit(b), 0u); + } + } + + CheckDexRegisterMap(code_info, + code_info.GetDexRegisterMapOf( + stack_map, encoding, entry.num_dex_registers), + entry.num_dex_registers, + entry.live_dex_registers_mask, + entry.dex_register_locations_start_index); + + // Check inline info. + DCHECK_EQ(stack_map.HasInlineInfo(encoding), (entry.inlining_depth != 0)); + if (entry.inlining_depth != 0) { + InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding); + DCHECK_EQ(inline_info.GetDepth(), entry.inlining_depth); + for (size_t d = 0; d < entry.inlining_depth; ++d) { + size_t inline_info_index = entry.inline_infos_start_index + d; + DCHECK_LT(inline_info_index, inline_infos_.size()); + InlineInfoEntry inline_entry = inline_infos_[inline_info_index]; + DCHECK_EQ(inline_info.GetDexPcAtDepth(d), inline_entry.dex_pc); + DCHECK_EQ(inline_info.GetMethodIndexAtDepth(d), inline_entry.method_index); + DCHECK_EQ(inline_info.GetInvokeTypeAtDepth(d), inline_entry.invoke_type); + + CheckDexRegisterMap(code_info, + code_info.GetDexRegisterMapAtDepth( + d, inline_info, encoding, inline_entry.num_dex_registers), + inline_entry.num_dex_registers, + inline_entry.live_dex_registers_mask, + inline_entry.dex_register_locations_start_index); + } + } + } +} + } // namespace art diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h index fc27a2b446..016a911424 100644 --- a/compiler/optimizing/stack_map_stream.h +++ b/compiler/optimizing/stack_map_stream.h @@ -167,6 +167,13 @@ class StackMapStream : public ValueObject { const BitVector& live_dex_registers_mask, uint32_t start_index_in_dex_register_locations) const; + void CheckDexRegisterMap(const CodeInfo& code_info, + const DexRegisterMap& dex_register_map, + size_t num_dex_registers, + BitVector* live_dex_registers_mask, + size_t dex_register_locations_index) const; + void CheckCodeInfo(MemoryRegion region) const; + ArenaAllocator* allocator_; ArenaVector<StackMapEntry> stack_maps_; |