diff options
| -rw-r--r-- | compiler/optimizing/code_generator_arm.cc | 425 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator_arm.h | 1 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator_arm_vixl.cc | 414 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator_arm_vixl.h | 3 | ||||
| -rw-r--r-- | runtime/class_linker_test.cc | 2 | ||||
| -rw-r--r-- | runtime/mirror/class_ext.cc | 4 | ||||
| -rw-r--r-- | runtime/mirror/class_ext.h | 8 | ||||
| -rw-r--r-- | runtime/openjdkjvmti/ti_class.cc | 2 | ||||
| -rw-r--r-- | runtime/openjdkjvmti/ti_redefine.cc | 43 | ||||
| -rw-r--r-- | runtime/openjdkjvmti/ti_redefine.h | 4 | ||||
| -rw-r--r-- | runtime/openjdkjvmti/transform.cc | 25 | ||||
| -rw-r--r-- | test/981-dedup-original-dex/expected.txt | 0 | ||||
| -rw-r--r-- | test/981-dedup-original-dex/info.txt | 4 | ||||
| -rwxr-xr-x | test/981-dedup-original-dex/run | 17 | ||||
| -rw-r--r-- | test/981-dedup-original-dex/src/Main.java | 139 | ||||
| -rw-r--r-- | test/981-dedup-original-dex/src/Transform.java | 21 | ||||
| -rw-r--r-- | test/981-dedup-original-dex/src/Transform2.java | 21 | ||||
| -rwxr-xr-x | test/testrunner/run_build_test_target.py | 2 | ||||
| -rw-r--r-- | test/ti-agent/common_load.cc | 1 | ||||
| -rw-r--r-- | tools/art | 147 |
20 files changed, 845 insertions, 438 deletions
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index caea250ab6..d735b27090 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -1602,14 +1602,20 @@ static void GenerateVcmp(HInstruction* instruction, CodeGeneratorARM* codegen) { } } -static Condition GenerateLongTestConstant(HCondition* condition, - bool invert, - CodeGeneratorARM* codegen) { +static std::pair<Condition, Condition> GenerateLongTestConstant(HCondition* condition, + bool invert, + CodeGeneratorARM* codegen) { DCHECK_EQ(condition->GetLeft()->GetType(), Primitive::kPrimLong); const LocationSummary* const locations = condition->GetLocations(); - IfCondition cond = invert ? condition->GetOppositeCondition() : condition->GetCondition(); - Condition ret = EQ; + IfCondition cond = condition->GetCondition(); + IfCondition opposite = condition->GetOppositeCondition(); + + if (invert) { + std::swap(cond, opposite); + } + + std::pair<Condition, Condition> ret; const Location left = locations->InAt(0); const Location right = locations->InAt(1); @@ -1629,22 +1635,26 @@ static Condition GenerateLongTestConstant(HCondition* condition, __ CmpConstant(left_high, High32Bits(value)); __ it(EQ); __ cmp(left_low, ShifterOperand(Low32Bits(value)), EQ); - ret = ARMUnsignedCondition(cond); + ret = std::make_pair(ARMUnsignedCondition(cond), ARMUnsignedCondition(opposite)); break; case kCondLE: case kCondGT: // Trivially true or false. if (value == std::numeric_limits<int64_t>::max()) { __ cmp(left_low, ShifterOperand(left_low)); - ret = cond == kCondLE ? EQ : NE; + ret = cond == kCondLE ? std::make_pair(EQ, NE) : std::make_pair(NE, EQ); break; } if (cond == kCondLE) { + DCHECK_EQ(opposite, kCondGT); cond = kCondLT; + opposite = kCondGE; } else { DCHECK_EQ(cond, kCondGT); + DCHECK_EQ(opposite, kCondLE); cond = kCondGE; + opposite = kCondLT; } value++; @@ -1653,7 +1663,7 @@ static Condition GenerateLongTestConstant(HCondition* condition, case kCondLT: __ CmpConstant(left_low, Low32Bits(value)); __ sbcs(IP, left_high, ShifterOperand(High32Bits(value))); - ret = ARMCondition(cond); + ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite)); break; default: LOG(FATAL) << "Unreachable"; @@ -1663,14 +1673,20 @@ static Condition GenerateLongTestConstant(HCondition* condition, return ret; } -static Condition GenerateLongTest(HCondition* condition, - bool invert, - CodeGeneratorARM* codegen) { +static std::pair<Condition, Condition> GenerateLongTest(HCondition* condition, + bool invert, + CodeGeneratorARM* codegen) { DCHECK_EQ(condition->GetLeft()->GetType(), Primitive::kPrimLong); const LocationSummary* const locations = condition->GetLocations(); - IfCondition cond = invert ? condition->GetOppositeCondition() : condition->GetCondition(); - Condition ret = EQ; + IfCondition cond = condition->GetCondition(); + IfCondition opposite = condition->GetOppositeCondition(); + + if (invert) { + std::swap(cond, opposite); + } + + std::pair<Condition, Condition> ret; Location left = locations->InAt(0); Location right = locations->InAt(1); @@ -1689,15 +1705,19 @@ static Condition GenerateLongTest(HCondition* condition, __ cmp(left.AsRegisterPairLow<Register>(), ShifterOperand(right.AsRegisterPairLow<Register>()), EQ); - ret = ARMUnsignedCondition(cond); + ret = std::make_pair(ARMUnsignedCondition(cond), ARMUnsignedCondition(opposite)); break; case kCondLE: case kCondGT: if (cond == kCondLE) { + DCHECK_EQ(opposite, kCondGT); cond = kCondGE; + opposite = kCondLT; } else { DCHECK_EQ(cond, kCondGT); + DCHECK_EQ(opposite, kCondLE); cond = kCondLT; + opposite = kCondGE; } std::swap(left, right); @@ -1709,7 +1729,7 @@ static Condition GenerateLongTest(HCondition* condition, __ sbcs(IP, left.AsRegisterPairHigh<Register>(), ShifterOperand(right.AsRegisterPairHigh<Register>())); - ret = ARMCondition(cond); + ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite)); break; default: LOG(FATAL) << "Unreachable"; @@ -1719,90 +1739,83 @@ static Condition GenerateLongTest(HCondition* condition, return ret; } -static Condition GenerateTest(HInstruction* instruction, - Location loc, - bool invert, - CodeGeneratorARM* codegen) { - DCHECK(!instruction->IsConstant()); +static std::pair<Condition, Condition> GenerateTest(HCondition* condition, + bool invert, + CodeGeneratorARM* codegen) { + const LocationSummary* const locations = condition->GetLocations(); + const Primitive::Type type = condition->GetLeft()->GetType(); + IfCondition cond = condition->GetCondition(); + IfCondition opposite = condition->GetOppositeCondition(); + std::pair<Condition, Condition> ret; + const Location right = locations->InAt(1); - Condition ret = invert ? EQ : NE; + if (invert) { + std::swap(cond, opposite); + } - if (IsBooleanValueOrMaterializedCondition(instruction)) { - __ CmpConstant(loc.AsRegister<Register>(), 0); + if (type == Primitive::kPrimLong) { + ret = locations->InAt(1).IsConstant() + ? GenerateLongTestConstant(condition, invert, codegen) + : GenerateLongTest(condition, invert, codegen); + } else if (Primitive::IsFloatingPointType(type)) { + GenerateVcmp(condition, codegen); + __ vmstat(); + ret = std::make_pair(ARMFPCondition(cond, condition->IsGtBias()), + ARMFPCondition(opposite, condition->IsGtBias())); } else { - HCondition* const condition = instruction->AsCondition(); - const LocationSummary* const locations = condition->GetLocations(); - const Primitive::Type type = condition->GetLeft()->GetType(); - const IfCondition cond = invert ? condition->GetOppositeCondition() : condition->GetCondition(); - const Location right = locations->InAt(1); - - if (type == Primitive::kPrimLong) { - ret = condition->GetLocations()->InAt(1).IsConstant() - ? GenerateLongTestConstant(condition, invert, codegen) - : GenerateLongTest(condition, invert, codegen); - } else if (Primitive::IsFloatingPointType(type)) { - GenerateVcmp(condition, codegen); - __ vmstat(); - ret = ARMFPCondition(cond, condition->IsGtBias()); - } else { - DCHECK(Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) << type; + DCHECK(Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) << type; - const Register left = locations->InAt(0).AsRegister<Register>(); + const Register left = locations->InAt(0).AsRegister<Register>(); - if (right.IsRegister()) { - __ cmp(left, ShifterOperand(right.AsRegister<Register>())); - } else { - DCHECK(right.IsConstant()); - __ CmpConstant(left, CodeGenerator::GetInt32ValueOf(right.GetConstant())); - } - - ret = ARMCondition(cond); + if (right.IsRegister()) { + __ cmp(left, ShifterOperand(right.AsRegister<Register>())); + } else { + DCHECK(right.IsConstant()); + __ CmpConstant(left, CodeGenerator::GetInt32ValueOf(right.GetConstant())); } + + ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite)); } return ret; } -static bool CanGenerateTest(HInstruction* condition, ArmAssembler* assembler) { - if (!IsBooleanValueOrMaterializedCondition(condition)) { - const HCondition* const cond = condition->AsCondition(); - - if (cond->GetLeft()->GetType() == Primitive::kPrimLong) { - const LocationSummary* const locations = cond->GetLocations(); - const IfCondition c = cond->GetCondition(); - - if (locations->InAt(1).IsConstant()) { - const int64_t value = locations->InAt(1).GetConstant()->AsLongConstant()->GetValue(); - ShifterOperand so; - - if (c < kCondLT || c > kCondGE) { - // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8, - // we check that the least significant half of the first input to be compared - // is in a low register (the other half is read outside an IT block), and - // the constant fits in an 8-bit unsigned integer, so that a 16-bit CMP - // encoding can be used. - if (!ArmAssembler::IsLowRegister(locations->InAt(0).AsRegisterPairLow<Register>()) || - !IsUint<8>(Low32Bits(value))) { - return false; - } - } else if (c == kCondLE || c == kCondGT) { - if (value < std::numeric_limits<int64_t>::max() && - !assembler->ShifterOperandCanHold(kNoRegister, - kNoRegister, - SBC, - High32Bits(value + 1), - kCcSet, - &so)) { - return false; - } - } else if (!assembler->ShifterOperandCanHold(kNoRegister, - kNoRegister, - SBC, - High32Bits(value), - kCcSet, - &so)) { +static bool CanGenerateTest(HCondition* condition, ArmAssembler* assembler) { + if (condition->GetLeft()->GetType() == Primitive::kPrimLong) { + const LocationSummary* const locations = condition->GetLocations(); + const IfCondition c = condition->GetCondition(); + + if (locations->InAt(1).IsConstant()) { + const int64_t value = locations->InAt(1).GetConstant()->AsLongConstant()->GetValue(); + ShifterOperand so; + + if (c < kCondLT || c > kCondGE) { + // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8, + // we check that the least significant half of the first input to be compared + // is in a low register (the other half is read outside an IT block), and + // the constant fits in an 8-bit unsigned integer, so that a 16-bit CMP + // encoding can be used. + if (!ArmAssembler::IsLowRegister(locations->InAt(0).AsRegisterPairLow<Register>()) || + !IsUint<8>(Low32Bits(value))) { + return false; + } + } else if (c == kCondLE || c == kCondGT) { + if (value < std::numeric_limits<int64_t>::max() && + !assembler->ShifterOperandCanHold(kNoRegister, + kNoRegister, + SBC, + High32Bits(value + 1), + kCcSet, + &so)) { return false; } + } else if (!assembler->ShifterOperandCanHold(kNoRegister, + kNoRegister, + SBC, + High32Bits(value), + kCcSet, + &so)) { + return false; } } } @@ -2415,13 +2428,6 @@ void LocationsBuilderARM::VisitExit(HExit* exit) { void InstructionCodeGeneratorARM::VisitExit(HExit* exit ATTRIBUTE_UNUSED) { } -void InstructionCodeGeneratorARM::GenerateFPJumps(HCondition* cond, - Label* true_label, - Label* false_label ATTRIBUTE_UNUSED) { - __ vmstat(); // transfer FP status register to ARM APSR. - __ b(true_label, ARMFPCondition(cond->GetCondition(), cond->IsGtBias())); -} - void InstructionCodeGeneratorARM::GenerateLongComparesAndJumps(HCondition* cond, Label* true_label, Label* false_label) { @@ -2438,7 +2444,6 @@ void InstructionCodeGeneratorARM::GenerateLongComparesAndJumps(HCondition* cond, // Set the conditions for the test, remembering that == needs to be // decided using the low words. - // TODO: consider avoiding jumps with temporary and CMP low+SBC high switch (if_cond) { case kCondEQ: case kCondNE: @@ -2509,25 +2514,38 @@ void InstructionCodeGeneratorARM::GenerateLongComparesAndJumps(HCondition* cond, void InstructionCodeGeneratorARM::GenerateCompareTestAndBranch(HCondition* condition, Label* true_target_in, Label* false_target_in) { + if (CanGenerateTest(condition, codegen_->GetAssembler())) { + Label* non_fallthrough_target; + bool invert; + + if (true_target_in == nullptr) { + DCHECK(false_target_in != nullptr); + non_fallthrough_target = false_target_in; + invert = true; + } else { + non_fallthrough_target = true_target_in; + invert = false; + } + + const auto cond = GenerateTest(condition, invert, codegen_); + + __ b(non_fallthrough_target, cond.first); + + if (false_target_in != nullptr && false_target_in != non_fallthrough_target) { + __ b(false_target_in); + } + + return; + } + // Generated branching requires both targets to be explicit. If either of the // targets is nullptr (fallthrough) use and bind `fallthrough_target` instead. Label fallthrough_target; Label* true_target = true_target_in == nullptr ? &fallthrough_target : true_target_in; Label* false_target = false_target_in == nullptr ? &fallthrough_target : false_target_in; - Primitive::Type type = condition->InputAt(0)->GetType(); - switch (type) { - case Primitive::kPrimLong: - GenerateLongComparesAndJumps(condition, true_target, false_target); - break; - case Primitive::kPrimFloat: - case Primitive::kPrimDouble: - GenerateVcmp(condition, codegen_); - GenerateFPJumps(condition, true_target, false_target); - break; - default: - LOG(FATAL) << "Unexpected compare type " << type; - } + DCHECK_EQ(condition->InputAt(0)->GetType(), Primitive::kPrimLong); + GenerateLongComparesAndJumps(condition, true_target, false_target); if (false_target != &fallthrough_target) { __ b(false_target); @@ -2729,7 +2747,8 @@ void InstructionCodeGeneratorARM::VisitSelect(HSelect* select) { } if (!Primitive::IsFloatingPointType(type) && - CanGenerateTest(condition, codegen_->GetAssembler())) { + (IsBooleanValueOrMaterializedCondition(condition) || + CanGenerateTest(condition->AsCondition(), codegen_->GetAssembler()))) { bool invert = false; if (out.Equals(second)) { @@ -2753,7 +2772,14 @@ void InstructionCodeGeneratorARM::VisitSelect(HSelect* select) { codegen_->MoveLocation(out, src.Equals(first) ? second : first, type); } - const Condition cond = GenerateTest(condition, locations->InAt(2), invert, codegen_); + std::pair<Condition, Condition> cond; + + if (IsBooleanValueOrMaterializedCondition(condition)) { + __ CmpConstant(locations->InAt(2).AsRegister<Register>(), 0); + cond = invert ? std::make_pair(EQ, NE) : std::make_pair(NE, EQ); + } else { + cond = GenerateTest(condition->AsCondition(), invert, codegen_); + } if (out.IsRegister()) { ShifterOperand operand; @@ -2765,8 +2791,8 @@ void InstructionCodeGeneratorARM::VisitSelect(HSelect* select) { operand = ShifterOperand(src.AsRegister<Register>()); } - __ it(cond); - __ mov(out.AsRegister<Register>(), operand, cond); + __ it(cond.first); + __ mov(out.AsRegister<Register>(), operand, cond.first); } else { DCHECK(out.IsRegisterPair()); @@ -2784,10 +2810,10 @@ void InstructionCodeGeneratorARM::VisitSelect(HSelect* select) { operand_low = ShifterOperand(src.AsRegisterPairLow<Register>()); } - __ it(cond); - __ mov(out.AsRegisterPairLow<Register>(), operand_low, cond); - __ it(cond); - __ mov(out.AsRegisterPairHigh<Register>(), operand_high, cond); + __ it(cond.first); + __ mov(out.AsRegisterPairLow<Register>(), operand_low, cond.first); + __ it(cond.first); + __ mov(out.AsRegisterPairHigh<Register>(), operand_high, cond.first); } return; @@ -2840,7 +2866,7 @@ void LocationsBuilderARM::HandleCondition(HCondition* cond) { locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1))); if (!cond->IsEmittedAtUseSite()) { - locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } break; @@ -2867,51 +2893,44 @@ void InstructionCodeGeneratorARM::HandleCondition(HCondition* cond) { return; } - LocationSummary* locations = cond->GetLocations(); - Location left = locations->InAt(0); - Location right = locations->InAt(1); - Register out = locations->Out().AsRegister<Register>(); - Label true_label, false_label; + const Register out = cond->GetLocations()->Out().AsRegister<Register>(); - switch (cond->InputAt(0)->GetType()) { - default: { - // Integer case. - if (right.IsRegister()) { - __ cmp(left.AsRegister<Register>(), ShifterOperand(right.AsRegister<Register>())); - } else { - DCHECK(right.IsConstant()); - __ CmpConstant(left.AsRegister<Register>(), - CodeGenerator::GetInt32ValueOf(right.GetConstant())); - } - __ it(ARMCondition(cond->GetCondition()), kItElse); - __ mov(locations->Out().AsRegister<Register>(), ShifterOperand(1), - ARMCondition(cond->GetCondition())); - __ mov(locations->Out().AsRegister<Register>(), ShifterOperand(0), - ARMCondition(cond->GetOppositeCondition())); - return; - } - case Primitive::kPrimLong: - GenerateLongComparesAndJumps(cond, &true_label, &false_label); - break; - case Primitive::kPrimFloat: - case Primitive::kPrimDouble: - GenerateVcmp(cond, codegen_); - GenerateFPJumps(cond, &true_label, &false_label); - break; + if (ArmAssembler::IsLowRegister(out) && CanGenerateTest(cond, codegen_->GetAssembler())) { + const auto condition = GenerateTest(cond, false, codegen_); + + __ it(condition.first); + __ mov(out, ShifterOperand(1), condition.first); + __ it(condition.second); + __ mov(out, ShifterOperand(0), condition.second); + return; } // Convert the jumps into the result. Label done_label; - Label* final_label = codegen_->GetFinalLabel(cond, &done_label); + Label* const final_label = codegen_->GetFinalLabel(cond, &done_label); - // False case: result = 0. - __ Bind(&false_label); - __ LoadImmediate(out, 0); - __ b(final_label); + if (cond->InputAt(0)->GetType() == Primitive::kPrimLong) { + Label true_label, false_label; - // True case: result = 1. - __ Bind(&true_label); - __ LoadImmediate(out, 1); + GenerateLongComparesAndJumps(cond, &true_label, &false_label); + + // False case: result = 0. + __ Bind(&false_label); + __ LoadImmediate(out, 0); + __ b(final_label); + + // True case: result = 1. + __ Bind(&true_label); + __ LoadImmediate(out, 1); + } else { + DCHECK(CanGenerateTest(cond, codegen_->GetAssembler())); + + const auto condition = GenerateTest(cond, false, codegen_); + + __ mov(out, ShifterOperand(0), AL, kCcKeep); + __ b(final_label, condition.second); + __ LoadImmediate(out, 1); + } if (done_label.IsLinked()) { __ Bind(&done_label); @@ -7039,14 +7058,16 @@ void InstructionCodeGeneratorARM::VisitInstanceOf(HInstanceOf* instruction) { uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value(); - Label done, zero; - Label* final_label = codegen_->GetFinalLabel(instruction, &done); + Label done; + Label* const final_label = codegen_->GetFinalLabel(instruction, &done); SlowPathCodeARM* slow_path = nullptr; // Return 0 if `obj` is null. // avoid null check if we know obj is not null. if (instruction->MustDoNullCheck()) { - __ CompareAndBranchIfZero(obj, &zero); + DCHECK_NE(out, obj); + __ LoadImmediate(out, 0); + __ CompareAndBranchIfZero(obj, final_label); } switch (type_check_kind) { @@ -7058,11 +7079,23 @@ void InstructionCodeGeneratorARM::VisitInstanceOf(HInstanceOf* instruction) { class_offset, maybe_temp_loc, kCompilerReadBarrierOption); - __ cmp(out, ShifterOperand(cls)); // Classes must be equal for the instanceof to succeed. - __ b(&zero, NE); - __ LoadImmediate(out, 1); - __ b(final_label); + __ cmp(out, ShifterOperand(cls)); + // We speculatively set the result to false without changing the condition + // flags, which allows us to avoid some branching later. + __ mov(out, ShifterOperand(0), AL, kCcKeep); + + // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8, + // we check that the output is in a low register, so that a 16-bit MOV + // encoding can be used. + if (ArmAssembler::IsLowRegister(out)) { + __ it(EQ); + __ mov(out, ShifterOperand(1), EQ); + } else { + __ b(final_label, NE); + __ LoadImmediate(out, 1); + } + break; } @@ -7084,14 +7117,11 @@ void InstructionCodeGeneratorARM::VisitInstanceOf(HInstanceOf* instruction) { super_offset, maybe_temp_loc, kCompilerReadBarrierOption); - // If `out` is null, we use it for the result, and jump to `done`. + // If `out` is null, we use it for the result, and jump to the final label. __ CompareAndBranchIfZero(out, final_label); __ cmp(out, ShifterOperand(cls)); __ b(&loop, NE); __ LoadImmediate(out, 1); - if (zero.IsLinked()) { - __ b(final_label); - } break; } @@ -7114,14 +7144,32 @@ void InstructionCodeGeneratorARM::VisitInstanceOf(HInstanceOf* instruction) { super_offset, maybe_temp_loc, kCompilerReadBarrierOption); - __ CompareAndBranchIfNonZero(out, &loop); - // If `out` is null, we use it for the result, and jump to `done`. - __ b(final_label); - __ Bind(&success); - __ LoadImmediate(out, 1); - if (zero.IsLinked()) { + // This is essentially a null check, but it sets the condition flags to the + // proper value for the code that follows the loop, i.e. not `EQ`. + __ cmp(out, ShifterOperand(1)); + __ b(&loop, HS); + + // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8, + // we check that the output is in a low register, so that a 16-bit MOV + // encoding can be used. + if (ArmAssembler::IsLowRegister(out)) { + // If `out` is null, we use it for the result, and the condition flags + // have already been set to `NE`, so the IT block that comes afterwards + // (and which handles the successful case) turns into a NOP (instead of + // overwriting `out`). + __ Bind(&success); + // There is only one branch to the `success` label (which is bound to this + // IT block), and it has the same condition, `EQ`, so in that case the MOV + // is executed. + __ it(EQ); + __ mov(out, ShifterOperand(1), EQ); + } else { + // If `out` is null, we use it for the result, and jump to the final label. __ b(final_label); + __ Bind(&success); + __ LoadImmediate(out, 1); } + break; } @@ -7144,14 +7192,28 @@ void InstructionCodeGeneratorARM::VisitInstanceOf(HInstanceOf* instruction) { component_offset, maybe_temp_loc, kCompilerReadBarrierOption); - // If `out` is null, we use it for the result, and jump to `done`. + // If `out` is null, we use it for the result, and jump to the final label. __ CompareAndBranchIfZero(out, final_label); __ LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset); static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot"); - __ CompareAndBranchIfNonZero(out, &zero); - __ Bind(&exact_check); - __ LoadImmediate(out, 1); - __ b(final_label); + __ cmp(out, ShifterOperand(0)); + // We speculatively set the result to false without changing the condition + // flags, which allows us to avoid some branching later. + __ mov(out, ShifterOperand(0), AL, kCcKeep); + + // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8, + // we check that the output is in a low register, so that a 16-bit MOV + // encoding can be used. + if (ArmAssembler::IsLowRegister(out)) { + __ Bind(&exact_check); + __ it(EQ); + __ mov(out, ShifterOperand(1), EQ); + } else { + __ b(final_label, NE); + __ Bind(&exact_check); + __ LoadImmediate(out, 1); + } + break; } @@ -7171,9 +7233,6 @@ void InstructionCodeGeneratorARM::VisitInstanceOf(HInstanceOf* instruction) { codegen_->AddSlowPath(slow_path); __ b(slow_path->GetEntryLabel(), NE); __ LoadImmediate(out, 1); - if (zero.IsLinked()) { - __ b(final_label); - } break; } @@ -7202,18 +7261,10 @@ void InstructionCodeGeneratorARM::VisitInstanceOf(HInstanceOf* instruction) { /* is_fatal */ false); codegen_->AddSlowPath(slow_path); __ b(slow_path->GetEntryLabel()); - if (zero.IsLinked()) { - __ b(final_label); - } break; } } - if (zero.IsLinked()) { - __ Bind(&zero); - __ LoadImmediate(out, 0); - } - if (done.IsLinked()) { __ Bind(&done); } diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index 59a7f7c048..86f2f21df7 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -299,7 +299,6 @@ class InstructionCodeGeneratorARM : public InstructionCodeGenerator { void GenerateCompareTestAndBranch(HCondition* condition, Label* true_target, Label* false_target); - void GenerateFPJumps(HCondition* cond, Label* true_label, Label* false_label); void GenerateLongComparesAndJumps(HCondition* cond, Label* true_label, Label* false_label); void DivRemOneOrMinusOne(HBinaryOperation* instruction); void DivRemByPowerOfTwo(HBinaryOperation* instruction); diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 2d2d8109a3..a1c3da9e9c 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -1687,14 +1687,21 @@ static void GenerateVcmp(HInstruction* instruction, CodeGeneratorARMVIXL* codege } } -static vixl32::Condition GenerateLongTestConstant(HCondition* condition, - bool invert, - CodeGeneratorARMVIXL* codegen) { +static std::pair<vixl32::Condition, vixl32::Condition> GenerateLongTestConstant( + HCondition* condition, + bool invert, + CodeGeneratorARMVIXL* codegen) { DCHECK_EQ(condition->GetLeft()->GetType(), Primitive::kPrimLong); const LocationSummary* const locations = condition->GetLocations(); - IfCondition cond = invert ? condition->GetOppositeCondition() : condition->GetCondition(); - vixl32::Condition ret = eq; + IfCondition cond = condition->GetCondition(); + IfCondition opposite = condition->GetOppositeCondition(); + + if (invert) { + std::swap(cond, opposite); + } + + std::pair<vixl32::Condition, vixl32::Condition> ret(eq, ne); const Location left = locations->InAt(0); const Location right = locations->InAt(1); @@ -1713,13 +1720,14 @@ static vixl32::Condition GenerateLongTestConstant(HCondition* condition, case kCondAE: { __ Cmp(left_high, High32Bits(value)); + // We use the scope because of the IT block that follows. ExactAssemblyScope guard(codegen->GetVIXLAssembler(), 2 * vixl32::k16BitT32InstructionSizeInBytes, CodeBufferCheckScope::kExactSize); __ it(eq); __ cmp(eq, left_low, Low32Bits(value)); - ret = ARMUnsignedCondition(cond); + ret = std::make_pair(ARMUnsignedCondition(cond), ARMUnsignedCondition(opposite)); break; } case kCondLE: @@ -1727,15 +1735,19 @@ static vixl32::Condition GenerateLongTestConstant(HCondition* condition, // Trivially true or false. if (value == std::numeric_limits<int64_t>::max()) { __ Cmp(left_low, left_low); - ret = cond == kCondLE ? eq : ne; + ret = cond == kCondLE ? std::make_pair(eq, ne) : std::make_pair(ne, eq); break; } if (cond == kCondLE) { + DCHECK_EQ(opposite, kCondGT); cond = kCondLT; + opposite = kCondGE; } else { DCHECK_EQ(cond, kCondGT); + DCHECK_EQ(opposite, kCondLE); cond = kCondGE; + opposite = kCondLT; } value++; @@ -1746,7 +1758,7 @@ static vixl32::Condition GenerateLongTestConstant(HCondition* condition, __ Cmp(left_low, Low32Bits(value)); __ Sbcs(temps.Acquire(), left_high, High32Bits(value)); - ret = ARMCondition(cond); + ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite)); break; } default: @@ -1757,14 +1769,21 @@ static vixl32::Condition GenerateLongTestConstant(HCondition* condition, return ret; } -static vixl32::Condition GenerateLongTest(HCondition* condition, - bool invert, - CodeGeneratorARMVIXL* codegen) { +static std::pair<vixl32::Condition, vixl32::Condition> GenerateLongTest( + HCondition* condition, + bool invert, + CodeGeneratorARMVIXL* codegen) { DCHECK_EQ(condition->GetLeft()->GetType(), Primitive::kPrimLong); const LocationSummary* const locations = condition->GetLocations(); - IfCondition cond = invert ? condition->GetOppositeCondition() : condition->GetCondition(); - vixl32::Condition ret = eq; + IfCondition cond = condition->GetCondition(); + IfCondition opposite = condition->GetOppositeCondition(); + + if (invert) { + std::swap(cond, opposite); + } + + std::pair<vixl32::Condition, vixl32::Condition> ret(eq, ne); Location left = locations->InAt(0); Location right = locations->InAt(1); @@ -1779,22 +1798,27 @@ static vixl32::Condition GenerateLongTest(HCondition* condition, case kCondAE: { __ Cmp(HighRegisterFrom(left), HighRegisterFrom(right)); + // We use the scope because of the IT block that follows. ExactAssemblyScope guard(codegen->GetVIXLAssembler(), 2 * vixl32::k16BitT32InstructionSizeInBytes, CodeBufferCheckScope::kExactSize); __ it(eq); __ cmp(eq, LowRegisterFrom(left), LowRegisterFrom(right)); - ret = ARMUnsignedCondition(cond); + ret = std::make_pair(ARMUnsignedCondition(cond), ARMUnsignedCondition(opposite)); break; } case kCondLE: case kCondGT: if (cond == kCondLE) { + DCHECK_EQ(opposite, kCondGT); cond = kCondGE; + opposite = kCondLT; } else { DCHECK_EQ(cond, kCondGT); + DCHECK_EQ(opposite, kCondLE); cond = kCondLT; + opposite = kCondGE; } std::swap(left, right); @@ -1805,7 +1829,7 @@ static vixl32::Condition GenerateLongTest(HCondition* condition, __ Cmp(LowRegisterFrom(left), LowRegisterFrom(right)); __ Sbcs(temps.Acquire(), HighRegisterFrom(left), HighRegisterFrom(right)); - ret = ARMCondition(cond); + ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite)); break; } default: @@ -1816,69 +1840,62 @@ static vixl32::Condition GenerateLongTest(HCondition* condition, return ret; } -static vixl32::Condition GenerateTest(HInstruction* instruction, - Location loc, - bool invert, - CodeGeneratorARMVIXL* codegen) { - DCHECK(!instruction->IsConstant()); +static std::pair<vixl32::Condition, vixl32::Condition> GenerateTest(HCondition* condition, + bool invert, + CodeGeneratorARMVIXL* codegen) { + const Primitive::Type type = condition->GetLeft()->GetType(); + IfCondition cond = condition->GetCondition(); + IfCondition opposite = condition->GetOppositeCondition(); + std::pair<vixl32::Condition, vixl32::Condition> ret(eq, ne); - vixl32::Condition ret = invert ? eq : ne; + if (invert) { + std::swap(cond, opposite); + } - if (IsBooleanValueOrMaterializedCondition(instruction)) { - __ Cmp(RegisterFrom(loc), 0); + if (type == Primitive::kPrimLong) { + ret = condition->GetLocations()->InAt(1).IsConstant() + ? GenerateLongTestConstant(condition, invert, codegen) + : GenerateLongTest(condition, invert, codegen); + } else if (Primitive::IsFloatingPointType(type)) { + GenerateVcmp(condition, codegen); + __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR); + ret = std::make_pair(ARMFPCondition(cond, condition->IsGtBias()), + ARMFPCondition(opposite, condition->IsGtBias())); } else { - HCondition* const condition = instruction->AsCondition(); - const Primitive::Type type = condition->GetLeft()->GetType(); - const IfCondition cond = invert ? condition->GetOppositeCondition() : condition->GetCondition(); - - if (type == Primitive::kPrimLong) { - ret = condition->GetLocations()->InAt(1).IsConstant() - ? GenerateLongTestConstant(condition, invert, codegen) - : GenerateLongTest(condition, invert, codegen); - } else if (Primitive::IsFloatingPointType(type)) { - GenerateVcmp(condition, codegen); - __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR); - ret = ARMFPCondition(cond, condition->IsGtBias()); - } else { - DCHECK(Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) << type; - __ Cmp(InputRegisterAt(condition, 0), InputOperandAt(condition, 1)); - ret = ARMCondition(cond); - } + DCHECK(Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) << type; + __ Cmp(InputRegisterAt(condition, 0), InputOperandAt(condition, 1)); + ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite)); } return ret; } -static bool CanGenerateTest(HInstruction* condition, ArmVIXLAssembler* assembler) { - if (!IsBooleanValueOrMaterializedCondition(condition)) { - const HCondition* const cond = condition->AsCondition(); - - if (cond->GetLeft()->GetType() == Primitive::kPrimLong) { - const LocationSummary* const locations = cond->GetLocations(); - const IfCondition c = cond->GetCondition(); +static bool CanGenerateTest(HCondition* condition, ArmVIXLAssembler* assembler) { + if (condition->GetLeft()->GetType() == Primitive::kPrimLong) { + const LocationSummary* const locations = condition->GetLocations(); + const IfCondition c = condition->GetCondition(); - if (locations->InAt(1).IsConstant()) { - const int64_t value = Int64ConstantFrom(locations->InAt(1)); + if (locations->InAt(1).IsConstant()) { + const int64_t value = Int64ConstantFrom(locations->InAt(1)); - if (c < kCondLT || c > kCondGE) { - // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8, - // we check that the least significant half of the first input to be compared - // is in a low register (the other half is read outside an IT block), and - // the constant fits in an 8-bit unsigned integer, so that a 16-bit CMP - // encoding can be used. - if (!LowRegisterFrom(locations->InAt(0)).IsLow() || !IsUint<8>(Low32Bits(value))) { - return false; - } - // TODO(VIXL): The rest of the checks are there to keep the backend in sync with - // the previous one, but are not strictly necessary. - } else if (c == kCondLE || c == kCondGT) { - if (value < std::numeric_limits<int64_t>::max() && - !assembler->ShifterOperandCanHold(SBC, High32Bits(value + 1), kCcSet)) { - return false; - } - } else if (!assembler->ShifterOperandCanHold(SBC, High32Bits(value), kCcSet)) { + if (c < kCondLT || c > kCondGE) { + // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8, + // we check that the least significant half of the first input to be compared + // is in a low register (the other half is read outside an IT block), and + // the constant fits in an 8-bit unsigned integer, so that a 16-bit CMP + // encoding can be used. + if (!LowRegisterFrom(locations->InAt(0)).IsLow() || !IsUint<8>(Low32Bits(value))) { + return false; + } + // TODO(VIXL): The rest of the checks are there to keep the backend in sync with + // the previous one, but are not strictly necessary. + } else if (c == kCondLE || c == kCondGT) { + if (value < std::numeric_limits<int64_t>::max() && + !assembler->ShifterOperandCanHold(SBC, High32Bits(value + 1), kCcSet)) { return false; } + } else if (!assembler->ShifterOperandCanHold(SBC, High32Bits(value), kCcSet)) { + return false; } } } @@ -2445,14 +2462,6 @@ void LocationsBuilderARMVIXL::VisitExit(HExit* exit) { void InstructionCodeGeneratorARMVIXL::VisitExit(HExit* exit ATTRIBUTE_UNUSED) { } -void InstructionCodeGeneratorARMVIXL::GenerateFPJumps(HCondition* cond, - vixl32::Label* true_label, - vixl32::Label* false_label ATTRIBUTE_UNUSED) { - // To branch on the result of the FP compare we transfer FPSCR to APSR (encoded as PC in VMRS). - __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR); - __ B(ARMFPCondition(cond->GetCondition(), cond->IsGtBias()), true_label); -} - void InstructionCodeGeneratorARMVIXL::GenerateLongComparesAndJumps(HCondition* cond, vixl32::Label* true_label, vixl32::Label* false_label) { @@ -2469,7 +2478,6 @@ void InstructionCodeGeneratorARMVIXL::GenerateLongComparesAndJumps(HCondition* c // Set the conditions for the test, remembering that == needs to be // decided using the low words. - // TODO: consider avoiding jumps with temporary and CMP low+SBC high switch (if_cond) { case kCondEQ: case kCondNE: @@ -2540,31 +2548,44 @@ void InstructionCodeGeneratorARMVIXL::GenerateLongComparesAndJumps(HCondition* c void InstructionCodeGeneratorARMVIXL::GenerateCompareTestAndBranch(HCondition* condition, vixl32::Label* true_target_in, vixl32::Label* false_target_in) { + if (CanGenerateTest(condition, codegen_->GetAssembler())) { + vixl32::Label* non_fallthrough_target; + bool invert; + + if (true_target_in == nullptr) { + DCHECK(false_target_in != nullptr); + non_fallthrough_target = false_target_in; + invert = true; + } else { + non_fallthrough_target = true_target_in; + invert = false; + } + + const auto cond = GenerateTest(condition, invert, codegen_); + + __ B(cond.first, non_fallthrough_target); + + if (false_target_in != nullptr && false_target_in != non_fallthrough_target) { + __ B(false_target_in); + } + + return; + } + // Generated branching requires both targets to be explicit. If either of the // targets is nullptr (fallthrough) use and bind `fallthrough` instead. vixl32::Label fallthrough; vixl32::Label* true_target = (true_target_in == nullptr) ? &fallthrough : true_target_in; vixl32::Label* false_target = (false_target_in == nullptr) ? &fallthrough : false_target_in; - Primitive::Type type = condition->InputAt(0)->GetType(); - switch (type) { - case Primitive::kPrimLong: - GenerateLongComparesAndJumps(condition, true_target, false_target); - break; - case Primitive::kPrimFloat: - case Primitive::kPrimDouble: - GenerateVcmp(condition, codegen_); - GenerateFPJumps(condition, true_target, false_target); - break; - default: - LOG(FATAL) << "Unexpected compare type " << type; - } + DCHECK_EQ(condition->InputAt(0)->GetType(), Primitive::kPrimLong); + GenerateLongComparesAndJumps(condition, true_target, false_target); if (false_target != &fallthrough) { __ B(false_target); } - if (true_target_in == nullptr || false_target_in == nullptr) { + if (fallthrough.IsReferenced()) { __ Bind(&fallthrough); } } @@ -2759,7 +2780,8 @@ void InstructionCodeGeneratorARMVIXL::VisitSelect(HSelect* select) { } if (!Primitive::IsFloatingPointType(type) && - CanGenerateTest(condition, codegen_->GetAssembler())) { + (IsBooleanValueOrMaterializedCondition(condition) || + CanGenerateTest(condition->AsCondition(), codegen_->GetAssembler()))) { bool invert = false; if (out.Equals(second)) { @@ -2783,15 +2805,24 @@ void InstructionCodeGeneratorARMVIXL::VisitSelect(HSelect* select) { codegen_->MoveLocation(out, src.Equals(first) ? second : first, type); } - const vixl32::Condition cond = GenerateTest(condition, locations->InAt(2), invert, codegen_); + std::pair<vixl32::Condition, vixl32::Condition> cond(eq, ne); + + if (IsBooleanValueOrMaterializedCondition(condition)) { + __ Cmp(InputRegisterAt(select, 2), 0); + cond = invert ? std::make_pair(eq, ne) : std::make_pair(ne, eq); + } else { + cond = GenerateTest(condition->AsCondition(), invert, codegen_); + } + const size_t instr_count = out.IsRegisterPair() ? 4 : 2; + // We use the scope because of the IT block that follows. ExactAssemblyScope guard(GetVIXLAssembler(), instr_count * vixl32::k16BitT32InstructionSizeInBytes, CodeBufferCheckScope::kExactSize); if (out.IsRegister()) { - __ it(cond); - __ mov(cond, RegisterFrom(out), OperandFrom(src, type)); + __ it(cond.first); + __ mov(cond.first, RegisterFrom(out), OperandFrom(src, type)); } else { DCHECK(out.IsRegisterPair()); @@ -2809,10 +2840,10 @@ void InstructionCodeGeneratorARMVIXL::VisitSelect(HSelect* select) { operand_low = LowRegisterFrom(src); } - __ it(cond); - __ mov(cond, LowRegisterFrom(out), operand_low); - __ it(cond); - __ mov(cond, HighRegisterFrom(out), operand_high); + __ it(cond.first); + __ mov(cond.first, LowRegisterFrom(out), operand_low); + __ it(cond.first); + __ mov(cond.first, HighRegisterFrom(out), operand_high); } return; @@ -2865,7 +2896,7 @@ void LocationsBuilderARMVIXL::HandleCondition(HCondition* cond) { locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1))); if (!cond->IsEmittedAtUseSite()) { - locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } break; @@ -2892,50 +2923,48 @@ void InstructionCodeGeneratorARMVIXL::HandleCondition(HCondition* cond) { return; } - Location right = cond->GetLocations()->InAt(1); - vixl32::Register out = OutputRegister(cond); - vixl32::Label true_label, false_label; + const vixl32::Register out = OutputRegister(cond); - switch (cond->InputAt(0)->GetType()) { - default: { - // Integer case. - if (right.IsRegister()) { - __ Cmp(InputRegisterAt(cond, 0), InputOperandAt(cond, 1)); - } else { - DCHECK(right.IsConstant()); - __ Cmp(InputRegisterAt(cond, 0), - CodeGenerator::GetInt32ValueOf(right.GetConstant())); - } - ExactAssemblyScope aas(GetVIXLAssembler(), - 3 * vixl32::kMaxInstructionSizeInBytes, - CodeBufferCheckScope::kMaximumSize); - __ ite(ARMCondition(cond->GetCondition())); - __ mov(ARMCondition(cond->GetCondition()), OutputRegister(cond), 1); - __ mov(ARMCondition(cond->GetOppositeCondition()), OutputRegister(cond), 0); - return; - } - case Primitive::kPrimLong: - GenerateLongComparesAndJumps(cond, &true_label, &false_label); - break; - case Primitive::kPrimFloat: - case Primitive::kPrimDouble: - GenerateVcmp(cond, codegen_); - GenerateFPJumps(cond, &true_label, &false_label); - break; + if (out.IsLow() && CanGenerateTest(cond, codegen_->GetAssembler())) { + const auto condition = GenerateTest(cond, false, codegen_); + // We use the scope because of the IT block that follows. + ExactAssemblyScope guard(GetVIXLAssembler(), + 4 * vixl32::k16BitT32InstructionSizeInBytes, + CodeBufferCheckScope::kExactSize); + + __ it(condition.first); + __ mov(condition.first, out, 1); + __ it(condition.second); + __ mov(condition.second, out, 0); + return; } // Convert the jumps into the result. vixl32::Label done_label; - vixl32::Label* final_label = codegen_->GetFinalLabel(cond, &done_label); + vixl32::Label* const final_label = codegen_->GetFinalLabel(cond, &done_label); - // False case: result = 0. - __ Bind(&false_label); - __ Mov(out, 0); - __ B(final_label); + if (cond->InputAt(0)->GetType() == Primitive::kPrimLong) { + vixl32::Label true_label, false_label; - // True case: result = 1. - __ Bind(&true_label); - __ Mov(out, 1); + GenerateLongComparesAndJumps(cond, &true_label, &false_label); + + // False case: result = 0. + __ Bind(&false_label); + __ Mov(out, 0); + __ B(final_label); + + // True case: result = 1. + __ Bind(&true_label); + __ Mov(out, 1); + } else { + DCHECK(CanGenerateTest(cond, codegen_->GetAssembler())); + + const auto condition = GenerateTest(cond, false, codegen_); + + __ Mov(LeaveFlags, out, 0); + __ B(condition.second, final_label, /* far_target */ false); + __ Mov(out, 1); + } if (done_label.IsReferenced()) { __ Bind(&done_label); @@ -7079,14 +7108,16 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value(); - vixl32::Label done, zero; - vixl32::Label* final_label = codegen_->GetFinalLabel(instruction, &done); + vixl32::Label done; + vixl32::Label* const final_label = codegen_->GetFinalLabel(instruction, &done); SlowPathCodeARMVIXL* slow_path = nullptr; // Return 0 if `obj` is null. // avoid null check if we know obj is not null. if (instruction->MustDoNullCheck()) { - __ CompareAndBranchIfZero(obj, &zero, /* far_target */ false); + DCHECK(!out.Is(obj)); + __ Mov(out, 0); + __ CompareAndBranchIfZero(obj, final_label, /* far_target */ false); } switch (type_check_kind) { @@ -7098,11 +7129,28 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) class_offset, maybe_temp_loc, kCompilerReadBarrierOption); - __ Cmp(out, cls); // Classes must be equal for the instanceof to succeed. - __ B(ne, &zero, /* far_target */ false); - __ Mov(out, 1); - __ B(final_label); + __ Cmp(out, cls); + // We speculatively set the result to false without changing the condition + // flags, which allows us to avoid some branching later. + __ Mov(LeaveFlags, out, 0); + + // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8, + // we check that the output is in a low register, so that a 16-bit MOV + // encoding can be used. + if (out.IsLow()) { + // We use the scope because of the IT block that follows. + ExactAssemblyScope guard(GetVIXLAssembler(), + 2 * vixl32::k16BitT32InstructionSizeInBytes, + CodeBufferCheckScope::kExactSize); + + __ it(eq); + __ mov(eq, out, 1); + } else { + __ B(ne, final_label, /* far_target */ false); + __ Mov(out, 1); + } + break; } @@ -7124,14 +7172,11 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) super_offset, maybe_temp_loc, kCompilerReadBarrierOption); - // If `out` is null, we use it for the result, and jump to `done`. + // If `out` is null, we use it for the result, and jump to the final label. __ CompareAndBranchIfZero(out, final_label, /* far_target */ false); __ Cmp(out, cls); __ B(ne, &loop, /* far_target */ false); __ Mov(out, 1); - if (zero.IsReferenced()) { - __ B(final_label); - } break; } @@ -7154,14 +7199,38 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) super_offset, maybe_temp_loc, kCompilerReadBarrierOption); - __ CompareAndBranchIfNonZero(out, &loop); - // If `out` is null, we use it for the result, and jump to `done`. - __ B(final_label); - __ Bind(&success); - __ Mov(out, 1); - if (zero.IsReferenced()) { + // This is essentially a null check, but it sets the condition flags to the + // proper value for the code that follows the loop, i.e. not `eq`. + __ Cmp(out, 1); + __ B(hs, &loop, /* far_target */ false); + + // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8, + // we check that the output is in a low register, so that a 16-bit MOV + // encoding can be used. + if (out.IsLow()) { + // If `out` is null, we use it for the result, and the condition flags + // have already been set to `ne`, so the IT block that comes afterwards + // (and which handles the successful case) turns into a NOP (instead of + // overwriting `out`). + __ Bind(&success); + + // We use the scope because of the IT block that follows. + ExactAssemblyScope guard(GetVIXLAssembler(), + 2 * vixl32::k16BitT32InstructionSizeInBytes, + CodeBufferCheckScope::kExactSize); + + // There is only one branch to the `success` label (which is bound to this + // IT block), and it has the same condition, `eq`, so in that case the MOV + // is executed. + __ it(eq); + __ mov(eq, out, 1); + } else { + // If `out` is null, we use it for the result, and jump to the final label. __ B(final_label); + __ Bind(&success); + __ Mov(out, 1); } + break; } @@ -7184,14 +7253,34 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) component_offset, maybe_temp_loc, kCompilerReadBarrierOption); - // If `out` is null, we use it for the result, and jump to `done`. + // If `out` is null, we use it for the result, and jump to the final label. __ CompareAndBranchIfZero(out, final_label, /* far_target */ false); GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset); static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot"); - __ CompareAndBranchIfNonZero(out, &zero, /* far_target */ false); - __ Bind(&exact_check); - __ Mov(out, 1); - __ B(final_label); + __ Cmp(out, 0); + // We speculatively set the result to false without changing the condition + // flags, which allows us to avoid some branching later. + __ Mov(LeaveFlags, out, 0); + + // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8, + // we check that the output is in a low register, so that a 16-bit MOV + // encoding can be used. + if (out.IsLow()) { + __ Bind(&exact_check); + + // We use the scope because of the IT block that follows. + ExactAssemblyScope guard(GetVIXLAssembler(), + 2 * vixl32::k16BitT32InstructionSizeInBytes, + CodeBufferCheckScope::kExactSize); + + __ it(eq); + __ mov(eq, out, 1); + } else { + __ B(ne, final_label, /* far_target */ false); + __ Bind(&exact_check); + __ Mov(out, 1); + } + break; } @@ -7211,9 +7300,6 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) codegen_->AddSlowPath(slow_path); __ B(ne, slow_path->GetEntryLabel()); __ Mov(out, 1); - if (zero.IsReferenced()) { - __ B(final_label); - } break; } @@ -7242,18 +7328,10 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) /* is_fatal */ false); codegen_->AddSlowPath(slow_path); __ B(slow_path->GetEntryLabel()); - if (zero.IsReferenced()) { - __ B(final_label); - } break; } } - if (zero.IsReferenced()) { - __ Bind(&zero); - __ Mov(out, 0); - } - if (done.IsReferenced()) { __ Bind(&done); } diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h index 781027ab30..1e9669dc38 100644 --- a/compiler/optimizing/code_generator_arm_vixl.h +++ b/compiler/optimizing/code_generator_arm_vixl.h @@ -401,9 +401,6 @@ class InstructionCodeGeneratorARMVIXL : public InstructionCodeGenerator { void GenerateCompareTestAndBranch(HCondition* condition, vixl::aarch32::Label* true_target, vixl::aarch32::Label* false_target); - void GenerateFPJumps(HCondition* cond, - vixl::aarch32::Label* true_label, - vixl::aarch32::Label* false_label); void GenerateLongComparesAndJumps(HCondition* cond, vixl::aarch32::Label* true_label, vixl::aarch32::Label* false_label); diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 9f04e598eb..b421810113 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -618,7 +618,7 @@ struct ClassExtOffsets : public CheckOffsets<mirror::ClassExt> { ClassExtOffsets() : CheckOffsets<mirror::ClassExt>(false, "Ldalvik/system/ClassExt;") { addOffset(OFFSETOF_MEMBER(mirror::ClassExt, obsolete_dex_caches_), "obsoleteDexCaches"); addOffset(OFFSETOF_MEMBER(mirror::ClassExt, obsolete_methods_), "obsoleteMethods"); - addOffset(OFFSETOF_MEMBER(mirror::ClassExt, original_dex_file_bytes_), "originalDexFile"); + addOffset(OFFSETOF_MEMBER(mirror::ClassExt, original_dex_file_), "originalDexFile"); addOffset(OFFSETOF_MEMBER(mirror::ClassExt, verify_error_), "verifyError"); } }; diff --git a/runtime/mirror/class_ext.cc b/runtime/mirror/class_ext.cc index 5dc3aca094..94e4b88f6c 100644 --- a/runtime/mirror/class_ext.cc +++ b/runtime/mirror/class_ext.cc @@ -117,9 +117,9 @@ void ClassExt::SetVerifyError(ObjPtr<Object> err) { } } -void ClassExt::SetOriginalDexFileBytes(ObjPtr<ByteArray> bytes) { +void ClassExt::SetOriginalDexFile(ObjPtr<Object> bytes) { DCHECK(!Runtime::Current()->IsActiveTransaction()); - SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(ClassExt, original_dex_file_bytes_), bytes); + SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(ClassExt, original_dex_file_), bytes); } void ClassExt::SetClass(ObjPtr<Class> dalvik_system_ClassExt) { diff --git a/runtime/mirror/class_ext.h b/runtime/mirror/class_ext.h index fac955a45e..708665d46b 100644 --- a/runtime/mirror/class_ext.h +++ b/runtime/mirror/class_ext.h @@ -60,11 +60,11 @@ class MANAGED ClassExt : public Object { OFFSET_OF_OBJECT_MEMBER(ClassExt, obsolete_methods_)); } - ByteArray* GetOriginalDexFileBytes() REQUIRES_SHARED(Locks::mutator_lock_) { - return GetFieldObject<ByteArray>(OFFSET_OF_OBJECT_MEMBER(ClassExt, original_dex_file_bytes_)); + Object* GetOriginalDexFile() REQUIRES_SHARED(Locks::mutator_lock_) { + return GetFieldObject<Object>(OFFSET_OF_OBJECT_MEMBER(ClassExt, original_dex_file_)); } - void SetOriginalDexFileBytes(ObjPtr<ByteArray> bytes) REQUIRES_SHARED(Locks::mutator_lock_); + void SetOriginalDexFile(ObjPtr<Object> bytes) REQUIRES_SHARED(Locks::mutator_lock_); void SetObsoleteArrays(ObjPtr<PointerArray> methods, ObjPtr<ObjectArray<DexCache>> dex_caches) REQUIRES_SHARED(Locks::mutator_lock_); @@ -89,7 +89,7 @@ class MANAGED ClassExt : public Object { HeapReference<PointerArray> obsolete_methods_; - HeapReference<ByteArray> original_dex_file_bytes_; + HeapReference<Object> original_dex_file_; // The saved verification error of this class. HeapReference<Object> verify_error_; diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc index 2d1b25ed26..38fd1d4af3 100644 --- a/runtime/openjdkjvmti/ti_class.cc +++ b/runtime/openjdkjvmti/ti_class.cc @@ -259,7 +259,7 @@ struct ClassCallback : public art::ClassLoadCallback { } // Actually set the ClassExt's original bytes once we have actually succeeded. - ext->SetOriginalDexFileBytes(arr.Get()); + ext->SetOriginalDexFile(arr.Get()); // Set the return values *final_class_def = &dex_file->GetClassDef(0); *final_dex_file = dex_file.release(); diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc index 9c1d6ef0a5..7faddfb0f9 100644 --- a/runtime/openjdkjvmti/ti_redefine.cc +++ b/runtime/openjdkjvmti/ti_redefine.cc @@ -469,7 +469,7 @@ void Redefiner::RecordFailure(jvmtiError result, result_ = result; } -art::mirror::ByteArray* Redefiner::ClassRedefinition::AllocateOrGetOriginalDexFileBytes() { +art::mirror::Object* Redefiner::ClassRedefinition::AllocateOrGetOriginalDexFile() { // If we have been specifically given a new set of bytes use that if (original_dex_file_.size() != 0) { return art::mirror::ByteArray::AllocateAndFill( @@ -481,24 +481,21 @@ art::mirror::ByteArray* Redefiner::ClassRedefinition::AllocateOrGetOriginalDexFi // See if we already have one set. art::ObjPtr<art::mirror::ClassExt> ext(GetMirrorClass()->GetExtData()); if (!ext.IsNull()) { - art::ObjPtr<art::mirror::ByteArray> old_original_bytes(ext->GetOriginalDexFileBytes()); - if (!old_original_bytes.IsNull()) { + art::ObjPtr<art::mirror::Object> old_original_dex_file(ext->GetOriginalDexFile()); + if (!old_original_dex_file.IsNull()) { // We do. Use it. - return old_original_bytes.Ptr(); + return old_original_dex_file.Ptr(); } } - // Copy the current dex_file - const art::DexFile& current_dex_file = GetMirrorClass()->GetDexFile(); + // return the current dex_cache which has the dex file in it. + art::ObjPtr<art::mirror::DexCache> current_dex_cache(GetMirrorClass()->GetDexCache()); // TODO Handle this or make it so it cannot happen. - if (current_dex_file.NumClassDefs() != 1) { + if (current_dex_cache->GetDexFile()->NumClassDefs() != 1) { LOG(WARNING) << "Current dex file has more than one class in it. Calling RetransformClasses " << "on this class might fail if no transformations are applied to it!"; } - return art::mirror::ByteArray::AllocateAndFill( - driver_->self_, - reinterpret_cast<const signed char*>(current_dex_file.Begin()), - current_dex_file.Size()); + return current_dex_cache.Ptr(); } struct CallbackCtx { @@ -847,9 +844,9 @@ class RedefinitionDataHolder { return art::down_cast<art::mirror::Class*>(GetSlot(klass_index, kSlotMirrorClass)); } - art::mirror::ByteArray* GetOriginalDexFileBytes(jint klass_index) const + art::mirror::Object* GetOriginalDexFile(jint klass_index) const REQUIRES_SHARED(art::Locks::mutator_lock_) { - return art::down_cast<art::mirror::ByteArray*>(GetSlot(klass_index, kSlotOrigDexFile)); + return art::down_cast<art::mirror::Object*>(GetSlot(klass_index, kSlotOrigDexFile)); } void SetSourceClassLoader(jint klass_index, art::mirror::ClassLoader* loader) @@ -872,7 +869,7 @@ class RedefinitionDataHolder { REQUIRES_SHARED(art::Locks::mutator_lock_) { SetSlot(klass_index, kSlotMirrorClass, klass); } - void SetOriginalDexFileBytes(jint klass_index, art::mirror::ByteArray* bytes) + void SetOriginalDexFile(jint klass_index, art::mirror::Object* bytes) REQUIRES_SHARED(art::Locks::mutator_lock_) { SetSlot(klass_index, kSlotOrigDexFile, bytes); } @@ -985,9 +982,9 @@ class RedefinitionDataIter { art::mirror::Class* GetMirrorClass() const REQUIRES_SHARED(art::Locks::mutator_lock_) { return holder_.GetMirrorClass(idx_); } - art::mirror::ByteArray* GetOriginalDexFileBytes() const + art::mirror::Object* GetOriginalDexFile() const REQUIRES_SHARED(art::Locks::mutator_lock_) { - return holder_.GetOriginalDexFileBytes(idx_); + return holder_.GetOriginalDexFile(idx_); } int32_t GetIndex() const { return idx_; @@ -1010,9 +1007,9 @@ class RedefinitionDataIter { void SetMirrorClass(art::mirror::Class* klass) REQUIRES_SHARED(art::Locks::mutator_lock_) { holder_.SetMirrorClass(idx_, klass); } - void SetOriginalDexFileBytes(art::mirror::ByteArray* bytes) + void SetOriginalDexFile(art::mirror::Object* bytes) REQUIRES_SHARED(art::Locks::mutator_lock_) { - holder_.SetOriginalDexFileBytes(idx_, bytes); + holder_.SetOriginalDexFile(idx_, bytes); } private: @@ -1138,8 +1135,8 @@ bool Redefiner::ClassRedefinition::FinishRemainingAllocations( } // We won't always need to set this field. - cur_data->SetOriginalDexFileBytes(AllocateOrGetOriginalDexFileBytes()); - if (cur_data->GetOriginalDexFileBytes() == nullptr) { + cur_data->SetOriginalDexFile(AllocateOrGetOriginalDexFile()); + if (cur_data->GetOriginalDexFile() == nullptr) { driver_->self_->AssertPendingOOMException(); driver_->self_->ClearException(); RecordFailure(ERR(OUT_OF_MEMORY), "Unable to allocate array for original dex file"); @@ -1285,7 +1282,7 @@ jvmtiError Redefiner::Run() { art::mirror::Class* klass = data.GetMirrorClass(); // TODO Rewrite so we don't do a stack walk for each and every class. redef.FindAndAllocateObsoleteMethods(klass); - redef.UpdateClass(klass, data.GetNewDexCache(), data.GetOriginalDexFileBytes()); + redef.UpdateClass(klass, data.GetNewDexCache(), data.GetOriginalDexFile()); } // TODO We should check for if any of the redefined methods are intrinsic methods here and, if any // are, force a full-world deoptimization before finishing redefinition. If we don't do this then @@ -1365,7 +1362,7 @@ void Redefiner::ClassRedefinition::UpdateFields(art::ObjPtr<art::mirror::Class> void Redefiner::ClassRedefinition::UpdateClass( art::ObjPtr<art::mirror::Class> mclass, art::ObjPtr<art::mirror::DexCache> new_dex_cache, - art::ObjPtr<art::mirror::ByteArray> original_dex_file) { + art::ObjPtr<art::mirror::Object> original_dex_file) { DCHECK_EQ(dex_file_->NumClassDefs(), 1u); const art::DexFile::ClassDef& class_def = dex_file_->GetClassDef(0); UpdateMethods(mclass, new_dex_cache, class_def); @@ -1379,7 +1376,7 @@ void Redefiner::ClassRedefinition::UpdateClass( mclass->SetDexTypeIndex(dex_file_->GetIndexForTypeId(*dex_file_->FindTypeId(class_sig_.c_str()))); art::ObjPtr<art::mirror::ClassExt> ext(mclass->GetExtData()); CHECK(!ext.IsNull()); - ext->SetOriginalDexFileBytes(original_dex_file); + ext->SetOriginalDexFile(original_dex_file); } // This function does all (java) allocations we need to do for the Class being redefined. diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h index 4313a9476e..6c09d46e89 100644 --- a/runtime/openjdkjvmti/ti_redefine.h +++ b/runtime/openjdkjvmti/ti_redefine.h @@ -137,7 +137,7 @@ class Redefiner { REQUIRES_SHARED(art::Locks::mutator_lock_); // This may return nullptr with a OOME pending if allocation fails. - art::mirror::ByteArray* AllocateOrGetOriginalDexFileBytes() + art::mirror::Object* AllocateOrGetOriginalDexFile() REQUIRES_SHARED(art::Locks::mutator_lock_); void RecordFailure(jvmtiError e, const std::string& err) { @@ -196,7 +196,7 @@ class Redefiner { void UpdateClass(art::ObjPtr<art::mirror::Class> mclass, art::ObjPtr<art::mirror::DexCache> new_dex_cache, - art::ObjPtr<art::mirror::ByteArray> original_dex_file) + art::ObjPtr<art::mirror::Object> original_dex_file) REQUIRES(art::Locks::mutator_lock_); void ReleaseDexFile() REQUIRES_SHARED(art::Locks::mutator_lock_); diff --git a/runtime/openjdkjvmti/transform.cc b/runtime/openjdkjvmti/transform.cc index bd52cbb7f9..06aecbaee3 100644 --- a/runtime/openjdkjvmti/transform.cc +++ b/runtime/openjdkjvmti/transform.cc @@ -150,16 +150,27 @@ jvmtiError Transformer::GetDexDataForRetransformation(ArtJvmTiEnv* env, art::Handle<art::mirror::Class> klass, /*out*/jint* dex_data_len, /*out*/unsigned char** dex_data) { - art::StackHandleScope<2> hs(art::Thread::Current()); + art::StackHandleScope<3> hs(art::Thread::Current()); art::Handle<art::mirror::ClassExt> ext(hs.NewHandle(klass->GetExtData())); if (!ext.IsNull()) { - art::Handle<art::mirror::ByteArray> orig_dex(hs.NewHandle(ext->GetOriginalDexFileBytes())); + art::Handle<art::mirror::Object> orig_dex(hs.NewHandle(ext->GetOriginalDexFile())); if (!orig_dex.IsNull()) { - *dex_data_len = static_cast<jint>(orig_dex->GetLength()); - return CopyDataIntoJvmtiBuffer(env, - reinterpret_cast<const unsigned char*>(orig_dex->GetData()), - *dex_data_len, - /*out*/dex_data); + if (orig_dex->IsArrayInstance()) { + DCHECK(orig_dex->GetClass()->GetComponentType()->IsPrimitiveByte()); + art::Handle<art::mirror::ByteArray> orig_dex_bytes( + hs.NewHandle(art::down_cast<art::mirror::ByteArray*>(orig_dex->AsArray()))); + *dex_data_len = static_cast<jint>(orig_dex_bytes->GetLength()); + return CopyDataIntoJvmtiBuffer( + env, + reinterpret_cast<const unsigned char*>(orig_dex_bytes->GetData()), + *dex_data_len, + /*out*/dex_data); + } else { + DCHECK(orig_dex->IsDexCache()); + const art::DexFile* dex_file = orig_dex->AsDexCache()->GetDexFile(); + *dex_data_len = static_cast<jint>(dex_file->Size()); + return CopyDataIntoJvmtiBuffer(env, dex_file->Begin(), dex_file->Size(), /*out*/dex_data); + } } } // TODO De-quicken the dex file before passing it to the agents. diff --git a/test/981-dedup-original-dex/expected.txt b/test/981-dedup-original-dex/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/981-dedup-original-dex/expected.txt diff --git a/test/981-dedup-original-dex/info.txt b/test/981-dedup-original-dex/info.txt new file mode 100644 index 0000000000..62696e00d7 --- /dev/null +++ b/test/981-dedup-original-dex/info.txt @@ -0,0 +1,4 @@ +Tests basic functions in the jvmti plugin. + +This checks that we do not needlessly duplicate the contents of retransformed +classes original dex files. diff --git a/test/981-dedup-original-dex/run b/test/981-dedup-original-dex/run new file mode 100755 index 0000000000..e92b873956 --- /dev/null +++ b/test/981-dedup-original-dex/run @@ -0,0 +1,17 @@ +#!/bin/bash +# +# Copyright 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +./default-run "$@" --jvmti diff --git a/test/981-dedup-original-dex/src/Main.java b/test/981-dedup-original-dex/src/Main.java new file mode 100644 index 0000000000..cd3f007532 --- /dev/null +++ b/test/981-dedup-original-dex/src/Main.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.Field; +import java.util.Base64; + +import dalvik.system.ClassExt; + +public class Main { + + /** + * base64 encoded class/dex file for + * class Transform { + * public void sayHi() { + * System.out.println("Goodbye"); + * } + * } + */ + private static final byte[] DEX_BYTES_1 = Base64.getDecoder().decode( + "ZGV4CjAzNQCLXSBQ5FiS3f16krSYZFF8xYZtFVp0GRXMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO" + + "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB" + + "AABqAQAAcwEAAIABAACXAQAAqwEAAL8BAADTAQAA4wEAAOYBAADqAQAA/gEAAAMCAAAMAgAAAgAA" + + "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" + + "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAHAAAAAAAAAB4CAAAA" + + "AAAAAQABAAEAAAATAgAABAAAAHAQAwAAAA4AAwABAAIAAAAYAgAACQAAAGIAAAAbAQEAAABuIAIA" + + "EAAOAAAAAQAAAAMABjxpbml0PgAHR29vZGJ5ZQALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50" + + "U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xh" + + "bmcvU3lzdGVtOwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTMuMzYAA291" + + "dAAHcHJpbnRsbgAFc2F5SGkAEQAHDgATAAcOhQAAAAEBAICABKACAQG4Ag0AAAAAAAAAAQAAAAAA" + + "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA" + + "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA" + + "AgAAABMCAAAAIAAAAQAAAB4CAAAAEAAAAQAAACwCAAA="); + + /** + * base64 encoded class/dex file for + * class Transform2 { + * public void sayHi() { + * System.out.println("Goodbye2"); + * } + * } + */ + private static final byte[] DEX_BYTES_2 = Base64.getDecoder().decode( + "ZGV4CjAzNQAjXDED2iflQ3NXbPtBRVjQVMqoDU9nDz/QAgAAcAAAAHhWNBIAAAAAAAAAADACAAAO" + + "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACwAQAAIAEAAGIB" + + "AABqAQAAdAEAAIIBAACZAQAArQEAAMEBAADVAQAA5gEAAOkBAADtAQAAAQIAAAYCAAAPAgAAAgAA" + + "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" + + "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAHAAAAAAAAACECAAAA" + + "AAAAAQABAAEAAAAWAgAABAAAAHAQAwAAAA4AAwABAAIAAAAbAgAACQAAAGIAAAAbAQEAAABuIAIA" + + "EAAOAAAAAQAAAAMABjxpbml0PgAIR29vZGJ5ZTIADExUcmFuc2Zvcm0yOwAVTGphdmEvaW8vUHJp" + + "bnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEv" + + "bGFuZy9TeXN0ZW07AA9UcmFuc2Zvcm0yLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTQuMzAA" + + "A291dAAHcHJpbnRsbgAFc2F5SGkAAQAHDgADAAcOhwAAAAEBAICABKACAQG4AgANAAAAAAAAAAEA" + + "AAAAAAAAAQAAAA4AAABwAAAAAgAAAAYAAACoAAAAAwAAAAIAAADAAAAABAAAAAEAAADYAAAABQAA" + + "AAQAAADgAAAABgAAAAEAAAAAAQAAASAAAAIAAAAgAQAAARAAAAEAAABcAQAAAiAAAA4AAABiAQAA" + + "AyAAAAIAAAAWAgAAACAAAAEAAAAhAgAAABAAAAEAAAAwAgAA"); + + public static void main(String[] args) { + try { + doTest(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private static void assertSame(Object a, Object b) throws Exception { + if (a != b) { + throw new AssertionError("'" + (a != null ? a.toString() : "null") + "' is not the same as " + + "'" + (b != null ? b.toString() : "null") + "'"); + } + } + + private static Object getObjectField(Object o, String name) throws Exception { + return getObjectField(o, o.getClass(), name); + } + + private static Object getObjectField(Object o, Class<?> type, String name) throws Exception { + Field f = type.getDeclaredField(name); + f.setAccessible(true); + return f.get(o); + } + + private static Object getOriginalDexFile(Class<?> k) throws Exception { + ClassExt ext_data_object = (ClassExt) getObjectField(k, "extData"); + if (ext_data_object == null) { + return null; + } + + return getObjectField(ext_data_object, "originalDexFile"); + } + + public static void doTest() throws Exception { + // Make sure both of these are loaded prior to transformations being added so they have the same + // original dex files. + Transform t1 = new Transform(); + Transform2 t2 = new Transform2(); + + assertSame(null, getOriginalDexFile(t1.getClass())); + assertSame(null, getOriginalDexFile(t2.getClass())); + assertSame(null, getOriginalDexFile(Main.class)); + + addCommonTransformationResult("Transform", new byte[0], DEX_BYTES_1); + addCommonTransformationResult("Transform2", new byte[0], DEX_BYTES_2); + enableCommonRetransformation(true); + doCommonClassRetransformation(Transform.class, Transform2.class); + + assertSame(getOriginalDexFile(t1.getClass()), getOriginalDexFile(t2.getClass())); + assertSame(null, getOriginalDexFile(Main.class)); + // Make sure that the original dex file is a DexCache object. + assertSame(getOriginalDexFile(t1.getClass()).getClass(), Class.forName("java.lang.DexCache")); + + // Check that we end up with a byte[] if we do a direct RedefineClasses + enableCommonRetransformation(false); + doCommonClassRedefinition(Transform.class, new byte[0], DEX_BYTES_1); + assertSame((new byte[0]).getClass(), getOriginalDexFile(t1.getClass()).getClass()); + } + + // Transforms the class + private static native void doCommonClassRetransformation(Class<?>... target); + private static native void doCommonClassRedefinition(Class<?> target, + byte[] class_file, + byte[] dex_file); + private static native void enableCommonRetransformation(boolean enable); + private static native void addCommonTransformationResult(String target_name, + byte[] class_bytes, + byte[] dex_bytes); +} diff --git a/test/981-dedup-original-dex/src/Transform.java b/test/981-dedup-original-dex/src/Transform.java new file mode 100644 index 0000000000..3c97907ddc --- /dev/null +++ b/test/981-dedup-original-dex/src/Transform.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class Transform { + public void sayHi() { + System.out.println("hello"); + } +} diff --git a/test/981-dedup-original-dex/src/Transform2.java b/test/981-dedup-original-dex/src/Transform2.java new file mode 100644 index 0000000000..eb22842184 --- /dev/null +++ b/test/981-dedup-original-dex/src/Transform2.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class Transform2 { + public void sayHi() { + System.out.println("hello2"); + } +} diff --git a/test/testrunner/run_build_test_target.py b/test/testrunner/run_build_test_target.py index 835b678cd6..e105da33d4 100755 --- a/test/testrunner/run_build_test_target.py +++ b/test/testrunner/run_build_test_target.py @@ -51,6 +51,8 @@ if target.get('target'): build_command += ' -j' + str(n_threads) build_command += ' -C ' + env.ANDROID_BUILD_TOP build_command += ' ' + target.get('target') + # Add 'dist' to avoid Jack issues b/36169180. + build_command += ' dist' print build_command.split() if subprocess.call(build_command.split()): sys.exit(1) diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc index fddae3af02..8cb14bdfc5 100644 --- a/test/ti-agent/common_load.cc +++ b/test/ti-agent/common_load.cc @@ -121,6 +121,7 @@ static AgentLib agents[] = { { "943-private-recursive-jit", common_redefine::OnLoad, nullptr }, { "944-transform-classloaders", common_redefine::OnLoad, nullptr }, { "945-obsolete-native", common_redefine::OnLoad, nullptr }, + { "981-dedup-original-dex", common_retransform::OnLoad, nullptr }, }; static AgentLib* FindAgent(char* name) { @@ -30,53 +30,121 @@ function follow_links() { } function find_libdir() { - # Get the actual file, $DALVIKVM may be a symbolic link. + # Get the actual file, $1 is the ART_BINARY_PATH and may be a symbolic link. # Use realpath instead of readlink because Android does not have a readlink. - if [[ "$(realpath "$ANDROID_ROOT/bin/$DALVIKVM")" == *dalvikvm64 ]]; then + if [[ "$(realpath "$1")" == *dalvikvm64 ]]; then echo "lib64" else echo "lib" fi } -invoke_with= -DALVIKVM=dalvikvm +function usage() { + cat 1>&2 <<EOF +Usage: art [OPTIONS] [ART_OPTIONS] CLASS + +Supported OPTIONS include: + --32 Use the 32-bit Android Runtime. + --64 Use the 64-bit Android Runtime. + --callgrind Launch the Android Runtime in callgrind. + -d Use the debug ART library (libartd.so). + --debug Equivalent to -d. + --gdb Launch the Android Runtime in gdb. + --help Display usage message. + --invoke-with <program> Launch the Android Runtime in <program>. + --perf Launch the Android Runtime with perf recording. + --perf-report Launch the Android Runtime with perf recording with + report upon completion. + --verbose Run script verbosely. + +The ART_OPTIONS are passed directly to the Android Runtime. + +Example: + art --32 -cp my_classes.dex MainClass + +Common errors: + 1) Not having core.art available (see $ANDROID_BUILD_TOP/art/Android.mk). + eg m -j32 build-art-host + 2) Not having boot.art available (see $ANDROID_BUILD_TOP/build/make/core/dex_preopt_libart_boot.mk) + eg m -j32 out/target/product/generic_x86_64/dex_bootjars/system/framework/x86_64/boot.art +EOF +} + +LAUNCH_WRAPPER= +ART_BINARY=dalvikvm LIBART=libart.so +VERBOSE="no" -while true; do - if [ "$1" = "--invoke-with" ]; then - shift - invoke_with="$invoke_with $1" - shift - elif [ "$1" = "-d" ]; then +while [[ "$1" = "-"* ]]; do + case $1 in + --32) + ART_BINARY=dalvikvm32 + ;; + --64) + ART_BINARY=dalvikvm64 + ;; + --callgrind) + LAUNCH_WRAPPER="valgrind --tool=callgrind" + ;; + -d) + ;& # Fallthrough + --debug) LIBART="libartd.so" + ;; + --gdb) + LIBART="libartd.so" + LAUNCH_WRAPPER="gdb --args" + ;; + --help) + usage + exit 0 + ;; + --invoke-with) + LAUNCH_WRAPPER=$2 shift - elif [ "$1" = "--32" ]; then - DALVIKVM=dalvikvm32 - shift - elif [ "$1" = "--64" ]; then - DALVIKVM=dalvikvm64 - shift - elif [ "$1" = "--perf" ]; then + ;; + --perf) PERF="record" - shift - elif [ "$1" = "--perf-report" ]; then + ;; + --perf-report) PERF="report" - shift - elif expr "$1" : "--" >/dev/null 2>&1; then + ;; + --verbose) + VERBOSE="yes" + ;; + --*) echo "unknown option: $1" 1>&2 + usage exit 1 - else + ;; + *) break - fi + ;; + esac + shift done +if [ $# -eq 0 ]; then + usage + exit 1 +fi + PROG_NAME="$(follow_links)" PROG_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)" ANDROID_ROOT=$PROG_DIR/.. -LIBDIR=$(find_libdir) +ART_BINARY_PATH=$ANDROID_ROOT/bin/$ART_BINARY + +if [ ! -x "$ART_BINARY_PATH" ]; then + cat 1>&2 <<EOF +Android Runtime not found: $ART_BINARY_PATH +This script should be in the same directory as the Android Runtime ($ART_BINARY). +EOF + exit 1 +fi + +LIBDIR="$(find_libdir $ART_BINARY_PATH)" LD_LIBRARY_PATH=$ANDROID_ROOT/$LIBDIR -DEBUG_OPTION="" +EXTRA_OPTIONS="" DELETE_ANDROID_DATA=false # If ANDROID_DATA is the system ANDROID_DATA or is not set, use our own, @@ -88,23 +156,24 @@ if [ "$ANDROID_DATA" = "/data" ] || [ "$ANDROID_DATA" = "" ]; then fi if [ z"$PERF" != z ]; then - invoke_with="perf record -g -o $ANDROID_DATA/perf.data -e cycles:u $invoke_with" - DEBUG_OPTION="-Xcompiler-option --generate-debug-info" + LAUNCH_WRAPPER="perf record -g -o $ANDROID_DATA/perf.data -e cycles:u $LAUNCH_WRAPPER" + EXTRA_OPTIONS="-Xcompiler-option --generate-debug-info" fi # We use the PIC core image to work with perf. -ANDROID_DATA=$ANDROID_DATA \ - ANDROID_ROOT=$ANDROID_ROOT \ - LD_LIBRARY_PATH=$LD_LIBRARY_PATH \ - PATH=$ANDROID_ROOT/bin:$PATH \ - LD_USE_LOAD_BIAS=1 \ - $invoke_with $ANDROID_ROOT/bin/$DALVIKVM $lib \ - -XXlib:$LIBART \ - -Xnorelocate \ - -Ximage:$ANDROID_ROOT/framework/core.art \ - $DEBUG_OPTION \ - "$@" - +CMD="ANDROID_DATA=$ANDROID_DATA \ + ANDROID_ROOT=$ANDROID_ROOT \ + LD_LIBRARY_PATH=$LD_LIBRARY_PATH \ + PATH=$ANDROID_ROOT/bin:$PATH \ + LD_USE_LOAD_BIAS=1 \ + $LAUNCH_WRAPPER $ART_BINARY_PATH $lib \ + -XXlib:$LIBART \ + -Xnorelocate \ + -Ximage:$ANDROID_ROOT/framework/core.art \ + $EXTRA_OPTIONS \ + $@" +[ "$VERBOSE" = yes ] && echo $CMD +eval $CMD EXIT_STATUS=$? if [ z"$PERF" != z ]; then |