diff options
Diffstat (limited to 'compiler/optimizing')
| -rw-r--r-- | compiler/optimizing/code_generator.cc | 48 | ||||
| -rw-r--r-- | compiler/optimizing/constant_folding.cc | 180 | ||||
| -rw-r--r-- | compiler/optimizing/instruction_simplifier.cc | 265 | ||||
| -rw-r--r-- | compiler/optimizing/nodes.cc | 36 | ||||
| -rw-r--r-- | compiler/optimizing/nodes.h | 55 | ||||
| -rw-r--r-- | compiler/optimizing/stack_map_stream.h | 77 | ||||
| -rw-r--r-- | compiler/optimizing/stack_map_test.cc | 143 |
7 files changed, 689 insertions, 115 deletions
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index ed3f949afe..7d256ae4aa 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -610,7 +610,7 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction, uint32_t dex_pc) { for (size_t i = 0; i < environment_size; ++i) { HInstruction* current = environment->GetInstructionAt(i); if (current == nullptr) { - stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kNone, 0); + stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kNone, 0); continue; } @@ -620,37 +620,43 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction, uint32_t dex_pc) { DCHECK_EQ(current, location.GetConstant()); if (current->IsLongConstant()) { int64_t value = current->AsLongConstant()->GetValue(); - stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, Low32Bits(value)); - stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, High32Bits(value)); + stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kConstant, + Low32Bits(value)); + stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kConstant, + High32Bits(value)); ++i; DCHECK_LT(i, environment_size); } else if (current->IsDoubleConstant()) { int64_t value = bit_cast<double, int64_t>(current->AsDoubleConstant()->GetValue()); - stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, Low32Bits(value)); - stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, High32Bits(value)); + stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kConstant, + Low32Bits(value)); + stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kConstant, + High32Bits(value)); ++i; DCHECK_LT(i, environment_size); } else if (current->IsIntConstant()) { int32_t value = current->AsIntConstant()->GetValue(); - stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, value); + stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kConstant, value); } else if (current->IsNullConstant()) { - stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, 0); + stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kConstant, 0); } else { DCHECK(current->IsFloatConstant()); int32_t value = bit_cast<float, int32_t>(current->AsFloatConstant()->GetValue()); - stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, value); + stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kConstant, value); } break; } case Location::kStackSlot: { - stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInStack, location.GetStackIndex()); + stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kInStack, + location.GetStackIndex()); break; } case Location::kDoubleStackSlot: { - stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInStack, location.GetStackIndex()); - stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInStack, + stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kInStack, + location.GetStackIndex()); + stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kInStack, location.GetHighStackIndex(kVRegSize)); ++i; DCHECK_LT(i, environment_size); @@ -659,9 +665,9 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction, uint32_t dex_pc) { case Location::kRegister : { int id = location.reg(); - stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInRegister, id); + stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kInRegister, id); if (current->GetType() == Primitive::kPrimLong) { - stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInRegister, id); + stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kInRegister, id); ++i; DCHECK_LT(i, environment_size); } @@ -670,9 +676,9 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction, uint32_t dex_pc) { case Location::kFpuRegister : { int id = location.reg(); - stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInFpuRegister, id); + stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kInFpuRegister, id); if (current->GetType() == Primitive::kPrimDouble) { - stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInFpuRegister, id); + stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kInFpuRegister, id); ++i; DCHECK_LT(i, environment_size); } @@ -680,16 +686,20 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction, uint32_t dex_pc) { } case Location::kFpuRegisterPair : { - stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInFpuRegister, location.low()); - stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInFpuRegister, location.high()); + stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kInFpuRegister, + location.low()); + stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kInFpuRegister, + location.high()); ++i; DCHECK_LT(i, environment_size); break; } case Location::kRegisterPair : { - stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInRegister, location.low()); - stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInRegister, location.high()); + stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kInRegister, + location.low()); + stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kInRegister, + location.high()); ++i; DCHECK_LT(i, environment_size); break; diff --git a/compiler/optimizing/constant_folding.cc b/compiler/optimizing/constant_folding.cc index fca9933872..ec0cc3e98b 100644 --- a/compiler/optimizing/constant_folding.cc +++ b/compiler/optimizing/constant_folding.cc @@ -18,7 +18,28 @@ namespace art { +// This visitor tries to simplify operations that yield a constant. For example +// `input * 0` is replaced by a null constant. +class InstructionWithAbsorbingInputSimplifier : public HGraphVisitor { + public: + explicit InstructionWithAbsorbingInputSimplifier(HGraph* graph) : HGraphVisitor(graph) {} + + private: + void VisitShift(HBinaryOperation* shift); + + void VisitAnd(HAnd* instruction) OVERRIDE; + void VisitMul(HMul* instruction) OVERRIDE; + void VisitOr(HOr* instruction) OVERRIDE; + void VisitRem(HRem* instruction) OVERRIDE; + void VisitShl(HShl* instruction) OVERRIDE; + void VisitShr(HShr* instruction) OVERRIDE; + void VisitSub(HSub* instruction) OVERRIDE; + void VisitUShr(HUShr* instruction) OVERRIDE; + void VisitXor(HXor* instruction) OVERRIDE; +}; + void HConstantFolding::Run() { + InstructionWithAbsorbingInputSimplifier simplifier(graph_); // Process basic blocks in reverse post-order in the dominator tree, // so that an instruction turned into a constant, used as input of // another instruction, may possibly be used to turn that second @@ -38,6 +59,8 @@ void HConstantFolding::Run() { inst->AsBinaryOperation()->TryStaticEvaluation(); if (constant != nullptr) { inst->GetBlock()->ReplaceAndRemoveInstructionWith(inst, constant); + } else { + inst->Accept(&simplifier); } } else if (inst->IsUnaryOperation()) { // Constant folding: replace `op(a)' with a constant at compile @@ -47,9 +70,166 @@ void HConstantFolding::Run() { if (constant != nullptr) { inst->GetBlock()->ReplaceAndRemoveInstructionWith(inst, constant); } + } else if (inst->IsDivZeroCheck()) { + // We can safely remove the check if the input is a non-null constant. + HDivZeroCheck* check = inst->AsDivZeroCheck(); + HInstruction* check_input = check->InputAt(0); + if (check_input->IsConstant() && !check_input->AsConstant()->IsZero()) { + check->ReplaceWith(check_input); + check->GetBlock()->RemoveInstruction(check); + } } } } } +void InstructionWithAbsorbingInputSimplifier::VisitShift(HBinaryOperation* instruction) { + DCHECK(instruction->IsShl() || instruction->IsShr() || instruction->IsUShr()); + HInstruction* left = instruction->GetLeft(); + if (left->IsConstant() && left->AsConstant()->IsZero()) { + // Replace code looking like + // SHL dst, 0, shift_amount + // with + // CONSTANT 0 + instruction->ReplaceWith(left); + instruction->GetBlock()->RemoveInstruction(instruction); + } +} + +void InstructionWithAbsorbingInputSimplifier::VisitAnd(HAnd* instruction) { + HConstant* input_cst = instruction->GetConstantRight(); + if ((input_cst != nullptr) && input_cst->IsZero()) { + // Replace code looking like + // AND dst, src, 0 + // with + // CONSTANT 0 + instruction->ReplaceWith(input_cst); + instruction->GetBlock()->RemoveInstruction(instruction); + } +} + +void InstructionWithAbsorbingInputSimplifier::VisitMul(HMul* instruction) { + HConstant* input_cst = instruction->GetConstantRight(); + Primitive::Type type = instruction->GetType(); + if (Primitive::IsIntOrLongType(type) && + (input_cst != nullptr) && input_cst->IsZero()) { + // Replace code looking like + // MUL dst, src, 0 + // with + // CONSTANT 0 + // Integral multiplication by zero always yields zero, but floating-point + // multiplication by zero does not always do. For example `Infinity * 0.0` + // should yield a NaN. + instruction->ReplaceWith(input_cst); + instruction->GetBlock()->RemoveInstruction(instruction); + } +} + +void InstructionWithAbsorbingInputSimplifier::VisitOr(HOr* instruction) { + HConstant* input_cst = instruction->GetConstantRight(); + + if (input_cst == nullptr) { + return; + } + + if (Int64FromConstant(input_cst) == -1) { + // Replace code looking like + // OR dst, src, 0xFFF...FF + // with + // CONSTANT 0xFFF...FF + instruction->ReplaceWith(input_cst); + instruction->GetBlock()->RemoveInstruction(instruction); + } +} + +void InstructionWithAbsorbingInputSimplifier::VisitRem(HRem* instruction) { + Primitive::Type type = instruction->GetType(); + + if (!Primitive::IsIntegralType(type)) { + return; + } + + HBasicBlock* block = instruction->GetBlock(); + + if (instruction->GetLeft()->IsConstant() && + instruction->GetLeft()->AsConstant()->IsZero()) { + // Replace code looking like + // REM dst, 0, src + // with + // CONSTANT 0 + instruction->ReplaceWith(instruction->GetLeft()); + block->RemoveInstruction(instruction); + } + + HConstant* cst_right = instruction->GetRight()->AsConstant(); + if (((cst_right != nullptr) && + (cst_right->IsOne() || cst_right->IsMinusOne())) || + (instruction->GetLeft() == instruction->GetRight())) { + // Replace code looking like + // REM dst, src, 1 + // or + // REM dst, src, -1 + // or + // REM dst, src, src + // with + // CONSTANT 0 + ArenaAllocator* allocator = GetGraph()->GetArena(); + block->ReplaceAndRemoveInstructionWith(instruction, + HConstant::NewConstant(allocator, type, 0)); + } +} + +void InstructionWithAbsorbingInputSimplifier::VisitShl(HShl* instruction) { + VisitShift(instruction); +} + +void InstructionWithAbsorbingInputSimplifier::VisitShr(HShr* instruction) { + VisitShift(instruction); +} + +void InstructionWithAbsorbingInputSimplifier::VisitSub(HSub* instruction) { + Primitive::Type type = instruction->GetType(); + + if (!Primitive::IsIntegralType(type)) { + return; + } + + HBasicBlock* block = instruction->GetBlock(); + ArenaAllocator* allocator = GetGraph()->GetArena(); + + // We assume that GVN has run before, so we only perform a pointer + // comparison. If for some reason the values are equal but the pointers are + // different, we are still correct and only miss an optimisation + // opportunity. + if (instruction->GetLeft() == instruction->GetRight()) { + // Replace code looking like + // SUB dst, src, src + // with + // CONSTANT 0 + // Note that we cannot optimise `x - x` to `0` for floating-point. It does + // not work when `x` is an infinity. + block->ReplaceAndRemoveInstructionWith(instruction, + HConstant::NewConstant(allocator, type, 0)); + } +} + +void InstructionWithAbsorbingInputSimplifier::VisitUShr(HUShr* instruction) { + VisitShift(instruction); +} + +void InstructionWithAbsorbingInputSimplifier::VisitXor(HXor* instruction) { + if (instruction->GetLeft() == instruction->GetRight()) { + // Replace code looking like + // XOR dst, src, src + // with + // CONSTANT 0 + Primitive::Type type = instruction->GetType(); + HBasicBlock* block = instruction->GetBlock(); + ArenaAllocator* allocator = GetGraph()->GetArena(); + + block->ReplaceAndRemoveInstructionWith(instruction, + HConstant::NewConstant(allocator, type, 0)); + } +} + } // namespace art diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index fd99070780..2ef19b92a1 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -27,6 +27,8 @@ class InstructionSimplifierVisitor : public HGraphVisitor { : HGraphVisitor(graph), stats_(stats) {} private: + void VisitShift(HBinaryOperation* shift); + void VisitSuspendCheck(HSuspendCheck* check) OVERRIDE; void VisitEqual(HEqual* equal) OVERRIDE; void VisitArraySet(HArraySet* equal) OVERRIDE; @@ -34,6 +36,16 @@ class InstructionSimplifierVisitor : public HGraphVisitor { void VisitNullCheck(HNullCheck* instruction) OVERRIDE; void VisitArrayLength(HArrayLength* instruction) OVERRIDE; void VisitCheckCast(HCheckCast* instruction) OVERRIDE; + void VisitAdd(HAdd* instruction) OVERRIDE; + void VisitAnd(HAnd* instruction) OVERRIDE; + void VisitDiv(HDiv* instruction) OVERRIDE; + void VisitMul(HMul* instruction) OVERRIDE; + void VisitOr(HOr* instruction) OVERRIDE; + void VisitShl(HShl* instruction) OVERRIDE; + void VisitShr(HShr* instruction) OVERRIDE; + void VisitSub(HSub* instruction) OVERRIDE; + void VisitUShr(HUShr* instruction) OVERRIDE; + void VisitXor(HXor* instruction) OVERRIDE; OptimizingCompilerStats* stats_; }; @@ -43,6 +55,29 @@ void InstructionSimplifier::Run() { visitor.VisitInsertionOrder(); } +namespace { + +bool AreAllBitsSet(HConstant* constant) { + return Int64FromConstant(constant) == -1; +} + +} // namespace + +void InstructionSimplifierVisitor::VisitShift(HBinaryOperation* instruction) { + DCHECK(instruction->IsShl() || instruction->IsShr() || instruction->IsUShr()); + HConstant* input_cst = instruction->GetConstantRight(); + HInstruction* input_other = instruction->GetLeastConstantLeft(); + + if ((input_cst != nullptr) && input_cst->IsZero()) { + // Replace code looking like + // SHL dst, src, 0 + // with + // src + instruction->ReplaceWith(input_other); + instruction->GetBlock()->RemoveInstruction(instruction); + } +} + void InstructionSimplifierVisitor::VisitNullCheck(HNullCheck* null_check) { HInstruction* obj = null_check->InputAt(0); if (!obj->CanBeNull()) { @@ -137,4 +172,234 @@ void InstructionSimplifierVisitor::VisitTypeConversion(HTypeConversion* instruct } } +void InstructionSimplifierVisitor::VisitAdd(HAdd* instruction) { + HConstant* input_cst = instruction->GetConstantRight(); + HInstruction* input_other = instruction->GetLeastConstantLeft(); + if ((input_cst != nullptr) && input_cst->IsZero()) { + // Replace code looking like + // ADD dst, src, 0 + // with + // src + instruction->ReplaceWith(input_other); + instruction->GetBlock()->RemoveInstruction(instruction); + } +} + +void InstructionSimplifierVisitor::VisitAnd(HAnd* instruction) { + HConstant* input_cst = instruction->GetConstantRight(); + HInstruction* input_other = instruction->GetLeastConstantLeft(); + + if ((input_cst != nullptr) && AreAllBitsSet(input_cst)) { + // Replace code looking like + // AND dst, src, 0xFFF...FF + // with + // src + instruction->ReplaceWith(input_other); + instruction->GetBlock()->RemoveInstruction(instruction); + return; + } + + // We assume that GVN has run before, so we only perform a pointer comparison. + // If for some reason the values are equal but the pointers are different, we + // are still correct and only miss an optimisation opportunity. + if (instruction->GetLeft() == instruction->GetRight()) { + // Replace code looking like + // AND dst, src, src + // with + // src + instruction->ReplaceWith(instruction->GetLeft()); + instruction->GetBlock()->RemoveInstruction(instruction); + } +} + +void InstructionSimplifierVisitor::VisitDiv(HDiv* instruction) { + HConstant* input_cst = instruction->GetConstantRight(); + HInstruction* input_other = instruction->GetLeastConstantLeft(); + Primitive::Type type = instruction->GetType(); + + if ((input_cst != nullptr) && input_cst->IsOne()) { + // Replace code looking like + // DIV dst, src, 1 + // with + // src + instruction->ReplaceWith(input_other); + instruction->GetBlock()->RemoveInstruction(instruction); + return; + } + + if ((input_cst != nullptr) && input_cst->IsMinusOne() && + (Primitive::IsFloatingPointType(type) || Primitive::IsIntOrLongType(type))) { + // Replace code looking like + // DIV dst, src, -1 + // with + // NEG dst, src + instruction->GetBlock()->ReplaceAndRemoveInstructionWith( + instruction, (new (GetGraph()->GetArena()) HNeg(type, input_other))); + } +} + +void InstructionSimplifierVisitor::VisitMul(HMul* instruction) { + HConstant* input_cst = instruction->GetConstantRight(); + HInstruction* input_other = instruction->GetLeastConstantLeft(); + Primitive::Type type = instruction->GetType(); + HBasicBlock* block = instruction->GetBlock(); + ArenaAllocator* allocator = GetGraph()->GetArena(); + + if (input_cst == nullptr) { + return; + } + + if (input_cst->IsOne()) { + // Replace code looking like + // MUL dst, src, 1 + // with + // src + instruction->ReplaceWith(input_other); + instruction->GetBlock()->RemoveInstruction(instruction); + return; + } + + if (input_cst->IsMinusOne() && + (Primitive::IsFloatingPointType(type) || Primitive::IsIntOrLongType(type))) { + // Replace code looking like + // MUL dst, src, -1 + // with + // NEG dst, src + HNeg* neg = new (allocator) HNeg(type, input_other); + block->ReplaceAndRemoveInstructionWith(instruction, neg); + return; + } + + if (Primitive::IsFloatingPointType(type) && + ((input_cst->IsFloatConstant() && input_cst->AsFloatConstant()->GetValue() == 2.0f) || + (input_cst->IsDoubleConstant() && input_cst->AsDoubleConstant()->GetValue() == 2.0))) { + // Replace code looking like + // FP_MUL dst, src, 2.0 + // with + // FP_ADD dst, src, src + // The 'int' and 'long' cases are handled below. + block->ReplaceAndRemoveInstructionWith(instruction, + new (allocator) HAdd(type, input_other, input_other)); + return; + } + + if (Primitive::IsIntOrLongType(type)) { + int64_t factor = Int64FromConstant(input_cst); + // We expect the `0` case to have been handled in the constant folding pass. + DCHECK_NE(factor, 0); + if (IsPowerOfTwo(factor)) { + // Replace code looking like + // MUL dst, src, pow_of_2 + // with + // SHL dst, src, log2(pow_of_2) + HIntConstant* shift = new (allocator) HIntConstant(WhichPowerOf2(factor)); + block->InsertInstructionBefore(shift, instruction); + HShl* shl = new(allocator) HShl(type, input_other, shift); + block->ReplaceAndRemoveInstructionWith(instruction, shl); + } + } +} + +void InstructionSimplifierVisitor::VisitOr(HOr* instruction) { + HConstant* input_cst = instruction->GetConstantRight(); + HInstruction* input_other = instruction->GetLeastConstantLeft(); + + if ((input_cst != nullptr) && input_cst->IsZero()) { + // Replace code looking like + // OR dst, src, 0 + // with + // src + instruction->ReplaceWith(input_other); + instruction->GetBlock()->RemoveInstruction(instruction); + return; + } + + // We assume that GVN has run before, so we only perform a pointer comparison. + // If for some reason the values are equal but the pointers are different, we + // are still correct and only miss an optimisation opportunity. + if (instruction->GetLeft() == instruction->GetRight()) { + // Replace code looking like + // OR dst, src, src + // with + // src + instruction->ReplaceWith(instruction->GetLeft()); + instruction->GetBlock()->RemoveInstruction(instruction); + } +} + +void InstructionSimplifierVisitor::VisitShl(HShl* instruction) { + VisitShift(instruction); +} + +void InstructionSimplifierVisitor::VisitShr(HShr* instruction) { + VisitShift(instruction); +} + +void InstructionSimplifierVisitor::VisitSub(HSub* instruction) { + HConstant* input_cst = instruction->GetConstantRight(); + HInstruction* input_other = instruction->GetLeastConstantLeft(); + + if ((input_cst != nullptr) && input_cst->IsZero()) { + // Replace code looking like + // SUB dst, src, 0 + // with + // src + instruction->ReplaceWith(input_other); + instruction->GetBlock()->RemoveInstruction(instruction); + return; + } + + Primitive::Type type = instruction->GetType(); + if (!Primitive::IsIntegralType(type)) { + return; + } + + HBasicBlock* block = instruction->GetBlock(); + ArenaAllocator* allocator = GetGraph()->GetArena(); + + if (instruction->GetLeft()->IsConstant()) { + int64_t left = Int64FromConstant(instruction->GetLeft()->AsConstant()); + if (left == 0) { + // Replace code looking like + // SUB dst, 0, src + // with + // NEG dst, src + // Note that we cannot optimise `0.0 - x` to `-x` for floating-point. When + // `x` is `0.0`, the former expression yields `0.0`, while the later + // yields `-0.0`. + HNeg* neg = new (allocator) HNeg(type, instruction->GetRight()); + block->ReplaceAndRemoveInstructionWith(instruction, neg); + } + } +} + +void InstructionSimplifierVisitor::VisitUShr(HUShr* instruction) { + VisitShift(instruction); +} + +void InstructionSimplifierVisitor::VisitXor(HXor* instruction) { + HConstant* input_cst = instruction->GetConstantRight(); + HInstruction* input_other = instruction->GetLeastConstantLeft(); + + if ((input_cst != nullptr) && input_cst->IsZero()) { + // Replace code looking like + // XOR dst, src, 0 + // with + // src + instruction->ReplaceWith(input_other); + instruction->GetBlock()->RemoveInstruction(instruction); + return; + } + + if ((input_cst != nullptr) && AreAllBitsSet(input_cst)) { + // Replace code looking like + // XOR dst, src, 0xFFF...FF + // with + // NOT dst, src + HNot* bitwise_not = new (GetGraph()->GetArena()) HNot(instruction->GetType(), input_other); + instruction->GetBlock()->ReplaceAndRemoveInstructionWith(instruction, bitwise_not); + return; + } +} + } // namespace art diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index e51bbc330a..a90ebced69 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -673,10 +673,43 @@ HConstant* HBinaryOperation::TryStaticEvaluation() const { return nullptr; } +HConstant* HBinaryOperation::GetConstantRight() const { + if (GetRight()->IsConstant()) { + return GetRight()->AsConstant(); + } else if (IsCommutative() && GetLeft()->IsConstant()) { + return GetLeft()->AsConstant(); + } else { + return nullptr; + } +} + +// If `GetConstantRight()` returns one of the input, this returns the other +// one. Otherwise it returns nullptr. +HInstruction* HBinaryOperation::GetLeastConstantLeft() const { + HInstruction* most_constant_right = GetConstantRight(); + if (most_constant_right == nullptr) { + return nullptr; + } else if (most_constant_right == GetLeft()) { + return GetRight(); + } else { + return GetLeft(); + } +} + bool HCondition::IsBeforeWhenDisregardMoves(HIf* if_) const { return this == if_->GetPreviousDisregardingMoves(); } +HConstant* HConstant::NewConstant(ArenaAllocator* allocator, Primitive::Type type, int64_t val) { + if (type == Primitive::kPrimInt) { + DCHECK(IsInt<32>(val)); + return new (allocator) HIntConstant(val); + } else { + DCHECK_EQ(type, Primitive::kPrimLong); + return new (allocator) HLongConstant(val); + } +} + bool HInstruction::Equals(HInstruction* other) const { if (!InstructionTypeEquals(other)) return false; DCHECK_EQ(GetKind(), other->GetKind()); @@ -907,7 +940,8 @@ void HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { } else { if (!returns_void) { // There will be multiple returns. - return_value = new (allocator) HPhi(allocator, kNoRegNumber, 0, invoke->GetType()); + return_value = new (allocator) HPhi( + allocator, kNoRegNumber, 0, HPhi::ToPhiType(invoke->GetType())); to->AddPhi(return_value->AsPhi()); } for (size_t i = 0, e = to->GetPredecessors().Size(); i < e; ++i) { diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index d4498a6d42..ec3d7438ab 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -133,8 +133,13 @@ class HGraph : public ArenaObject<kArenaAllocMisc> { // recognition. Returns whether it was successful in doing all these steps. bool TryBuildingSsa() { BuildDominatorTree(); + // The SSA builder requires loops to all be natural. Specifically, the dead phi + // elimination phase checks the consistency of the graph when doing a post-order + // visit for eliminating dead phis: a dead phi can only have loop header phi + // users remaining when being visited. + if (!AnalyzeNaturalLoops()) return false; TransformToSsa(); - return AnalyzeNaturalLoops(); + return true; } void BuildDominatorTree(); @@ -1569,6 +1574,14 @@ class HBinaryOperation : public HExpression<2> { virtual int32_t Evaluate(int32_t x, int32_t y) const = 0; virtual int64_t Evaluate(int64_t x, int64_t y) const = 0; + // Returns an input that can legally be used as the right input and is + // constant, or nullptr. + HConstant* GetConstantRight() const; + + // If `GetConstantRight()` returns one of the input, this returns the other + // one. Otherwise it returns nullptr. + HInstruction* GetLeastConstantLeft() const; + DECLARE_INSTRUCTION(BinaryOperation); private: @@ -1840,6 +1853,12 @@ class HConstant : public HExpression<0> { bool CanBeMoved() const OVERRIDE { return true; } + virtual bool IsMinusOne() const { return false; } + virtual bool IsZero() const { return false; } + virtual bool IsOne() const { return false; } + + static HConstant* NewConstant(ArenaAllocator* allocator, Primitive::Type type, int64_t val); + DECLARE_INSTRUCTION(Constant); private: @@ -1859,6 +1878,16 @@ class HFloatConstant : public HConstant { size_t ComputeHashCode() const OVERRIDE { return static_cast<size_t>(GetValue()); } + bool IsMinusOne() const OVERRIDE { + return bit_cast<uint32_t>(AsFloatConstant()->GetValue()) == bit_cast<uint32_t>((-1.0f)); + } + bool IsZero() const OVERRIDE { + return AsFloatConstant()->GetValue() == 0.0f; + } + bool IsOne() const OVERRIDE { + return bit_cast<uint32_t>(AsFloatConstant()->GetValue()) == bit_cast<uint32_t>(1.0f); + } + DECLARE_INSTRUCTION(FloatConstant); private: @@ -1880,6 +1909,16 @@ class HDoubleConstant : public HConstant { size_t ComputeHashCode() const OVERRIDE { return static_cast<size_t>(GetValue()); } + bool IsMinusOne() const OVERRIDE { + return bit_cast<uint64_t>(AsDoubleConstant()->GetValue()) == bit_cast<uint64_t>((-1.0)); + } + bool IsZero() const OVERRIDE { + return AsDoubleConstant()->GetValue() == 0.0; + } + bool IsOne() const OVERRIDE { + return bit_cast<uint64_t>(AsDoubleConstant()->GetValue()) == bit_cast<uint64_t>(1.0); + } + DECLARE_INSTRUCTION(DoubleConstant); private: @@ -1925,6 +1964,10 @@ class HIntConstant : public HConstant { // method is an workaround until we fix the above. bool ActAsNullConstant() const OVERRIDE { return value_ == 0; } + bool IsMinusOne() const OVERRIDE { return GetValue() == -1; } + bool IsZero() const OVERRIDE { return GetValue() == 0; } + bool IsOne() const OVERRIDE { return GetValue() == 1; } + DECLARE_INSTRUCTION(IntConstant); private: @@ -1945,6 +1988,10 @@ class HLongConstant : public HConstant { size_t ComputeHashCode() const OVERRIDE { return static_cast<size_t>(GetValue()); } + bool IsMinusOne() const OVERRIDE { return GetValue() == -1; } + bool IsZero() const OVERRIDE { return GetValue() == 0; } + bool IsOne() const OVERRIDE { return GetValue() == 1; } + DECLARE_INSTRUCTION(LongConstant); private: @@ -3473,6 +3520,12 @@ class HBlocksInLoopIterator : public ValueObject { DISALLOW_COPY_AND_ASSIGN(HBlocksInLoopIterator); }; +inline int64_t Int64FromConstant(HConstant* constant) { + DCHECK(constant->IsIntConstant() || constant->IsLongConstant()); + return constant->IsIntConstant() ? constant->AsIntConstant()->GetValue() + : constant->AsLongConstant()->GetValue(); +} + } // namespace art #endif // ART_COMPILER_OPTIMIZING_NODES_H_ diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h index 5283d5dcca..79bebd2e64 100644 --- a/compiler/optimizing/stack_map_stream.h +++ b/compiler/optimizing/stack_map_stream.h @@ -56,11 +56,6 @@ class StackMapStream : public ValueObject { size_t inline_infos_start_index; }; - struct DexRegisterEntry { - DexRegisterMap::LocationKind kind; - int32_t value; - }; - struct InlineInfoEntry { uint32_t method_index; }; @@ -90,11 +85,11 @@ class StackMapStream : public ValueObject { } } - void AddDexRegisterEntry(DexRegisterMap::LocationKind kind, int32_t value) { - DexRegisterEntry entry; - entry.kind = kind; - entry.value = value; - dex_register_maps_.Add(entry); + void AddDexRegisterEntry(DexRegisterLocation::Kind kind, int32_t value) { + // Ensure we only use non-compressed location kind at this stage. + DCHECK(DexRegisterLocation::IsShortLocationKind(kind)) + << DexRegisterLocation::PrettyDescriptor(kind); + dex_register_maps_.Add(DexRegisterLocation(kind, value)); } void AddInlineInfoEntry(uint32_t method_index) { @@ -106,7 +101,7 @@ class StackMapStream : public ValueObject { size_t ComputeNeededSize() const { return CodeInfo::kFixedSize + ComputeStackMapSize() - + ComputeDexRegisterMapSize() + + ComputeDexRegisterMapsSize() + ComputeInlineInfoSize(); } @@ -114,27 +109,44 @@ class StackMapStream : public ValueObject { return stack_maps_.Size() * StackMap::ComputeAlignedStackMapSize(stack_mask_max_); } - size_t ComputeDexRegisterMapSize() const { - // We currently encode all dex register information per stack map. - return stack_maps_.Size() * DexRegisterMap::kFixedSize - // For each dex register entry. - + (dex_register_maps_.Size() * DexRegisterMap::SingleEntrySize()); + // Compute the size of the Dex register map of `entry`. + size_t ComputeDexRegisterMapSize(const StackMapEntry& entry) const { + size_t size = DexRegisterMap::kFixedSize; + for (size_t j = 0; j < entry.num_dex_registers; ++j) { + DexRegisterLocation dex_register_location = + dex_register_maps_.Get(entry.dex_register_maps_start_index + j); + size += DexRegisterMap::EntrySize(dex_register_location); + } + return size; + } + + // Compute the size of all the Dex register maps. + size_t ComputeDexRegisterMapsSize() const { + size_t size = stack_maps_.Size() * DexRegisterMap::kFixedSize; + // The size of each register location depends on the type of + // the entry. + for (size_t i = 0, e = dex_register_maps_.Size(); i < e; ++i) { + DexRegisterLocation entry = dex_register_maps_.Get(i); + size += DexRegisterMap::EntrySize(entry); + } + return size; } + // Compute the size of all the inline information pieces. size_t ComputeInlineInfoSize() const { return inline_infos_.Size() * InlineInfo::SingleEntrySize() // For encoding the depth. + (number_of_stack_maps_with_inline_info_ * InlineInfo::kFixedSize); } - size_t ComputeInlineInfoStart() const { - return ComputeDexRegisterMapStart() + ComputeDexRegisterMapSize(); - } - size_t ComputeDexRegisterMapStart() const { return CodeInfo::kFixedSize + ComputeStackMapSize(); } + size_t ComputeInlineInfoStart() const { + return ComputeDexRegisterMapStart() + ComputeDexRegisterMapsSize(); + } + void FillIn(MemoryRegion region) { CodeInfo code_info(region); code_info.SetOverallSize(region.size()); @@ -144,7 +156,7 @@ class StackMapStream : public ValueObject { MemoryRegion dex_register_maps_region = region.Subregion( ComputeDexRegisterMapStart(), - ComputeDexRegisterMapSize()); + ComputeDexRegisterMapsSize()); MemoryRegion inline_infos_region = region.Subregion( ComputeInlineInfoStart(), @@ -167,20 +179,25 @@ class StackMapStream : public ValueObject { } if (entry.num_dex_registers != 0) { - // Set the register map. - MemoryRegion register_region = dex_register_maps_region.Subregion( - next_dex_register_map_offset, - DexRegisterMap::kFixedSize - + entry.num_dex_registers * DexRegisterMap::SingleEntrySize()); + // Set the Dex register map. + MemoryRegion register_region = + dex_register_maps_region.Subregion( + next_dex_register_map_offset, + ComputeDexRegisterMapSize(entry)); next_dex_register_map_offset += register_region.size(); DexRegisterMap dex_register_map(register_region); stack_map.SetDexRegisterMapOffset(register_region.start() - memory_start); + // Offset in `dex_register_map` where to store the next register entry. + size_t offset = DexRegisterMap::kFixedSize; for (size_t j = 0; j < entry.num_dex_registers; ++j) { - DexRegisterEntry register_entry = - dex_register_maps_.Get(j + entry.dex_register_maps_start_index); - dex_register_map.SetRegisterInfo(j, register_entry.kind, register_entry.value); + DexRegisterLocation dex_register_location = + dex_register_maps_.Get(entry.dex_register_maps_start_index + j); + dex_register_map.SetRegisterInfo(offset, dex_register_location); + offset += DexRegisterMap::EntrySize(dex_register_location); } + // Ensure we reached the end of the Dex registers region. + DCHECK_EQ(offset, register_region.size()); } else { stack_map.SetDexRegisterMapOffset(StackMap::kNoDexRegisterMap); } @@ -208,7 +225,7 @@ class StackMapStream : public ValueObject { private: GrowableArray<StackMapEntry> stack_maps_; - GrowableArray<DexRegisterEntry> dex_register_maps_; + GrowableArray<DexRegisterLocation> dex_register_maps_; GrowableArray<InlineInfoEntry> inline_infos_; int stack_mask_max_; size_t number_of_stack_maps_with_inline_info_; diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc index 5b025106ac..3a5f80686d 100644 --- a/compiler/optimizing/stack_map_test.cc +++ b/compiler/optimizing/stack_map_test.cc @@ -22,7 +22,7 @@ namespace art { -bool SameBits(MemoryRegion region, const BitVector& bit_vector) { +static bool SameBits(MemoryRegion region, const BitVector& bit_vector) { for (size_t i = 0; i < region.size_in_bits(); ++i) { if (region.LoadBit(i) != bit_vector.IsBitSet(i)) { return false; @@ -31,9 +31,9 @@ bool SameBits(MemoryRegion region, const BitVector& bit_vector) { return true; } -size_t ComputeDexRegisterMapSize(size_t number_of_dex_registers) { - return DexRegisterMap::kFixedSize - + number_of_dex_registers * DexRegisterMap::SingleEntrySize(); +static size_t ComputeDexRegisterMapSize(const DexRegisterMap& dex_registers, + size_t number_of_dex_registers) { + return dex_registers.FindLocationOffset(number_of_dex_registers); } TEST(StackMapTest, Test1) { @@ -44,8 +44,8 @@ TEST(StackMapTest, Test1) { ArenaBitVector sp_mask(&arena, 0, false); size_t number_of_dex_registers = 2; stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); - stream.AddDexRegisterEntry(DexRegisterMap::kInStack, 0); - stream.AddDexRegisterEntry(DexRegisterMap::kConstant, -2); + stream.AddDexRegisterEntry(DexRegisterLocation::Kind::kInStack, 0); + stream.AddDexRegisterEntry(DexRegisterLocation::Kind::kConstant, -2); size_t size = stream.ComputeNeededSize(); void* memory = arena.Alloc(size, kArenaAllocMisc); @@ -67,14 +67,17 @@ TEST(StackMapTest, Test1) { ASSERT_TRUE(SameBits(stack_mask, sp_mask)); ASSERT_TRUE(stack_map.HasDexRegisterMap()); - DexRegisterMap dex_registers = - code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); - ASSERT_EQ(16u, dex_registers.Size()); - ASSERT_EQ(16u, ComputeDexRegisterMapSize(number_of_dex_registers)); - ASSERT_EQ(DexRegisterMap::kInStack, dex_registers.GetLocationKind(0)); - ASSERT_EQ(DexRegisterMap::kConstant, dex_registers.GetLocationKind(1)); - ASSERT_EQ(0, dex_registers.GetValue(0)); - ASSERT_EQ(-2, dex_registers.GetValue(1)); + DexRegisterMap dex_registers = code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); + ASSERT_EQ(6u, dex_registers.Size()); + ASSERT_EQ(6u, ComputeDexRegisterMapSize(dex_registers, number_of_dex_registers)); + DexRegisterLocation location0 = dex_registers.GetLocationKindAndValue(0); + DexRegisterLocation location1 = dex_registers.GetLocationKindAndValue(1); + ASSERT_EQ(DexRegisterLocation::Kind::kInStack, location0.GetKind()); + ASSERT_EQ(DexRegisterLocation::Kind::kConstant, location1.GetKind()); + ASSERT_EQ(DexRegisterLocation::Kind::kInStack, location0.GetInternalKind()); + ASSERT_EQ(DexRegisterLocation::Kind::kConstantLargeValue, location1.GetInternalKind()); + ASSERT_EQ(0, location0.GetValue()); + ASSERT_EQ(-2, location1.GetValue()); ASSERT_FALSE(stack_map.HasInlineInfo()); } @@ -89,8 +92,8 @@ TEST(StackMapTest, Test2) { sp_mask1.SetBit(4); size_t number_of_dex_registers = 2; stream.AddStackMapEntry(0, 64, 0x3, &sp_mask1, number_of_dex_registers, 2); - stream.AddDexRegisterEntry(DexRegisterMap::kInStack, 0); - stream.AddDexRegisterEntry(DexRegisterMap::kConstant, -2); + stream.AddDexRegisterEntry(DexRegisterLocation::Kind::kInStack, 0); + stream.AddDexRegisterEntry(DexRegisterLocation::Kind::kConstant, -2); stream.AddInlineInfoEntry(42); stream.AddInlineInfoEntry(82); @@ -98,8 +101,8 @@ TEST(StackMapTest, Test2) { sp_mask2.SetBit(3); sp_mask1.SetBit(8); stream.AddStackMapEntry(1, 128, 0xFF, &sp_mask2, number_of_dex_registers, 0); - stream.AddDexRegisterEntry(DexRegisterMap::kInRegister, 18); - stream.AddDexRegisterEntry(DexRegisterMap::kInFpuRegister, 3); + stream.AddDexRegisterEntry(DexRegisterLocation::Kind::kInRegister, 18); + stream.AddDexRegisterEntry(DexRegisterLocation::Kind::kInFpuRegister, 3); size_t size = stream.ComputeNeededSize(); void* memory = arena.Alloc(size, kArenaAllocMisc); @@ -111,54 +114,66 @@ TEST(StackMapTest, Test2) { ASSERT_EQ(2u, code_info.GetNumberOfStackMaps()); // First stack map. - StackMap stack_map = code_info.GetStackMapAt(0); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0))); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64))); - ASSERT_EQ(0u, stack_map.GetDexPc()); - ASSERT_EQ(64u, stack_map.GetNativePcOffset()); - ASSERT_EQ(0x3u, stack_map.GetRegisterMask()); - - MemoryRegion stack_mask = stack_map.GetStackMask(); - ASSERT_TRUE(SameBits(stack_mask, sp_mask1)); - - ASSERT_TRUE(stack_map.HasDexRegisterMap()); - DexRegisterMap dex_registers = - code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); - ASSERT_EQ(16u, dex_registers.Size()); - ASSERT_EQ(16u, ComputeDexRegisterMapSize(number_of_dex_registers)); - ASSERT_EQ(DexRegisterMap::kInStack, dex_registers.GetLocationKind(0)); - ASSERT_EQ(DexRegisterMap::kConstant, dex_registers.GetLocationKind(1)); - ASSERT_EQ(0, dex_registers.GetValue(0)); - ASSERT_EQ(-2, dex_registers.GetValue(1)); - - ASSERT_TRUE(stack_map.HasInlineInfo()); - InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map); - ASSERT_EQ(2u, inline_info.GetDepth()); - ASSERT_EQ(42u, inline_info.GetMethodReferenceIndexAtDepth(0)); - ASSERT_EQ(82u, inline_info.GetMethodReferenceIndexAtDepth(1)); + { + StackMap stack_map = code_info.GetStackMapAt(0); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64))); + ASSERT_EQ(0u, stack_map.GetDexPc()); + ASSERT_EQ(64u, stack_map.GetNativePcOffset()); + ASSERT_EQ(0x3u, stack_map.GetRegisterMask()); + + MemoryRegion stack_mask = stack_map.GetStackMask(); + ASSERT_TRUE(SameBits(stack_mask, sp_mask1)); + + ASSERT_TRUE(stack_map.HasDexRegisterMap()); + DexRegisterMap dex_registers = + code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); + ASSERT_EQ(6u, dex_registers.Size()); + ASSERT_EQ(6u, ComputeDexRegisterMapSize(dex_registers, number_of_dex_registers)); + DexRegisterLocation location0 = dex_registers.GetLocationKindAndValue(0); + DexRegisterLocation location1 = dex_registers.GetLocationKindAndValue(1); + ASSERT_EQ(DexRegisterLocation::Kind::kInStack, location0.GetKind()); + ASSERT_EQ(DexRegisterLocation::Kind::kConstant, location1.GetKind()); + ASSERT_EQ(DexRegisterLocation::Kind::kInStack, location0.GetInternalKind()); + ASSERT_EQ(DexRegisterLocation::Kind::kConstantLargeValue, location1.GetInternalKind()); + ASSERT_EQ(0, location0.GetValue()); + ASSERT_EQ(-2, location1.GetValue()); + + ASSERT_TRUE(stack_map.HasInlineInfo()); + InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map); + ASSERT_EQ(2u, inline_info.GetDepth()); + ASSERT_EQ(42u, inline_info.GetMethodReferenceIndexAtDepth(0)); + ASSERT_EQ(82u, inline_info.GetMethodReferenceIndexAtDepth(1)); + } // Second stack map. - stack_map = code_info.GetStackMapAt(1); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(1u))); - ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(128u))); - ASSERT_EQ(1u, stack_map.GetDexPc()); - ASSERT_EQ(128u, stack_map.GetNativePcOffset()); - ASSERT_EQ(0xFFu, stack_map.GetRegisterMask()); - - stack_mask = stack_map.GetStackMask(); - ASSERT_TRUE(SameBits(stack_mask, sp_mask2)); - - ASSERT_TRUE(stack_map.HasDexRegisterMap()); - dex_registers = - code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); - ASSERT_EQ(16u, dex_registers.Size()); - ASSERT_EQ(16u, ComputeDexRegisterMapSize(number_of_dex_registers)); - ASSERT_EQ(DexRegisterMap::kInRegister, dex_registers.GetLocationKind(0)); - ASSERT_EQ(DexRegisterMap::kInFpuRegister, dex_registers.GetLocationKind(1)); - ASSERT_EQ(18, dex_registers.GetValue(0)); - ASSERT_EQ(3, dex_registers.GetValue(1)); - - ASSERT_FALSE(stack_map.HasInlineInfo()); + { + StackMap stack_map = code_info.GetStackMapAt(1); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(1u))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(128u))); + ASSERT_EQ(1u, stack_map.GetDexPc()); + ASSERT_EQ(128u, stack_map.GetNativePcOffset()); + ASSERT_EQ(0xFFu, stack_map.GetRegisterMask()); + + MemoryRegion stack_mask = stack_map.GetStackMask(); + ASSERT_TRUE(SameBits(stack_mask, sp_mask2)); + + ASSERT_TRUE(stack_map.HasDexRegisterMap()); + DexRegisterMap dex_registers = + code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); + ASSERT_EQ(2u, dex_registers.Size()); + ASSERT_EQ(2u, ComputeDexRegisterMapSize(dex_registers, number_of_dex_registers)); + DexRegisterLocation location0 = dex_registers.GetLocationKindAndValue(0); + DexRegisterLocation location1 = dex_registers.GetLocationKindAndValue(1); + ASSERT_EQ(DexRegisterLocation::Kind::kInRegister, location0.GetKind()); + ASSERT_EQ(DexRegisterLocation::Kind::kInFpuRegister, location1.GetKind()); + ASSERT_EQ(DexRegisterLocation::Kind::kInRegister, location0.GetInternalKind()); + ASSERT_EQ(DexRegisterLocation::Kind::kInFpuRegister, location1.GetInternalKind()); + ASSERT_EQ(18, location0.GetValue()); + ASSERT_EQ(3, location1.GetValue()); + + ASSERT_FALSE(stack_map.HasInlineInfo()); + } } } // namespace art |