diff options
97 files changed, 3564 insertions, 2110 deletions
diff --git a/compiler/Android.mk b/compiler/Android.mk index 2cbafea573..7a257b649f 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -142,7 +142,9 @@ LIBART_COMPILER_SRC_FILES_arm64 := \ jni/quick/arm64/calling_convention_arm64.cc \ linker/arm64/relative_patcher_arm64.cc \ optimizing/code_generator_arm64.cc \ + optimizing/instruction_simplifier_arm.cc \ optimizing/instruction_simplifier_arm64.cc \ + optimizing/instruction_simplifier_shared.cc \ optimizing/intrinsics_arm64.cc \ utils/arm64/assembler_arm64.cc \ utils/arm64/managed_register_arm64.cc \ diff --git a/compiler/debug/elf_debug_loc_writer.h b/compiler/debug/elf_debug_loc_writer.h index 8fd20aa428..32f624acd3 100644 --- a/compiler/debug/elf_debug_loc_writer.h +++ b/compiler/debug/elf_debug_loc_writer.h @@ -232,8 +232,7 @@ static void WriteDebugLocEntry(const MethodDebugInfo* method_info, // kInStackLargeOffset and kConstantLargeValue are hidden by GetKind(). // kInRegisterHigh and kInFpuRegisterHigh should be handled by // the special cases above and they should not occur alone. - LOG(ERROR) << "Unexpected register location kind: " - << DexRegisterLocation::PrettyDescriptor(kind); + LOG(ERROR) << "Unexpected register location kind: " << kind; break; } if (is64bitValue) { diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h index b78b3d7d75..8800e4b08f 100644 --- a/compiler/dex/compiler_enums.h +++ b/compiler/dex/compiler_enums.h @@ -569,6 +569,7 @@ enum MemBarrierKind { kStoreStore, kAnyAny, kNTStoreStore, + kLastBarrierKind = kNTStoreStore }; std::ostream& operator<<(std::ostream& os, const MemBarrierKind& kind); diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc index 209f101199..ad4ddadd2f 100644 --- a/compiler/dex/quick/dex_file_method_inliner.cc +++ b/compiler/dex/quick/dex_file_method_inliner.cc @@ -37,6 +37,8 @@ namespace { // anonymous namespace static constexpr bool kIntrinsicIsStatic[] = { true, // kIntrinsicDoubleCvt true, // kIntrinsicFloatCvt + true, // kIntrinsicFloat2Int + true, // kIntrinsicDouble2Long true, // kIntrinsicFloatIsInfinite true, // kIntrinsicDoubleIsInfinite true, // kIntrinsicFloatIsNaN @@ -106,6 +108,8 @@ static_assert(arraysize(kIntrinsicIsStatic) == kInlineOpNop, "arraysize of kIntrinsicIsStatic unexpected"); static_assert(kIntrinsicIsStatic[kIntrinsicDoubleCvt], "DoubleCvt must be static"); static_assert(kIntrinsicIsStatic[kIntrinsicFloatCvt], "FloatCvt must be static"); +static_assert(kIntrinsicIsStatic[kIntrinsicFloat2Int], "Float2Int must be static"); +static_assert(kIntrinsicIsStatic[kIntrinsicDouble2Long], "Double2Long must be static"); static_assert(kIntrinsicIsStatic[kIntrinsicFloatIsInfinite], "FloatIsInfinite must be static"); static_assert(kIntrinsicIsStatic[kIntrinsicDoubleIsInfinite], "DoubleIsInfinite must be static"); static_assert(kIntrinsicIsStatic[kIntrinsicFloatIsNaN], "FloatIsNaN must be static"); @@ -277,6 +281,8 @@ const char* const DexFileMethodInliner::kNameCacheNames[] = { "equals", // kNameCacheEquals "getCharsNoCheck", // kNameCacheGetCharsNoCheck "isEmpty", // kNameCacheIsEmpty + "floatToIntBits", // kNameCacheFloatToIntBits + "doubleToLongBits", // kNameCacheDoubleToLongBits "isInfinite", // kNameCacheIsInfinite "isNaN", // kNameCacheIsNaN "indexOf", // kNameCacheIndexOf @@ -472,6 +478,9 @@ const DexFileMethodInliner::IntrinsicDef DexFileMethodInliner::kIntrinsicMethods INTRINSIC(JavaLangFloat, FloatToRawIntBits, F_I, kIntrinsicFloatCvt, 0), INTRINSIC(JavaLangFloat, IntBitsToFloat, I_F, kIntrinsicFloatCvt, kIntrinsicFlagToFloatingPoint), + INTRINSIC(JavaLangFloat, FloatToIntBits, F_I, kIntrinsicFloat2Int, 0), + INTRINSIC(JavaLangDouble, DoubleToLongBits, D_J, kIntrinsicDouble2Long, 0), + INTRINSIC(JavaLangFloat, IsInfinite, F_Z, kIntrinsicFloatIsInfinite, 0), INTRINSIC(JavaLangDouble, IsInfinite, D_Z, kIntrinsicDoubleIsInfinite, 0), INTRINSIC(JavaLangFloat, IsNaN, F_Z, kIntrinsicFloatIsNaN, 0), @@ -791,6 +800,8 @@ bool DexFileMethodInliner::GenIntrinsic(Mir2Lir* backend, CallInfo* info) { intrinsic.d.data & kIntrinsicFlagIsOrdered); case kIntrinsicSystemArrayCopyCharArray: return backend->GenInlinedArrayCopyCharArray(info); + case kIntrinsicFloat2Int: + case kIntrinsicDouble2Long: case kIntrinsicFloatIsInfinite: case kIntrinsicDoubleIsInfinite: case kIntrinsicFloatIsNaN: diff --git a/compiler/dex/quick/dex_file_method_inliner.h b/compiler/dex/quick/dex_file_method_inliner.h index 59b8a533ae..b465db2c54 100644 --- a/compiler/dex/quick/dex_file_method_inliner.h +++ b/compiler/dex/quick/dex_file_method_inliner.h @@ -190,6 +190,8 @@ class DexFileMethodInliner { kNameCacheEquals, kNameCacheGetCharsNoCheck, kNameCacheIsEmpty, + kNameCacheFloatToIntBits, + kNameCacheDoubleToLongBits, kNameCacheIsInfinite, kNameCacheIsNaN, kNameCacheIndexOf, diff --git a/compiler/image_test.cc b/compiler/image_test.cc index 992af29545..5763cec43f 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -289,6 +289,11 @@ TEST_F(ImageTest, WriteReadLZ4) { TestWriteRead(ImageHeader::kStorageModeLZ4); } +TEST_F(ImageTest, WriteReadLZ4HC) { + TestWriteRead(ImageHeader::kStorageModeLZ4HC); +} + + TEST_F(ImageTest, ImageHeaderIsValid) { uint32_t image_begin = ART_BASE_ADDRESS; uint32_t image_size_ = 16 * KB; diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 5eff8f37ec..871435b85f 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -18,6 +18,7 @@ #include <sys/stat.h> #include <lz4.h> +#include <lz4hc.h> #include <memory> #include <numeric> @@ -224,18 +225,28 @@ bool ImageWriter::Write(int image_fd, char* image_data = reinterpret_cast<char*>(image_info.image_->Begin()) + sizeof(ImageHeader); size_t data_size; const char* image_data_to_write; + const uint64_t compress_start_time = NanoTime(); CHECK_EQ(image_header->storage_mode_, image_storage_mode_); switch (image_storage_mode_) { case ImageHeader::kStorageModeLZ4: { - size_t compressed_max_size = LZ4_compressBound(image_data_size); + const size_t compressed_max_size = LZ4_compressBound(image_data_size); compressed_data.reset(new char[compressed_max_size]); data_size = LZ4_compress( reinterpret_cast<char*>(image_info.image_->Begin()) + sizeof(ImageHeader), &compressed_data[0], image_data_size); - image_data_to_write = &compressed_data[0]; - VLOG(compiler) << "Compressed from " << image_data_size << " to " << data_size; + + break; + } + case ImageHeader::kStorageModeLZ4HC: { + // Bound is same as non HC. + const size_t compressed_max_size = LZ4_compressBound(image_data_size); + compressed_data.reset(new char[compressed_max_size]); + data_size = LZ4_compressHC( + reinterpret_cast<char*>(image_info.image_->Begin()) + sizeof(ImageHeader), + &compressed_data[0], + image_data_size); break; } case ImageHeader::kStorageModeUncompressed: { @@ -249,6 +260,12 @@ bool ImageWriter::Write(int image_fd, } } + if (compressed_data != nullptr) { + image_data_to_write = &compressed_data[0]; + VLOG(compiler) << "Compressed from " << image_data_size << " to " << data_size << " in " + << PrettyDuration(NanoTime() - compress_start_time); + } + // Write header first, as uncompressed. image_header->data_size_ = data_size; if (!image_file->WriteFully(image_info.image_->Begin(), sizeof(ImageHeader))) { diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index 35ec7d41ff..57660c2623 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -282,7 +282,7 @@ void HGraphBuilder::InsertTryBoundaryBlocks(const DexFile::CodeItem& code_item) // Found a predecessor not covered by the same TryItem. Insert entering // boundary block. HTryBoundary* try_entry = - new (arena_) HTryBoundary(HTryBoundary::kEntry, try_block->GetDexPc()); + new (arena_) HTryBoundary(HTryBoundary::BoundaryKind::kEntry, try_block->GetDexPc()); try_block->CreateImmediateDominator()->AddInstruction(try_entry); LinkToCatchBlocks(try_entry, code_item, entry.second); break; @@ -316,7 +316,7 @@ void HGraphBuilder::InsertTryBoundaryBlocks(const DexFile::CodeItem& code_item) // Insert TryBoundary and link to catch blocks. HTryBoundary* try_exit = - new (arena_) HTryBoundary(HTryBoundary::kExit, successor->GetDexPc()); + new (arena_) HTryBoundary(HTryBoundary::BoundaryKind::kExit, successor->GetDexPc()); graph_->SplitEdge(try_block, successor)->AddInstruction(try_exit); LinkToCatchBlocks(try_exit, code_item, entry.second); } @@ -2843,7 +2843,7 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 case Instruction::MONITOR_ENTER: { current_block_->AddInstruction(new (arena_) HMonitorOperation( LoadLocal(instruction.VRegA_11x(), Primitive::kPrimNot, dex_pc), - HMonitorOperation::kEnter, + HMonitorOperation::OperationKind::kEnter, dex_pc)); break; } @@ -2851,7 +2851,7 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 case Instruction::MONITOR_EXIT: { current_block_->AddInstruction(new (arena_) HMonitorOperation( LoadLocal(instruction.VRegA_11x(), Primitive::kPrimNot, dex_pc), - HMonitorOperation::kExit, + HMonitorOperation::OperationKind::kExit, dex_pc)); break; } diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index cdbb9c31aa..aa9b01f30b 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -6413,6 +6413,33 @@ Literal* CodeGeneratorARM::DeduplicateMethodCodeLiteral(MethodReference target_m return DeduplicateMethodLiteral(target_method, &call_patches_); } +void LocationsBuilderARM::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instr, LocationSummary::kNoCall); + locations->SetInAt(HMultiplyAccumulate::kInputAccumulatorIndex, + Location::RequiresRegister()); + locations->SetInAt(HMultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister()); + locations->SetInAt(HMultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +} + +void InstructionCodeGeneratorARM::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) { + LocationSummary* locations = instr->GetLocations(); + Register res = locations->Out().AsRegister<Register>(); + Register accumulator = + locations->InAt(HMultiplyAccumulate::kInputAccumulatorIndex).AsRegister<Register>(); + Register mul_left = + locations->InAt(HMultiplyAccumulate::kInputMulLeftIndex).AsRegister<Register>(); + Register mul_right = + locations->InAt(HMultiplyAccumulate::kInputMulRightIndex).AsRegister<Register>(); + + if (instr->GetOpKind() == HInstruction::kAdd) { + __ mla(res, mul_left, mul_right, accumulator); + } else { + __ mls(res, mul_left, mul_right, accumulator); + } +} + void LocationsBuilderARM::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) { // Nothing to do, this should be removed during prepare for register allocator. LOG(FATAL) << "Unreachable"; @@ -6567,7 +6594,7 @@ void LocationsBuilderARM::VisitClassTableGet(HClassTableGet* instruction) { void InstructionCodeGeneratorARM::VisitClassTableGet(HClassTableGet* instruction) { LocationSummary* locations = instruction->GetLocations(); uint32_t method_offset = 0; - if (instruction->GetTableKind() == HClassTableGet::kVTable) { + if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) { method_offset = mirror::Class::EmbeddedVTableEntryOffset( instruction->GetIndex(), kArmPointerSize).SizeValue(); } else { diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index 2e4dc1e014..06e7c0015c 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -159,6 +159,7 @@ class LocationsBuilderARM : public HGraphVisitor { FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION) FOR_EACH_CONCRETE_INSTRUCTION_ARM(DECLARE_VISIT_INSTRUCTION) + FOR_EACH_CONCRETE_INSTRUCTION_SHARED(DECLARE_VISIT_INSTRUCTION) #undef DECLARE_VISIT_INSTRUCTION @@ -197,6 +198,7 @@ class InstructionCodeGeneratorARM : public InstructionCodeGenerator { FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION) FOR_EACH_CONCRETE_INSTRUCTION_ARM(DECLARE_VISIT_INSTRUCTION) + FOR_EACH_CONCRETE_INSTRUCTION_SHARED(DECLARE_VISIT_INSTRUCTION) #undef DECLARE_VISIT_INSTRUCTION diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 814f8b4d51..985dc056f6 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -1862,6 +1862,36 @@ void InstructionCodeGeneratorARM64::VisitAnd(HAnd* instruction) { HandleBinaryOp(instruction); } +void LocationsBuilderARM64::VisitArm64BitwiseNegatedRight(HArm64BitwiseNegatedRight* instr) { + DCHECK(Primitive::IsIntegralType(instr->GetType())) << instr->GetType(); + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instr); + locations->SetInAt(0, Location::RequiresRegister()); + // There is no immediate variant of negated bitwise instructions in AArch64. + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +} + +void InstructionCodeGeneratorARM64::VisitArm64BitwiseNegatedRight( + HArm64BitwiseNegatedRight* instr) { + Register dst = OutputRegister(instr); + Register lhs = InputRegisterAt(instr, 0); + Register rhs = InputRegisterAt(instr, 1); + + switch (instr->GetOpKind()) { + case HInstruction::kAnd: + __ Bic(dst, lhs, rhs); + break; + case HInstruction::kOr: + __ Orn(dst, lhs, rhs); + break; + case HInstruction::kXor: + __ Eon(dst, lhs, rhs); + break; + default: + LOG(FATAL) << "Unreachable"; + } +} + void LocationsBuilderARM64::VisitArm64DataProcWithShifterOp( HArm64DataProcWithShifterOp* instruction) { DCHECK(instruction->GetType() == Primitive::kPrimInt || @@ -1959,21 +1989,27 @@ void InstructionCodeGeneratorARM64::VisitArm64IntermediateAddress( Operand(InputOperandAt(instruction, 1))); } -void LocationsBuilderARM64::VisitArm64MultiplyAccumulate(HArm64MultiplyAccumulate* instr) { +void LocationsBuilderARM64::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instr, LocationSummary::kNoCall); - locations->SetInAt(HArm64MultiplyAccumulate::kInputAccumulatorIndex, - Location::RequiresRegister()); - locations->SetInAt(HArm64MultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister()); - locations->SetInAt(HArm64MultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister()); + HInstruction* accumulator = instr->InputAt(HMultiplyAccumulate::kInputAccumulatorIndex); + if (instr->GetOpKind() == HInstruction::kSub && + accumulator->IsConstant() && + accumulator->AsConstant()->IsZero()) { + // Don't allocate register for Mneg instruction. + } else { + locations->SetInAt(HMultiplyAccumulate::kInputAccumulatorIndex, + Location::RequiresRegister()); + } + locations->SetInAt(HMultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister()); + locations->SetInAt(HMultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister()); locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } -void InstructionCodeGeneratorARM64::VisitArm64MultiplyAccumulate(HArm64MultiplyAccumulate* instr) { +void InstructionCodeGeneratorARM64::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) { Register res = OutputRegister(instr); - Register accumulator = InputRegisterAt(instr, HArm64MultiplyAccumulate::kInputAccumulatorIndex); - Register mul_left = InputRegisterAt(instr, HArm64MultiplyAccumulate::kInputMulLeftIndex); - Register mul_right = InputRegisterAt(instr, HArm64MultiplyAccumulate::kInputMulRightIndex); + Register mul_left = InputRegisterAt(instr, HMultiplyAccumulate::kInputMulLeftIndex); + Register mul_right = InputRegisterAt(instr, HMultiplyAccumulate::kInputMulRightIndex); // Avoid emitting code that could trigger Cortex A53's erratum 835769. // This fixup should be carried out for all multiply-accumulate instructions: @@ -1993,10 +2029,17 @@ void InstructionCodeGeneratorARM64::VisitArm64MultiplyAccumulate(HArm64MultiplyA } if (instr->GetOpKind() == HInstruction::kAdd) { + Register accumulator = InputRegisterAt(instr, HMultiplyAccumulate::kInputAccumulatorIndex); __ Madd(res, mul_left, mul_right, accumulator); } else { DCHECK(instr->GetOpKind() == HInstruction::kSub); - __ Msub(res, mul_left, mul_right, accumulator); + HInstruction* accum_instr = instr->InputAt(HMultiplyAccumulate::kInputAccumulatorIndex); + if (accum_instr->IsConstant() && accum_instr->AsConstant()->IsZero()) { + __ Mneg(res, mul_left, mul_right); + } else { + Register accumulator = InputRegisterAt(instr, HMultiplyAccumulate::kInputAccumulatorIndex); + __ Msub(res, mul_left, mul_right, accumulator); + } } } @@ -5017,7 +5060,7 @@ void LocationsBuilderARM64::VisitClassTableGet(HClassTableGet* instruction) { void InstructionCodeGeneratorARM64::VisitClassTableGet(HClassTableGet* instruction) { LocationSummary* locations = instruction->GetLocations(); uint32_t method_offset = 0; - if (instruction->GetTableKind() == HClassTableGet::kVTable) { + if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) { method_offset = mirror::Class::EmbeddedVTableEntryOffset( instruction->GetIndex(), kArm64PointerSize).SizeValue(); } else { diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index 3527261835..10f1e7f008 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -196,6 +196,7 @@ class InstructionCodeGeneratorARM64 : public InstructionCodeGenerator { FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION) FOR_EACH_CONCRETE_INSTRUCTION_ARM64(DECLARE_VISIT_INSTRUCTION) + FOR_EACH_CONCRETE_INSTRUCTION_SHARED(DECLARE_VISIT_INSTRUCTION) #undef DECLARE_VISIT_INSTRUCTION @@ -285,6 +286,7 @@ class LocationsBuilderARM64 : public HGraphVisitor { FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION) FOR_EACH_CONCRETE_INSTRUCTION_ARM64(DECLARE_VISIT_INSTRUCTION) + FOR_EACH_CONCRETE_INSTRUCTION_SHARED(DECLARE_VISIT_INSTRUCTION) #undef DECLARE_VISIT_INSTRUCTION diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index 8d3d94b79d..f3c12efd8d 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -5245,7 +5245,7 @@ void LocationsBuilderMIPS::VisitClassTableGet(HClassTableGet* instruction) { void InstructionCodeGeneratorMIPS::VisitClassTableGet(HClassTableGet* instruction) { LocationSummary* locations = instruction->GetLocations(); uint32_t method_offset = 0; - if (instruction->GetTableKind() == HClassTableGet::kVTable) { + if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) { method_offset = mirror::Class::EmbeddedVTableEntryOffset( instruction->GetIndex(), kMipsPointerSize).SizeValue(); } else { diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 88e42f3faf..6b4a18c688 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -4127,7 +4127,7 @@ void LocationsBuilderX86::VisitClassTableGet(HClassTableGet* instruction) { void InstructionCodeGeneratorX86::VisitClassTableGet(HClassTableGet* instruction) { LocationSummary* locations = instruction->GetLocations(); uint32_t method_offset = 0; - if (instruction->GetTableKind() == HClassTableGet::kVTable) { + if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) { method_offset = mirror::Class::EmbeddedVTableEntryOffset( instruction->GetIndex(), kX86PointerSize).SizeValue(); } else { diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index bb24c6f59c..c132663016 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -3998,7 +3998,7 @@ void LocationsBuilderX86_64::VisitClassTableGet(HClassTableGet* instruction) { void InstructionCodeGeneratorX86_64::VisitClassTableGet(HClassTableGet* instruction) { LocationSummary* locations = instruction->GetLocations(); uint32_t method_offset = 0; - if (instruction->GetTableKind() == HClassTableGet::kVTable) { + if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) { method_offset = mirror::Class::EmbeddedVTableEntryOffset( instruction->GetIndex(), kX86_64PointerSize).SizeValue(); } else { diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index c0263e4e5b..b9638f2027 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -436,17 +436,23 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { StartAttributeStream("kind") << (try_boundary->IsEntry() ? "entry" : "exit"); } +#if defined(ART_ENABLE_CODEGEN_arm) || defined(ART_ENABLE_CODEGEN_arm64) + void VisitMultiplyAccumulate(HMultiplyAccumulate* instruction) OVERRIDE { + StartAttributeStream("kind") << instruction->GetOpKind(); + } +#endif + #ifdef ART_ENABLE_CODEGEN_arm64 + void VisitArm64BitwiseNegatedRight(HArm64BitwiseNegatedRight* instruction) OVERRIDE { + StartAttributeStream("kind") << instruction->GetOpKind(); + } + void VisitArm64DataProcWithShifterOp(HArm64DataProcWithShifterOp* instruction) OVERRIDE { StartAttributeStream("kind") << instruction->GetInstrKind() << "+" << instruction->GetOpKind(); if (HArm64DataProcWithShifterOp::IsShiftOp(instruction->GetOpKind())) { StartAttributeStream("shift") << instruction->GetShiftAmount(); } } - - void VisitArm64MultiplyAccumulate(HArm64MultiplyAccumulate* instruction) OVERRIDE { - StartAttributeStream("kind") << instruction->GetOpKind(); - } #endif bool IsPass(const char* name) { diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc index a1e1cde9df..82a898a9f1 100644 --- a/compiler/optimizing/induction_var_analysis.cc +++ b/compiler/optimizing/induction_var_analysis.cc @@ -552,9 +552,11 @@ void HInductionVarAnalysis::VisitCondition(HLoopInformation* loop, if (!IsExact(stride_expr, &stride_value)) { return; } - // Rewrite condition i != U into i < U or i > U if end condition is reached exactly. - if (cmp == kCondNE && ((stride_value == +1 && IsTaken(lower_expr, upper_expr, kCondLT)) || - (stride_value == -1 && IsTaken(lower_expr, upper_expr, kCondGT)))) { + // Rewrite condition i != U into strict end condition i < U or i > U if this end condition + // is reached exactly (tested by verifying if the loop has a unit stride and the non-strict + // condition would be always taken). + if (cmp == kCondNE && ((stride_value == +1 && IsTaken(lower_expr, upper_expr, kCondLE)) || + (stride_value == -1 && IsTaken(lower_expr, upper_expr, kCondGE)))) { cmp = stride_value > 0 ? kCondLT : kCondGT; } // Normalize a linear loop control with a nonzero stride: diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc index b162696a42..f9b6910acd 100644 --- a/compiler/optimizing/induction_var_range.cc +++ b/compiler/optimizing/induction_var_range.cc @@ -216,6 +216,14 @@ bool InductionVarRange::IsConstant(HInductionVarAnalysis::InductionInfo* info, } } } while (RefineOuter(&v_min, &v_max)); + // Exploit array length + c >= c, with c <= 0 to avoid arithmetic wrap-around anomalies + // (e.g. array length == maxint and c == 1 would yield minint). + if (request == kAtLeast) { + if (v_min.a_constant == 1 && v_min.b_constant <= 0 && v_min.instruction->IsArrayLength()) { + *value = v_min.b_constant; + return true; + } + } } return false; } diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 02a1acc240..d55009554f 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -651,7 +651,8 @@ bool HInliner::TryInlinePolymorphicCallToSameTarget(HInvoke* invoke_instruction, HClassTableGet* class_table_get = new (graph_->GetArena()) HClassTableGet( receiver_class, type, - invoke_instruction->IsInvokeVirtual() ? HClassTableGet::kVTable : HClassTableGet::kIMTable, + invoke_instruction->IsInvokeVirtual() ? HClassTableGet::TableKind::kVTable + : HClassTableGet::TableKind::kIMTable, method_offset, invoke_instruction->GetDexPc()); diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 13d3f752c3..f8a9a94e62 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -93,6 +93,7 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { void SimplifyStringEquals(HInvoke* invoke); void SimplifyCompare(HInvoke* invoke, bool has_zero_op); void SimplifyIsNaN(HInvoke* invoke); + void SimplifyFP2Int(HInvoke* invoke); OptimizingCompilerStats* stats_; bool simplification_occurred_ = false; @@ -1562,26 +1563,71 @@ void InstructionSimplifierVisitor::SimplifyIsNaN(HInvoke* invoke) { invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, condition); } +void InstructionSimplifierVisitor::SimplifyFP2Int(HInvoke* invoke) { + DCHECK(invoke->IsInvokeStaticOrDirect()); + uint32_t dex_pc = invoke->GetDexPc(); + HInstruction* x = invoke->InputAt(0); + Primitive::Type type = x->GetType(); + // Set proper bit pattern for NaN and replace intrinsic with raw version. + HInstruction* nan; + if (type == Primitive::kPrimDouble) { + nan = GetGraph()->GetLongConstant(0x7ff8000000000000L); + invoke->SetIntrinsic(Intrinsics::kDoubleDoubleToRawLongBits, + kNeedsEnvironmentOrCache, + kNoSideEffects, + kNoThrow); + } else { + DCHECK_EQ(type, Primitive::kPrimFloat); + nan = GetGraph()->GetIntConstant(0x7fc00000); + invoke->SetIntrinsic(Intrinsics::kFloatFloatToRawIntBits, + kNeedsEnvironmentOrCache, + kNoSideEffects, + kNoThrow); + } + // Test IsNaN(x), which is the same as x != x. + HCondition* condition = new (GetGraph()->GetArena()) HNotEqual(x, x, dex_pc); + condition->SetBias(ComparisonBias::kLtBias); + invoke->GetBlock()->InsertInstructionBefore(condition, invoke->GetNext()); + // Select between the two. + HInstruction* select = new (GetGraph()->GetArena()) HSelect(condition, nan, invoke, dex_pc); + invoke->GetBlock()->InsertInstructionBefore(select, condition->GetNext()); + invoke->ReplaceWithExceptInReplacementAtIndex(select, 0); // false at index 0 +} + void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) { - if (instruction->GetIntrinsic() == Intrinsics::kStringEquals) { - SimplifyStringEquals(instruction); - } else if (instruction->GetIntrinsic() == Intrinsics::kSystemArrayCopy) { - SimplifySystemArrayCopy(instruction); - } else if (instruction->GetIntrinsic() == Intrinsics::kIntegerRotateRight || - instruction->GetIntrinsic() == Intrinsics::kLongRotateRight) { - SimplifyRotate(instruction, false); - } else if (instruction->GetIntrinsic() == Intrinsics::kIntegerRotateLeft || - instruction->GetIntrinsic() == Intrinsics::kLongRotateLeft) { - SimplifyRotate(instruction, true); - } else if (instruction->GetIntrinsic() == Intrinsics::kIntegerCompare || - instruction->GetIntrinsic() == Intrinsics::kLongCompare) { - SimplifyCompare(instruction, /* is_signum */ false); - } else if (instruction->GetIntrinsic() == Intrinsics::kIntegerSignum || - instruction->GetIntrinsic() == Intrinsics::kLongSignum) { - SimplifyCompare(instruction, /* is_signum */ true); - } else if (instruction->GetIntrinsic() == Intrinsics::kFloatIsNaN || - instruction->GetIntrinsic() == Intrinsics::kDoubleIsNaN) { - SimplifyIsNaN(instruction); + switch (instruction->GetIntrinsic()) { + case Intrinsics::kStringEquals: + SimplifyStringEquals(instruction); + break; + case Intrinsics::kSystemArrayCopy: + SimplifySystemArrayCopy(instruction); + break; + case Intrinsics::kIntegerRotateRight: + case Intrinsics::kLongRotateRight: + SimplifyRotate(instruction, false); + break; + case Intrinsics::kIntegerRotateLeft: + case Intrinsics::kLongRotateLeft: + SimplifyRotate(instruction, true); + break; + case Intrinsics::kIntegerCompare: + case Intrinsics::kLongCompare: + SimplifyCompare(instruction, /* is_signum */ false); + break; + case Intrinsics::kIntegerSignum: + case Intrinsics::kLongSignum: + SimplifyCompare(instruction, /* is_signum */ true); + break; + case Intrinsics::kFloatIsNaN: + case Intrinsics::kDoubleIsNaN: + SimplifyIsNaN(instruction); + break; + case Intrinsics::kFloatFloatToIntBits: + case Intrinsics::kDoubleDoubleToLongBits: + SimplifyFP2Int(instruction); + break; + default: + break; } } diff --git a/compiler/optimizing/instruction_simplifier_arm.cc b/compiler/optimizing/instruction_simplifier_arm.cc new file mode 100644 index 0000000000..db1f9a79aa --- /dev/null +++ b/compiler/optimizing/instruction_simplifier_arm.cc @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "instruction_simplifier_arm.h" +#include "instruction_simplifier_shared.h" + +namespace art { +namespace arm { + +void InstructionSimplifierArmVisitor::VisitMul(HMul* instruction) { + if (TryCombineMultiplyAccumulate(instruction, kArm)) { + RecordSimplification(); + } +} + +} // namespace arm +} // namespace art diff --git a/compiler/optimizing/instruction_simplifier_arm.h b/compiler/optimizing/instruction_simplifier_arm.h new file mode 100644 index 0000000000..379b95d6ae --- /dev/null +++ b/compiler/optimizing/instruction_simplifier_arm.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_ARM_H_ +#define ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_ARM_H_ + +#include "nodes.h" +#include "optimization.h" + +namespace art { +namespace arm { + +class InstructionSimplifierArmVisitor : public HGraphVisitor { + public: + InstructionSimplifierArmVisitor(HGraph* graph, OptimizingCompilerStats* stats) + : HGraphVisitor(graph), stats_(stats) {} + + private: + void RecordSimplification() { + if (stats_ != nullptr) { + stats_->RecordStat(kInstructionSimplificationsArch); + } + } + + void VisitMul(HMul* instruction) OVERRIDE; + + OptimizingCompilerStats* stats_; +}; + + +class InstructionSimplifierArm : public HOptimization { + public: + InstructionSimplifierArm(HGraph* graph, OptimizingCompilerStats* stats) + : HOptimization(graph, "instruction_simplifier_arm", stats) {} + + void Run() OVERRIDE { + InstructionSimplifierArmVisitor visitor(graph_, stats_); + visitor.VisitReversePostOrder(); + } +}; + +} // namespace arm +} // namespace art + +#endif // ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_ARM_H_ diff --git a/compiler/optimizing/instruction_simplifier_arm64.cc b/compiler/optimizing/instruction_simplifier_arm64.cc index 4bcfc54791..c2bbdccc29 100644 --- a/compiler/optimizing/instruction_simplifier_arm64.cc +++ b/compiler/optimizing/instruction_simplifier_arm64.cc @@ -17,6 +17,7 @@ #include "instruction_simplifier_arm64.h" #include "common_arm64.h" +#include "instruction_simplifier_shared.h" #include "mirror/array-inl.h" namespace art { @@ -179,67 +180,53 @@ bool InstructionSimplifierArm64Visitor::TryMergeIntoUsersShifterOperand(HInstruc return true; } -bool InstructionSimplifierArm64Visitor::TrySimpleMultiplyAccumulatePatterns( - HMul* mul, HBinaryOperation* input_binop, HInstruction* input_other) { - DCHECK(Primitive::IsIntOrLongType(mul->GetType())); - DCHECK(input_binop->IsAdd() || input_binop->IsSub()); - DCHECK_NE(input_binop, input_other); - if (!input_binop->HasOnlyOneNonEnvironmentUse()) { - return false; - } - - // Try to interpret patterns like - // a * (b <+/-> 1) - // as - // (a * b) <+/-> a - HInstruction* input_a = input_other; - HInstruction* input_b = nullptr; // Set to a non-null value if we found a pattern to optimize. - HInstruction::InstructionKind op_kind; - - if (input_binop->IsAdd()) { - if ((input_binop->GetConstantRight() != nullptr) && input_binop->GetConstantRight()->IsOne()) { - // Interpret - // a * (b + 1) - // as - // (a * b) + a - input_b = input_binop->GetLeastConstantLeft(); - op_kind = HInstruction::kAdd; - } - } else { - DCHECK(input_binop->IsSub()); - if (input_binop->GetRight()->IsConstant() && - input_binop->GetRight()->AsConstant()->IsMinusOne()) { - // Interpret - // a * (b - (-1)) - // as - // a + (a * b) - input_b = input_binop->GetLeft(); - op_kind = HInstruction::kAdd; - } else if (input_binop->GetLeft()->IsConstant() && - input_binop->GetLeft()->AsConstant()->IsOne()) { - // Interpret - // a * (1 - b) - // as - // a - (a * b) - input_b = input_binop->GetRight(); - op_kind = HInstruction::kSub; +bool InstructionSimplifierArm64Visitor::TryMergeNegatedInput(HBinaryOperation* op) { + DCHECK(op->IsAnd() || op->IsOr() || op->IsXor()) << op->DebugName(); + HInstruction* left = op->GetLeft(); + HInstruction* right = op->GetRight(); + + // Only consider the case where there is exactly one Not, with 2 Not's De + // Morgan's laws should be applied instead. + if (left->IsNot() ^ right->IsNot()) { + HInstruction* hnot = (left->IsNot() ? left : right); + HInstruction* hother = (left->IsNot() ? right : left); + + // Only do the simplification if the Not has only one use and can thus be + // safely removed. Even though ARM64 negated bitwise operations do not have + // an immediate variant (only register), we still do the simplification when + // `hother` is a constant, because it removes an instruction if the constant + // cannot be encoded as an immediate: + // mov r0, #large_constant + // neg r2, r1 + // and r0, r0, r2 + // becomes: + // mov r0, #large_constant + // bic r0, r0, r1 + if (hnot->HasOnlyOneNonEnvironmentUse()) { + // Replace code looking like + // NOT tmp, mask + // AND dst, src, tmp (respectively ORR, EOR) + // with + // BIC dst, src, mask (respectively ORN, EON) + HInstruction* src = hnot->AsNot()->GetInput(); + + HArm64BitwiseNegatedRight* neg_op = new (GetGraph()->GetArena()) + HArm64BitwiseNegatedRight(op->GetType(), op->GetKind(), hother, src, op->GetDexPc()); + + op->GetBlock()->ReplaceAndRemoveInstructionWith(op, neg_op); + hnot->GetBlock()->RemoveInstruction(hnot); + RecordSimplification(); + return true; } } - if (input_b == nullptr) { - // We did not find a pattern we can optimize. - return false; - } - - HArm64MultiplyAccumulate* mulacc = new(GetGraph()->GetArena()) HArm64MultiplyAccumulate( - mul->GetType(), op_kind, input_a, input_a, input_b, mul->GetDexPc()); - - mul->GetBlock()->ReplaceAndRemoveInstructionWith(mul, mulacc); - input_binop->GetBlock()->RemoveInstruction(input_binop); - return false; } +void InstructionSimplifierArm64Visitor::VisitAnd(HAnd* instruction) { + TryMergeNegatedInput(instruction); +} + void InstructionSimplifierArm64Visitor::VisitArrayGet(HArrayGet* instruction) { TryExtractArrayAccessAddress(instruction, instruction->GetArray(), @@ -255,76 +242,13 @@ void InstructionSimplifierArm64Visitor::VisitArraySet(HArraySet* instruction) { } void InstructionSimplifierArm64Visitor::VisitMul(HMul* instruction) { - Primitive::Type type = instruction->GetType(); - if (!Primitive::IsIntOrLongType(type)) { - return; - } - - HInstruction* use = instruction->HasNonEnvironmentUses() - ? instruction->GetUses().GetFirst()->GetUser() - : nullptr; - - if (instruction->HasOnlyOneNonEnvironmentUse() && (use->IsAdd() || use->IsSub())) { - // Replace code looking like - // MUL tmp, x, y - // SUB dst, acc, tmp - // with - // MULSUB dst, acc, x, y - // Note that we do not want to (unconditionally) perform the merge when the - // multiplication has multiple uses and it can be merged in all of them. - // Multiple uses could happen on the same control-flow path, and we would - // then increase the amount of work. In the future we could try to evaluate - // whether all uses are on different control-flow paths (using dominance and - // reverse-dominance information) and only perform the merge when they are. - HInstruction* accumulator = nullptr; - HBinaryOperation* binop = use->AsBinaryOperation(); - HInstruction* binop_left = binop->GetLeft(); - HInstruction* binop_right = binop->GetRight(); - // Be careful after GVN. This should not happen since the `HMul` has only - // one use. - DCHECK_NE(binop_left, binop_right); - if (binop_right == instruction) { - accumulator = binop_left; - } else if (use->IsAdd()) { - DCHECK_EQ(binop_left, instruction); - accumulator = binop_right; - } - - if (accumulator != nullptr) { - HArm64MultiplyAccumulate* mulacc = - new (GetGraph()->GetArena()) HArm64MultiplyAccumulate(type, - binop->GetKind(), - accumulator, - instruction->GetLeft(), - instruction->GetRight()); - - binop->GetBlock()->ReplaceAndRemoveInstructionWith(binop, mulacc); - DCHECK(!instruction->HasUses()); - instruction->GetBlock()->RemoveInstruction(instruction); - RecordSimplification(); - return; - } - } - - // Use multiply accumulate instruction for a few simple patterns. - // We prefer not applying the following transformations if the left and - // right inputs perform the same operation. - // We rely on GVN having squashed the inputs if appropriate. However the - // results are still correct even if that did not happen. - if (instruction->GetLeft() == instruction->GetRight()) { - return; + if (TryCombineMultiplyAccumulate(instruction, kArm64)) { + RecordSimplification(); } +} - HInstruction* left = instruction->GetLeft(); - HInstruction* right = instruction->GetRight(); - if ((right->IsAdd() || right->IsSub()) && - TrySimpleMultiplyAccumulatePatterns(instruction, right->AsBinaryOperation(), left)) { - return; - } - if ((left->IsAdd() || left->IsSub()) && - TrySimpleMultiplyAccumulatePatterns(instruction, left->AsBinaryOperation(), right)) { - return; - } +void InstructionSimplifierArm64Visitor::VisitOr(HOr* instruction) { + TryMergeNegatedInput(instruction); } void InstructionSimplifierArm64Visitor::VisitShl(HShl* instruction) { @@ -359,5 +283,9 @@ void InstructionSimplifierArm64Visitor::VisitUShr(HUShr* instruction) { } } +void InstructionSimplifierArm64Visitor::VisitXor(HXor* instruction) { + TryMergeNegatedInput(instruction); +} + } // namespace arm64 } // namespace art diff --git a/compiler/optimizing/instruction_simplifier_arm64.h b/compiler/optimizing/instruction_simplifier_arm64.h index b7f490bb8c..cf8458713f 100644 --- a/compiler/optimizing/instruction_simplifier_arm64.h +++ b/compiler/optimizing/instruction_simplifier_arm64.h @@ -51,18 +51,21 @@ class InstructionSimplifierArm64Visitor : public HGraphVisitor { return TryMergeIntoShifterOperand(use, bitfield_op, true); } - bool TrySimpleMultiplyAccumulatePatterns(HMul* mul, - HBinaryOperation* input_binop, - HInstruction* input_other); + // For bitwise operations (And/Or/Xor) with a negated input, try to use + // a negated bitwise instruction. + bool TryMergeNegatedInput(HBinaryOperation* op); // HInstruction visitors, sorted alphabetically. + void VisitAnd(HAnd* instruction) OVERRIDE; void VisitArrayGet(HArrayGet* instruction) OVERRIDE; void VisitArraySet(HArraySet* instruction) OVERRIDE; void VisitMul(HMul* instruction) OVERRIDE; + void VisitOr(HOr* instruction) OVERRIDE; void VisitShl(HShl* instruction) OVERRIDE; void VisitShr(HShr* instruction) OVERRIDE; void VisitTypeConversion(HTypeConversion* instruction) OVERRIDE; void VisitUShr(HUShr* instruction) OVERRIDE; + void VisitXor(HXor* instruction) OVERRIDE; OptimizingCompilerStats* stats_; }; diff --git a/compiler/optimizing/instruction_simplifier_shared.cc b/compiler/optimizing/instruction_simplifier_shared.cc new file mode 100644 index 0000000000..45d196fa6d --- /dev/null +++ b/compiler/optimizing/instruction_simplifier_shared.cc @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "instruction_simplifier_shared.h" + +namespace art { + +namespace { + +bool TrySimpleMultiplyAccumulatePatterns(HMul* mul, + HBinaryOperation* input_binop, + HInstruction* input_other) { + DCHECK(Primitive::IsIntOrLongType(mul->GetType())); + DCHECK(input_binop->IsAdd() || input_binop->IsSub()); + DCHECK_NE(input_binop, input_other); + if (!input_binop->HasOnlyOneNonEnvironmentUse()) { + return false; + } + + // Try to interpret patterns like + // a * (b <+/-> 1) + // as + // (a * b) <+/-> a + HInstruction* input_a = input_other; + HInstruction* input_b = nullptr; // Set to a non-null value if we found a pattern to optimize. + HInstruction::InstructionKind op_kind; + + if (input_binop->IsAdd()) { + if ((input_binop->GetConstantRight() != nullptr) && input_binop->GetConstantRight()->IsOne()) { + // Interpret + // a * (b + 1) + // as + // (a * b) + a + input_b = input_binop->GetLeastConstantLeft(); + op_kind = HInstruction::kAdd; + } + } else { + DCHECK(input_binop->IsSub()); + if (input_binop->GetRight()->IsConstant() && + input_binop->GetRight()->AsConstant()->IsMinusOne()) { + // Interpret + // a * (b - (-1)) + // as + // a + (a * b) + input_b = input_binop->GetLeft(); + op_kind = HInstruction::kAdd; + } else if (input_binop->GetLeft()->IsConstant() && + input_binop->GetLeft()->AsConstant()->IsOne()) { + // Interpret + // a * (1 - b) + // as + // a - (a * b) + input_b = input_binop->GetRight(); + op_kind = HInstruction::kSub; + } + } + + if (input_b == nullptr) { + // We did not find a pattern we can optimize. + return false; + } + + ArenaAllocator* arena = mul->GetBlock()->GetGraph()->GetArena(); + HMultiplyAccumulate* mulacc = new(arena) HMultiplyAccumulate( + mul->GetType(), op_kind, input_a, input_a, input_b, mul->GetDexPc()); + + mul->GetBlock()->ReplaceAndRemoveInstructionWith(mul, mulacc); + input_binop->GetBlock()->RemoveInstruction(input_binop); + + return true; +} + +} // namespace + +bool TryCombineMultiplyAccumulate(HMul* mul, InstructionSet isa) { + Primitive::Type type = mul->GetType(); + switch (isa) { + case kArm: + case kThumb2: + if (type != Primitive::kPrimInt) { + return false; + } + break; + case kArm64: + if (!Primitive::IsIntOrLongType(type)) { + return false; + } + break; + default: + return false; + } + + HInstruction* use = mul->HasNonEnvironmentUses() + ? mul->GetUses().GetFirst()->GetUser() + : nullptr; + + ArenaAllocator* arena = mul->GetBlock()->GetGraph()->GetArena(); + + if (mul->HasOnlyOneNonEnvironmentUse()) { + if (use->IsAdd() || use->IsSub()) { + // Replace code looking like + // MUL tmp, x, y + // SUB dst, acc, tmp + // with + // MULSUB dst, acc, x, y + // Note that we do not want to (unconditionally) perform the merge when the + // multiplication has multiple uses and it can be merged in all of them. + // Multiple uses could happen on the same control-flow path, and we would + // then increase the amount of work. In the future we could try to evaluate + // whether all uses are on different control-flow paths (using dominance and + // reverse-dominance information) and only perform the merge when they are. + HInstruction* accumulator = nullptr; + HBinaryOperation* binop = use->AsBinaryOperation(); + HInstruction* binop_left = binop->GetLeft(); + HInstruction* binop_right = binop->GetRight(); + // Be careful after GVN. This should not happen since the `HMul` has only + // one use. + DCHECK_NE(binop_left, binop_right); + if (binop_right == mul) { + accumulator = binop_left; + } else if (use->IsAdd()) { + DCHECK_EQ(binop_left, mul); + accumulator = binop_right; + } + + if (accumulator != nullptr) { + HMultiplyAccumulate* mulacc = + new (arena) HMultiplyAccumulate(type, + binop->GetKind(), + accumulator, + mul->GetLeft(), + mul->GetRight()); + + binop->GetBlock()->ReplaceAndRemoveInstructionWith(binop, mulacc); + DCHECK(!mul->HasUses()); + mul->GetBlock()->RemoveInstruction(mul); + return true; + } + } else if (use->IsNeg() && isa != kArm) { + HMultiplyAccumulate* mulacc = + new (arena) HMultiplyAccumulate(type, + HInstruction::kSub, + mul->GetBlock()->GetGraph()->GetConstant(type, 0), + mul->GetLeft(), + mul->GetRight()); + + use->GetBlock()->ReplaceAndRemoveInstructionWith(use, mulacc); + DCHECK(!mul->HasUses()); + mul->GetBlock()->RemoveInstruction(mul); + return true; + } + } + + // Use multiply accumulate instruction for a few simple patterns. + // We prefer not applying the following transformations if the left and + // right inputs perform the same operation. + // We rely on GVN having squashed the inputs if appropriate. However the + // results are still correct even if that did not happen. + if (mul->GetLeft() == mul->GetRight()) { + return false; + } + + HInstruction* left = mul->GetLeft(); + HInstruction* right = mul->GetRight(); + if ((right->IsAdd() || right->IsSub()) && + TrySimpleMultiplyAccumulatePatterns(mul, right->AsBinaryOperation(), left)) { + return true; + } + if ((left->IsAdd() || left->IsSub()) && + TrySimpleMultiplyAccumulatePatterns(mul, left->AsBinaryOperation(), right)) { + return true; + } + return false; +} + +} // namespace art diff --git a/compiler/optimizing/instruction_simplifier_shared.h b/compiler/optimizing/instruction_simplifier_shared.h new file mode 100644 index 0000000000..9832ecc058 --- /dev/null +++ b/compiler/optimizing/instruction_simplifier_shared.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_SHARED_H_ +#define ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_SHARED_H_ + +#include "nodes.h" + +namespace art { + +bool TryCombineMultiplyAccumulate(HMul* mul, InstructionSet isa); + +} // namespace art + +#endif // ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_SHARED_H_ diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc index 316e86b4c9..3ed0278871 100644 --- a/compiler/optimizing/intrinsics.cc +++ b/compiler/optimizing/intrinsics.cc @@ -130,6 +130,10 @@ static Intrinsics GetIntrinsic(InlineMethod method) { case kIntrinsicFloatCvt: return ((method.d.data & kIntrinsicFlagToFloatingPoint) == 0) ? Intrinsics::kFloatFloatToRawIntBits : Intrinsics::kFloatIntBitsToFloat; + case kIntrinsicFloat2Int: + return Intrinsics::kFloatFloatToIntBits; + case kIntrinsicDouble2Long: + return Intrinsics::kDoubleDoubleToLongBits; // Floating-point tests. case kIntrinsicFloatIsInfinite: diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc index 8cbdcbbcaf..4ce919ee39 100644 --- a/compiler/optimizing/intrinsics_arm.cc +++ b/compiler/optimizing/intrinsics_arm.cc @@ -1909,6 +1909,69 @@ void IntrinsicCodeGeneratorARM::VisitShortReverseBytes(HInvoke* invoke) { __ revsh(out, in); } +void IntrinsicLocationsBuilderARM::VisitStringGetCharsNoCheck(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetInAt(2, Location::RequiresRegister()); + locations->SetInAt(3, Location::RequiresRegister()); + locations->SetInAt(4, Location::RequiresRegister()); + + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); +} + +void IntrinsicCodeGeneratorARM::VisitStringGetCharsNoCheck(HInvoke* invoke) { + ArmAssembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + // Check assumption that sizeof(Char) is 2 (used in scaling below). + const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar); + DCHECK_EQ(char_size, 2u); + + // Location of data in char array buffer. + const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value(); + + // Location of char array data in string. + const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value(); + + // void getCharsNoCheck(int srcBegin, int srcEnd, char[] dst, int dstBegin); + // Since getChars() calls getCharsNoCheck() - we use registers rather than constants. + Register srcObj = locations->InAt(0).AsRegister<Register>(); + Register srcBegin = locations->InAt(1).AsRegister<Register>(); + Register srcEnd = locations->InAt(2).AsRegister<Register>(); + Register dstObj = locations->InAt(3).AsRegister<Register>(); + Register dstBegin = locations->InAt(4).AsRegister<Register>(); + + Register src_ptr = locations->GetTemp(0).AsRegister<Register>(); + Register src_ptr_end = locations->GetTemp(1).AsRegister<Register>(); + Register dst_ptr = locations->GetTemp(2).AsRegister<Register>(); + Register tmp = locations->GetTemp(3).AsRegister<Register>(); + + // src range to copy. + __ add(src_ptr, srcObj, ShifterOperand(value_offset)); + __ add(src_ptr_end, src_ptr, ShifterOperand(srcEnd, LSL, 1)); + __ add(src_ptr, src_ptr, ShifterOperand(srcBegin, LSL, 1)); + + // dst to be copied. + __ add(dst_ptr, dstObj, ShifterOperand(data_offset)); + __ add(dst_ptr, dst_ptr, ShifterOperand(dstBegin, LSL, 1)); + + // Do the copy. + Label loop, done; + __ Bind(&loop); + __ cmp(src_ptr, ShifterOperand(src_ptr_end)); + __ b(&done, EQ); + __ ldrh(tmp, Address(src_ptr, char_size, Address::PostIndex)); + __ strh(tmp, Address(dst_ptr, char_size, Address::PostIndex)); + __ b(&loop); + __ Bind(&done); +} + // Unimplemented intrinsics. #define UNIMPLEMENTED_INTRINSIC(Name) \ @@ -1933,7 +1996,6 @@ UNIMPLEMENTED_INTRINSIC(MathRoundFloat) // Could be done by changing rounding UNIMPLEMENTED_INTRINSIC(UnsafeCASLong) // High register pressure. UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) -UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck) UNIMPLEMENTED_INTRINSIC(FloatIsInfinite) UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite) @@ -1944,6 +2006,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) // Handled as HIR instructions. +UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits) +UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits) UNIMPLEMENTED_INTRINSIC(FloatIsNaN) UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft) diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index b5f15fe22d..4be1695a94 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -1602,6 +1602,69 @@ void IntrinsicCodeGeneratorARM64::VisitMathNextAfter(HInvoke* invoke) { GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickNextAfter); } +void IntrinsicLocationsBuilderARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetInAt(2, Location::RequiresRegister()); + locations->SetInAt(3, Location::RequiresRegister()); + locations->SetInAt(4, Location::RequiresRegister()); + + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); +} + +void IntrinsicCodeGeneratorARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) { + vixl::MacroAssembler* masm = GetVIXLAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + // Check assumption that sizeof(Char) is 2 (used in scaling below). + const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar); + DCHECK_EQ(char_size, 2u); + + // Location of data in char array buffer. + const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value(); + + // Location of char array data in string. + const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value(); + + // void getCharsNoCheck(int srcBegin, int srcEnd, char[] dst, int dstBegin); + // Since getChars() calls getCharsNoCheck() - we use registers rather than constants. + Register srcObj = XRegisterFrom(locations->InAt(0)); + Register srcBegin = XRegisterFrom(locations->InAt(1)); + Register srcEnd = XRegisterFrom(locations->InAt(2)); + Register dstObj = XRegisterFrom(locations->InAt(3)); + Register dstBegin = XRegisterFrom(locations->InAt(4)); + + Register src_ptr = XRegisterFrom(locations->GetTemp(0)); + Register src_ptr_end = XRegisterFrom(locations->GetTemp(1)); + + UseScratchRegisterScope temps(masm); + Register dst_ptr = temps.AcquireX(); + Register tmp = temps.AcquireW(); + + // src range to copy. + __ Add(src_ptr, srcObj, Operand(value_offset)); + __ Add(src_ptr_end, src_ptr, Operand(srcEnd, LSL, 1)); + __ Add(src_ptr, src_ptr, Operand(srcBegin, LSL, 1)); + + // dst to be copied. + __ Add(dst_ptr, dstObj, Operand(data_offset)); + __ Add(dst_ptr, dst_ptr, Operand(dstBegin, LSL, 1)); + + // Do the copy. + vixl::Label loop, done; + __ Bind(&loop); + __ Cmp(src_ptr, src_ptr_end); + __ B(&done, eq); + __ Ldrh(tmp, MemOperand(src_ptr, char_size, vixl::PostIndex)); + __ Strh(tmp, MemOperand(dst_ptr, char_size, vixl::PostIndex)); + __ B(&loop); + __ Bind(&done); +} + // Unimplemented intrinsics. #define UNIMPLEMENTED_INTRINSIC(Name) \ @@ -1615,7 +1678,6 @@ UNIMPLEMENTED_INTRINSIC(LongBitCount) UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) UNIMPLEMENTED_INTRINSIC(SystemArrayCopy) UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) -UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck) UNIMPLEMENTED_INTRINSIC(FloatIsInfinite) UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite) @@ -1626,6 +1688,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) // Handled as HIR instructions. +UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits) +UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits) UNIMPLEMENTED_INTRINSIC(FloatIsNaN) UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft) diff --git a/compiler/optimizing/intrinsics_list.h b/compiler/optimizing/intrinsics_list.h index 88217b308e..e1aea924cf 100644 --- a/compiler/optimizing/intrinsics_list.h +++ b/compiler/optimizing/intrinsics_list.h @@ -23,10 +23,12 @@ #define INTRINSICS_LIST(V) \ V(DoubleDoubleToRawLongBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ + V(DoubleDoubleToLongBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(DoubleIsInfinite, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(DoubleIsNaN, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(DoubleLongBitsToDouble, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(FloatFloatToRawIntBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ + V(FloatFloatToIntBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(FloatIsInfinite, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(FloatIsNaN, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(FloatIntBitsToFloat, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index 2f183c3a62..a737d8100a 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -1772,6 +1772,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) // Handled as HIR instructions. +UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits) +UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits) UNIMPLEMENTED_INTRINSIC(FloatIsNaN) UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) UNIMPLEMENTED_INTRINSIC(IntegerCompare) diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index bd4f5329da..ca2652b74a 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -1824,6 +1824,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) // Handled as HIR instructions. +UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits) +UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits) UNIMPLEMENTED_INTRINSIC(FloatIsNaN) UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) UNIMPLEMENTED_INTRINSIC(IntegerCompare) diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index 260a8773fb..0df4553f56 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -2642,6 +2642,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) // Handled as HIR instructions. +UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits) +UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits) UNIMPLEMENTED_INTRINSIC(FloatIsNaN) UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft) diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 93e8c00e5a..2a9e684d11 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -2719,6 +2719,8 @@ UNIMPLEMENTED_INTRINSIC(FloatIsInfinite) UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite) // Handled as HIR instructions. +UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits) +UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits) UNIMPLEMENTED_INTRINSIC(FloatIsNaN) UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft) diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index f9acb089ee..27a5b97f5f 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -2206,7 +2206,8 @@ void HInstruction::SetReferenceTypeInfo(ReferenceTypeInfo rti) { CheckAgainstUpperBound(rti, AsBoundType()->GetUpperBound()); } } - reference_type_info_ = rti; + reference_type_handle_ = rti.GetTypeHandle(); + SetPackedFlag<kFlagReferenceTypeIsExact>(rti.IsExact()); } void HBoundType::SetUpperBound(const ReferenceTypeInfo& upper_bound, bool can_be_null) { @@ -2217,17 +2218,15 @@ void HBoundType::SetUpperBound(const ReferenceTypeInfo& upper_bound, bool can_be CheckAgainstUpperBound(GetReferenceTypeInfo(), upper_bound); } upper_bound_ = upper_bound; - upper_can_be_null_ = can_be_null; + SetPackedFlag<kFlagUpperCanBeNull>(can_be_null); } -ReferenceTypeInfo::ReferenceTypeInfo() : type_handle_(TypeHandle()), is_exact_(false) {} - -ReferenceTypeInfo::ReferenceTypeInfo(TypeHandle type_handle, bool is_exact) - : type_handle_(type_handle), is_exact_(is_exact) { +ReferenceTypeInfo ReferenceTypeInfo::Create(TypeHandle type_handle, bool is_exact) { if (kIsDebugBuild) { ScopedObjectAccess soa(Thread::Current()); DCHECK(IsValidHandle(type_handle)); } + return ReferenceTypeInfo(type_handle, is_exact); } std::ostream& operator<<(std::ostream& os, const ReferenceTypeInfo& rhs) { diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index b355883a72..9eddfc7e0e 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -154,8 +154,9 @@ class ReferenceTypeInfo : ValueObject { public: typedef Handle<mirror::Class> TypeHandle; - static ReferenceTypeInfo Create(TypeHandle type_handle, bool is_exact) { - // The constructor will check that the type_handle is valid. + static ReferenceTypeInfo Create(TypeHandle type_handle, bool is_exact); + + static ReferenceTypeInfo CreateUnchecked(TypeHandle type_handle, bool is_exact) { return ReferenceTypeInfo(type_handle, is_exact); } @@ -254,8 +255,9 @@ class ReferenceTypeInfo : ValueObject { } private: - ReferenceTypeInfo(); - ReferenceTypeInfo(TypeHandle type_handle, bool is_exact); + ReferenceTypeInfo() : type_handle_(TypeHandle()), is_exact_(false) {} + ReferenceTypeInfo(TypeHandle type_handle, bool is_exact) + : type_handle_(type_handle), is_exact_(is_exact) { } // The class of the object. TypeHandle type_handle_; @@ -1247,6 +1249,16 @@ class HLoopInformationOutwardIterator : public ValueObject { M(UShr, BinaryOperation) \ M(Xor, BinaryOperation) \ +/* + * Instructions, shared across several (not all) architectures. + */ +#if !defined(ART_ENABLE_CODEGEN_arm) && !defined(ART_ENABLE_CODEGEN_arm64) +#define FOR_EACH_CONCRETE_INSTRUCTION_SHARED(M) +#else +#define FOR_EACH_CONCRETE_INSTRUCTION_SHARED(M) \ + M(MultiplyAccumulate, Instruction) +#endif + #ifndef ART_ENABLE_CODEGEN_arm #define FOR_EACH_CONCRETE_INSTRUCTION_ARM(M) #else @@ -1258,9 +1270,9 @@ class HLoopInformationOutwardIterator : public ValueObject { #define FOR_EACH_CONCRETE_INSTRUCTION_ARM64(M) #else #define FOR_EACH_CONCRETE_INSTRUCTION_ARM64(M) \ + M(Arm64BitwiseNegatedRight, Instruction) \ M(Arm64DataProcWithShifterOp, Instruction) \ - M(Arm64IntermediateAddress, Instruction) \ - M(Arm64MultiplyAccumulate, Instruction) + M(Arm64IntermediateAddress, Instruction) #endif #define FOR_EACH_CONCRETE_INSTRUCTION_MIPS(M) @@ -1281,6 +1293,7 @@ class HLoopInformationOutwardIterator : public ValueObject { #define FOR_EACH_CONCRETE_INSTRUCTION(M) \ FOR_EACH_CONCRETE_INSTRUCTION_COMMON(M) \ + FOR_EACH_CONCRETE_INSTRUCTION_SHARED(M) \ FOR_EACH_CONCRETE_INSTRUCTION_ARM(M) \ FOR_EACH_CONCRETE_INSTRUCTION_ARM64(M) \ FOR_EACH_CONCRETE_INSTRUCTION_MIPS(M) \ @@ -1837,13 +1850,15 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> { dex_pc_(dex_pc), id_(-1), ssa_index_(-1), - emitted_at_use_site_(false), + packed_fields_(0u), environment_(nullptr), locations_(nullptr), live_interval_(nullptr), lifetime_position_(kNoLifetime), side_effects_(side_effects), - reference_type_info_(ReferenceTypeInfo::CreateInvalid()) {} + reference_type_handle_(ReferenceTypeInfo::CreateInvalid().GetTypeHandle()) { + SetPackedFlag<kFlagReferenceTypeIsExact>(ReferenceTypeInfo::CreateInvalid().IsExact()); + } virtual ~HInstruction() {} @@ -1912,7 +1927,8 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> { ReferenceTypeInfo GetReferenceTypeInfo() const { DCHECK_EQ(GetType(), Primitive::kPrimNot); - return reference_type_info_; + return ReferenceTypeInfo::CreateUnchecked(reference_type_handle_, + GetPackedFlag<kFlagReferenceTypeIsExact>());; } void AddUseAt(HInstruction* user, size_t index) { @@ -2101,13 +2117,45 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> { // The caller must ensure that this is safe to do. void RemoveEnvironmentUsers(); - bool IsEmittedAtUseSite() const { return emitted_at_use_site_; } - void MarkEmittedAtUseSite() { emitted_at_use_site_ = true; } + bool IsEmittedAtUseSite() const { return GetPackedFlag<kFlagEmittedAtUseSite>(); } + void MarkEmittedAtUseSite() { SetPackedFlag<kFlagEmittedAtUseSite>(true); } protected: + // If set, the machine code for this instruction is assumed to be generated by + // its users. Used by liveness analysis to compute use positions accordingly. + static constexpr size_t kFlagEmittedAtUseSite = 0u; + static constexpr size_t kFlagReferenceTypeIsExact = kFlagEmittedAtUseSite + 1; + static constexpr size_t kNumberOfGenericPackedBits = kFlagReferenceTypeIsExact + 1; + static constexpr size_t kMaxNumberOfPackedBits = sizeof(uint32_t) * kBitsPerByte; + virtual const HUserRecord<HInstruction*> InputRecordAt(size_t i) const = 0; virtual void SetRawInputRecordAt(size_t index, const HUserRecord<HInstruction*>& input) = 0; + uint32_t GetPackedFields() const { + return packed_fields_; + } + + template <size_t flag> + bool GetPackedFlag() const { + return (packed_fields_ & (1u << flag)) != 0u; + } + + template <size_t flag> + void SetPackedFlag(bool value = true) { + packed_fields_ = (packed_fields_ & ~(1u << flag)) | ((value ? 1u : 0u) << flag); + } + + template <typename BitFieldType> + typename BitFieldType::value_type GetPackedField() const { + return BitFieldType::Decode(packed_fields_); + } + + template <typename BitFieldType> + void SetPackedField(typename BitFieldType::value_type value) { + DCHECK(IsUint<BitFieldType::size>(static_cast<uintptr_t>(value))); + packed_fields_ = BitFieldType::Update(value, packed_fields_); + } + private: void RemoveEnvironmentUser(HUseListNode<HEnvironment*>* use_node) { env_uses_.Remove(use_node); } @@ -2124,9 +2172,8 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> { // When doing liveness analysis, instructions that have uses get an SSA index. int ssa_index_; - // If set, the machine code for this instruction is assumed to be generated by - // its users. Used by liveness analysis to compute use positions accordingly. - bool emitted_at_use_site_; + // Packed fields. + uint32_t packed_fields_; // List of instructions that have this instruction as input. HUseList<HInstruction*> uses_; @@ -2150,8 +2197,10 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> { SideEffects side_effects_; + // The reference handle part of the reference type info. + // The IsExact() flag is stored in packed fields. // TODO: for primitive types this should be marked as invalid. - ReferenceTypeInfo reference_type_info_; + ReferenceTypeInfo::TypeHandle reference_type_handle_; friend class GraphChecker; friend class HBasicBlock; @@ -2277,13 +2326,23 @@ template<intptr_t N> class HExpression : public HTemplateInstruction<N> { public: HExpression<N>(Primitive::Type type, SideEffects side_effects, uint32_t dex_pc) - : HTemplateInstruction<N>(side_effects, dex_pc), type_(type) {} + : HTemplateInstruction<N>(side_effects, dex_pc) { + this->template SetPackedField<TypeField>(type); + } virtual ~HExpression() {} - Primitive::Type GetType() const OVERRIDE { return type_; } + Primitive::Type GetType() const OVERRIDE { + return TypeField::Decode(this->GetPackedFields()); + } protected: - Primitive::Type type_; + static constexpr size_t kFieldType = HInstruction::kNumberOfGenericPackedBits; + static constexpr size_t kFieldTypeSize = + MinimumBitsToStore(static_cast<size_t>(Primitive::kPrimLast)); + static constexpr size_t kNumberOfExpressionPackedBits = kFieldType + kFieldTypeSize; + static_assert(kNumberOfExpressionPackedBits <= HInstruction::kMaxNumberOfPackedBits, + "Too many packed fields."); + using TypeField = BitField<Primitive::Type, kFieldType, kFieldTypeSize>; }; // Represents dex's RETURN_VOID opcode. A HReturnVoid is a control flow @@ -2573,13 +2632,16 @@ class HIf : public HTemplateInstruction<1> { // higher indices in no particular order. class HTryBoundary : public HTemplateInstruction<0> { public: - enum BoundaryKind { + enum class BoundaryKind { kEntry, kExit, + kLast = kExit }; explicit HTryBoundary(BoundaryKind kind, uint32_t dex_pc = kNoDexPc) - : HTemplateInstruction(SideEffects::None(), dex_pc), kind_(kind) {} + : HTemplateInstruction(SideEffects::None(), dex_pc) { + SetPackedField<BoundaryKindField>(kind); + } bool IsControlFlow() const OVERRIDE { return true; } @@ -2605,14 +2667,22 @@ class HTryBoundary : public HTemplateInstruction<0> { } } - bool IsEntry() const { return kind_ == BoundaryKind::kEntry; } + BoundaryKind GetBoundaryKind() const { return GetPackedField<BoundaryKindField>(); } + bool IsEntry() const { return GetBoundaryKind() == BoundaryKind::kEntry; } bool HasSameExceptionHandlersAs(const HTryBoundary& other) const; DECLARE_INSTRUCTION(TryBoundary); private: - const BoundaryKind kind_; + static constexpr size_t kFieldBoundaryKind = kNumberOfGenericPackedBits; + static constexpr size_t kFieldBoundaryKindSize = + MinimumBitsToStore(static_cast<size_t>(BoundaryKind::kLast)); + static constexpr size_t kNumberOfTryBoundaryPackedBits = + kFieldBoundaryKind + kFieldBoundaryKindSize; + static_assert(kNumberOfTryBoundaryPackedBits <= kMaxNumberOfPackedBits, + "Too many packed fields."); + using BoundaryKindField = BitField<BoundaryKind, kFieldBoundaryKind, kFieldBoundaryKindSize>; DISALLOW_COPY_AND_ASSIGN(HTryBoundary); }; @@ -2658,9 +2728,10 @@ class HCurrentMethod : public HExpression<0> { // of a class. class HClassTableGet : public HExpression<1> { public: - enum TableKind { + enum class TableKind { kVTable, kIMTable, + kLast = kIMTable }; HClassTableGet(HInstruction* cls, Primitive::Type type, @@ -2668,26 +2739,33 @@ class HClassTableGet : public HExpression<1> { size_t index, uint32_t dex_pc) : HExpression(type, SideEffects::None(), dex_pc), - index_(index), - table_kind_(kind) { + index_(index) { + SetPackedField<TableKindField>(kind); SetRawInputAt(0, cls); } bool CanBeMoved() const OVERRIDE { return true; } bool InstructionDataEquals(HInstruction* other) const OVERRIDE { return other->AsClassTableGet()->GetIndex() == index_ && - other->AsClassTableGet()->GetTableKind() == table_kind_; + other->AsClassTableGet()->GetPackedFields() == GetPackedFields(); } - TableKind GetTableKind() const { return table_kind_; } + TableKind GetTableKind() const { return GetPackedField<TableKindField>(); } size_t GetIndex() const { return index_; } DECLARE_INSTRUCTION(ClassTableGet); private: + static constexpr size_t kFieldTableKind = kNumberOfExpressionPackedBits; + static constexpr size_t kFieldTableKindSize = + MinimumBitsToStore(static_cast<size_t>(TableKind::kLast)); + static constexpr size_t kNumberOfClassTableGetPackedBits = kFieldTableKind + kFieldTableKindSize; + static_assert(kNumberOfClassTableGetPackedBits <= kMaxNumberOfPackedBits, + "Too many packed fields."); + using TableKindField = BitField<TableKind, kFieldTableKind, kFieldTableKind>; + // The index of the ArtMethod in the table. const size_t index_; - const TableKind table_kind_; DISALLOW_COPY_AND_ASSIGN(HClassTableGet); }; @@ -2854,6 +2932,7 @@ enum class ComparisonBias { kNoBias, // bias is not applicable (i.e. for long operation) kGtBias, // return 1 for NaN comparisons kLtBias, // return -1 for NaN comparisons + kLast = kLtBias }; std::ostream& operator<<(std::ostream& os, const ComparisonBias& rhs); @@ -2861,8 +2940,9 @@ std::ostream& operator<<(std::ostream& os, const ComparisonBias& rhs); class HCondition : public HBinaryOperation { public: HCondition(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc) - : HBinaryOperation(Primitive::kPrimBoolean, first, second, SideEffects::None(), dex_pc), - bias_(ComparisonBias::kNoBias) {} + : HBinaryOperation(Primitive::kPrimBoolean, first, second, SideEffects::None(), dex_pc) { + SetPackedField<ComparisonBiasField>(ComparisonBias::kNoBias); + } // For code generation purposes, returns whether this instruction is just before // `instruction`, and disregard moves in between. @@ -2874,12 +2954,12 @@ class HCondition : public HBinaryOperation { virtual IfCondition GetOppositeCondition() const = 0; - bool IsGtBias() const { return bias_ == ComparisonBias::kGtBias; } - ComparisonBias GetBias() const { return bias_; } - void SetBias(ComparisonBias bias) { bias_ = bias; } + bool IsGtBias() const { return GetBias() == ComparisonBias::kGtBias; } + ComparisonBias GetBias() const { return GetPackedField<ComparisonBiasField>(); } + void SetBias(ComparisonBias bias) { SetPackedField<ComparisonBiasField>(bias); } bool InstructionDataEquals(HInstruction* other) const OVERRIDE { - return bias_ == other->AsCondition()->bias_; + return GetPackedFields() == other->AsCondition()->GetPackedFields(); } bool IsFPConditionTrueIfNaN() const { @@ -2895,6 +2975,16 @@ class HCondition : public HBinaryOperation { } protected: + // Needed if we merge a HCompare into a HCondition. + static constexpr size_t kFieldComparisonBias = kNumberOfExpressionPackedBits; + static constexpr size_t kFieldComparisonBiasSize = + MinimumBitsToStore(static_cast<size_t>(ComparisonBias::kLast)); + static constexpr size_t kNumberOfConditionPackedBits = + kFieldComparisonBias + kFieldComparisonBiasSize; + static_assert(kNumberOfConditionPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + using ComparisonBiasField = + BitField<ComparisonBias, kFieldComparisonBias, kFieldComparisonBiasSize>; + template <typename T> int32_t Compare(T x, T y) const { return x > y ? 1 : (x < y ? -1 : 0); } @@ -2912,9 +3002,6 @@ class HCondition : public HBinaryOperation { } private: - // Needed if we merge a HCompare into a HCondition. - ComparisonBias bias_; - DISALLOW_COPY_AND_ASSIGN(HCondition); }; @@ -3327,8 +3414,8 @@ class HCompare : public HBinaryOperation { first, second, SideEffectsForArchRuntimeCalls(type), - dex_pc), - bias_(bias) { + dex_pc) { + SetPackedField<ComparisonBiasField>(bias); DCHECK_EQ(type, first->GetType()); DCHECK_EQ(type, second->GetType()); } @@ -3363,16 +3450,16 @@ class HCompare : public HBinaryOperation { } bool InstructionDataEquals(HInstruction* other) const OVERRIDE { - return bias_ == other->AsCompare()->bias_; + return GetPackedFields() == other->AsCompare()->GetPackedFields(); } - ComparisonBias GetBias() const { return bias_; } + ComparisonBias GetBias() const { return GetPackedField<ComparisonBiasField>(); } // Does this compare instruction have a "gt bias" (vs an "lt bias")? - // Only meaninfgul for floating-point comparisons. + // Only meaningful for floating-point comparisons. bool IsGtBias() const { DCHECK(Primitive::IsFloatingPointType(InputAt(0)->GetType())) << InputAt(0)->GetType(); - return bias_ == ComparisonBias::kGtBias; + return GetBias() == ComparisonBias::kGtBias; } static SideEffects SideEffectsForArchRuntimeCalls(Primitive::Type type) { @@ -3383,6 +3470,15 @@ class HCompare : public HBinaryOperation { DECLARE_INSTRUCTION(Compare); protected: + static constexpr size_t kFieldComparisonBias = kNumberOfExpressionPackedBits; + static constexpr size_t kFieldComparisonBiasSize = + MinimumBitsToStore(static_cast<size_t>(ComparisonBias::kLast)); + static constexpr size_t kNumberOfComparePackedBits = + kFieldComparisonBias + kFieldComparisonBiasSize; + static_assert(kNumberOfComparePackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + using ComparisonBiasField = + BitField<ComparisonBias, kFieldComparisonBias, kFieldComparisonBiasSize>; + // Return an integer constant containing the result of a comparison evaluated at compile time. HIntConstant* MakeConstantComparison(int32_t value, uint32_t dex_pc) const { DCHECK(value == -1 || value == 0 || value == 1) << value; @@ -3390,8 +3486,6 @@ class HCompare : public HBinaryOperation { } private: - const ComparisonBias bias_; - DISALLOW_COPY_AND_ASSIGN(HCompare); }; @@ -3459,9 +3553,9 @@ class HNewInstance : public HExpression<2> { : HExpression(Primitive::kPrimNot, SideEffects::CanTriggerGC(), dex_pc), type_index_(type_index), dex_file_(dex_file), - can_throw_(can_throw), - finalizable_(finalizable), entrypoint_(entrypoint) { + SetPackedFlag<kFlagCanThrow>(can_throw); + SetPackedFlag<kFlagFinalizable>(finalizable); SetRawInputAt(0, cls); SetRawInputAt(1, current_method); } @@ -3475,9 +3569,9 @@ class HNewInstance : public HExpression<2> { // It may throw when called on type that's not instantiable/accessible. // It can throw OOME. // TODO: distinguish between the two cases so we can for example allow allocation elimination. - bool CanThrow() const OVERRIDE { return can_throw_ || true; } + bool CanThrow() const OVERRIDE { return GetPackedFlag<kFlagCanThrow>() || true; } - bool IsFinalizable() const { return finalizable_; } + bool IsFinalizable() const { return GetPackedFlag<kFlagFinalizable>(); } bool CanBeNull() const OVERRIDE { return false; } @@ -3492,10 +3586,14 @@ class HNewInstance : public HExpression<2> { DECLARE_INSTRUCTION(NewInstance); private: + static constexpr size_t kFlagCanThrow = kNumberOfExpressionPackedBits; + static constexpr size_t kFlagFinalizable = kFlagCanThrow + 1; + static constexpr size_t kNumberOfNewInstancePackedBits = kFlagFinalizable + 1; + static_assert(kNumberOfNewInstancePackedBits <= kMaxNumberOfPackedBits, + "Too many packed fields."); + const uint16_t type_index_; const DexFile& dex_file_; - const bool can_throw_; - const bool finalizable_; QuickEntrypointEnum entrypoint_; DISALLOW_COPY_AND_ASSIGN(HNewInstance); @@ -3545,12 +3643,14 @@ class HInvoke : public HInstruction { // inputs at the end of their list of inputs. uint32_t GetNumberOfArguments() const { return number_of_arguments_; } - Primitive::Type GetType() const OVERRIDE { return return_type_; } + Primitive::Type GetType() const OVERRIDE { return GetPackedField<ReturnTypeField>(); } uint32_t GetDexMethodIndex() const { return dex_method_index_; } const DexFile& GetDexFile() const { return GetEnvironment()->GetDexFile(); } - InvokeType GetOriginalInvokeType() const { return original_invoke_type_; } + InvokeType GetOriginalInvokeType() const { + return GetPackedField<OriginalInvokeTypeField>(); + } Intrinsics GetIntrinsic() const { return intrinsic_; @@ -3565,7 +3665,7 @@ class HInvoke : public HInstruction { return GetEnvironment()->IsFromInlinedInvoke(); } - bool CanThrow() const OVERRIDE { return can_throw_; } + bool CanThrow() const OVERRIDE { return GetPackedFlag<kFlagCanThrow>(); } bool CanBeMoved() const OVERRIDE { return IsIntrinsic(); } @@ -3586,6 +3686,20 @@ class HInvoke : public HInstruction { DECLARE_ABSTRACT_INSTRUCTION(Invoke); protected: + static constexpr size_t kFieldOriginalInvokeType = kNumberOfGenericPackedBits; + static constexpr size_t kFieldOriginalInvokeTypeSize = + MinimumBitsToStore(static_cast<size_t>(kMaxInvokeType)); + static constexpr size_t kFieldReturnType = + kFieldOriginalInvokeType + kFieldOriginalInvokeTypeSize; + static constexpr size_t kFieldReturnTypeSize = + MinimumBitsToStore(static_cast<size_t>(Primitive::kPrimLast)); + static constexpr size_t kFlagCanThrow = kFieldReturnType + kFieldReturnTypeSize; + static constexpr size_t kNumberOfInvokePackedBits = kFlagCanThrow + 1; + static_assert(kNumberOfInvokePackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + using OriginalInvokeTypeField = + BitField<InvokeType, kFieldOriginalInvokeType, kFieldOriginalInvokeTypeSize>; + using ReturnTypeField = BitField<Primitive::Type, kFieldReturnType, kFieldReturnTypeSize>; + HInvoke(ArenaAllocator* arena, uint32_t number_of_arguments, uint32_t number_of_other_inputs, @@ -3598,12 +3712,12 @@ class HInvoke : public HInstruction { number_of_arguments_(number_of_arguments), inputs_(number_of_arguments + number_of_other_inputs, arena->Adapter(kArenaAllocInvokeInputs)), - return_type_(return_type), dex_method_index_(dex_method_index), - original_invoke_type_(original_invoke_type), - can_throw_(true), intrinsic_(Intrinsics::kNone), intrinsic_optimizations_(0) { + SetPackedField<ReturnTypeField>(return_type); + SetPackedField<OriginalInvokeTypeField>(original_invoke_type); + SetPackedFlag<kFlagCanThrow>(true); } const HUserRecord<HInstruction*> InputRecordAt(size_t index) const OVERRIDE { @@ -3614,14 +3728,11 @@ class HInvoke : public HInstruction { inputs_[index] = input; } - void SetCanThrow(bool can_throw) { can_throw_ = can_throw; } + void SetCanThrow(bool can_throw) { SetPackedFlag<kFlagCanThrow>(can_throw); } uint32_t number_of_arguments_; ArenaVector<HUserRecord<HInstruction*>> inputs_; - const Primitive::Type return_type_; const uint32_t dex_method_index_; - const InvokeType original_invoke_type_; - bool can_throw_; Intrinsics intrinsic_; // A magic word holding optimizations for intrinsics. See intrinsics.h. @@ -3662,6 +3773,7 @@ class HInvokeStaticOrDirect : public HInvoke { kNone, // Class already initialized. kExplicit, // Static call having explicit clinit check as last input. kImplicit, // Static call implicitly requiring a clinit check. + kLast = kImplicit }; // Determines how to load the target ArtMethod*. @@ -3682,7 +3794,7 @@ class HInvokeStaticOrDirect : public HInvoke { // the image relocatable or not. kDirectAddressWithFixup, - // Load from resoved methods array in the dex cache using a PC-relative load. + // Load from resolved methods array in the dex cache using a PC-relative load. // Used when we need to use the dex cache, for example for invoke-static that // may cause class initialization (the entry may point to a resolution method), // and we know that we can access the dex cache arrays using a PC-relative load. @@ -3754,10 +3866,11 @@ class HInvokeStaticOrDirect : public HInvoke { dex_pc, method_index, original_invoke_type), - optimized_invoke_type_(optimized_invoke_type), - clinit_check_requirement_(clinit_check_requirement), target_method_(target_method), - dispatch_info_(dispatch_info) { } + dispatch_info_(dispatch_info) { + SetPackedField<OptimizedInvokeTypeField>(optimized_invoke_type); + SetPackedField<ClinitCheckRequirementField>(clinit_check_requirement); + } void SetDispatchInfo(const DispatchInfo& dispatch_info) { bool had_current_method_input = HasCurrentMethodInput(); @@ -3789,7 +3902,7 @@ class HInvokeStaticOrDirect : public HInvoke { } bool CanBeNull() const OVERRIDE { - return return_type_ == Primitive::kPrimNot && !IsStringInit(); + return GetPackedField<ReturnTypeField>() == Primitive::kPrimNot && !IsStringInit(); } // Get the index of the special input, if any. @@ -3800,9 +3913,12 @@ class HInvokeStaticOrDirect : public HInvoke { uint32_t GetSpecialInputIndex() const { return GetNumberOfArguments(); } bool HasSpecialInput() const { return GetNumberOfArguments() != InputCount(); } - InvokeType GetOptimizedInvokeType() const { return optimized_invoke_type_; } + InvokeType GetOptimizedInvokeType() const { + return GetPackedField<OptimizedInvokeTypeField>(); + } + void SetOptimizedInvokeType(InvokeType invoke_type) { - optimized_invoke_type_ = invoke_type; + SetPackedField<OptimizedInvokeTypeField>(invoke_type); } MethodLoadKind GetMethodLoadKind() const { return dispatch_info_.method_load_kind; } @@ -3849,7 +3965,9 @@ class HInvokeStaticOrDirect : public HInvoke { return dispatch_info_.direct_code_ptr; } - ClinitCheckRequirement GetClinitCheckRequirement() const { return clinit_check_requirement_; } + ClinitCheckRequirement GetClinitCheckRequirement() const { + return GetPackedField<ClinitCheckRequirementField>(); + } // Is this instruction a call to a static method? bool IsStatic() const { @@ -3867,7 +3985,7 @@ class HInvokeStaticOrDirect : public HInvoke { DCHECK(last_input->IsLoadClass() || last_input->IsClinitCheck()) << last_input->DebugName(); RemoveAsUserOfInput(last_input_index); inputs_.pop_back(); - clinit_check_requirement_ = new_requirement; + SetPackedField<ClinitCheckRequirementField>(new_requirement); DCHECK(!IsStaticWithExplicitClinitCheck()); } @@ -3883,13 +4001,13 @@ class HInvokeStaticOrDirect : public HInvoke { // Is this a call to a static method whose declaring class has an // explicit initialization check in the graph? bool IsStaticWithExplicitClinitCheck() const { - return IsStatic() && (clinit_check_requirement_ == ClinitCheckRequirement::kExplicit); + return IsStatic() && (GetClinitCheckRequirement() == ClinitCheckRequirement::kExplicit); } // Is this a call to a static method whose declaring class has an // implicit intialization check requirement? bool IsStaticWithImplicitClinitCheck() const { - return IsStatic() && (clinit_check_requirement_ == ClinitCheckRequirement::kImplicit); + return IsStatic() && (GetClinitCheckRequirement() == ClinitCheckRequirement::kImplicit); } // Does this method load kind need the current method as an input? @@ -3918,8 +4036,23 @@ class HInvokeStaticOrDirect : public HInvoke { void RemoveInputAt(size_t index); private: - InvokeType optimized_invoke_type_; - ClinitCheckRequirement clinit_check_requirement_; + static constexpr size_t kFieldOptimizedInvokeType = kNumberOfInvokePackedBits; + static constexpr size_t kFieldOptimizedInvokeTypeSize = + MinimumBitsToStore(static_cast<size_t>(kMaxInvokeType)); + static constexpr size_t kFieldClinitCheckRequirement = + kFieldOptimizedInvokeType + kFieldOptimizedInvokeTypeSize; + static constexpr size_t kFieldClinitCheckRequirementSize = + MinimumBitsToStore(static_cast<size_t>(ClinitCheckRequirement::kLast)); + static constexpr size_t kNumberOfInvokeStaticOrDirectPackedBits = + kFieldClinitCheckRequirement + kFieldClinitCheckRequirementSize; + static_assert(kNumberOfInvokeStaticOrDirectPackedBits <= kMaxNumberOfPackedBits, + "Too many packed fields."); + using OptimizedInvokeTypeField = + BitField<InvokeType, kFieldOptimizedInvokeType, kFieldOptimizedInvokeTypeSize>; + using ClinitCheckRequirementField = BitField<ClinitCheckRequirement, + kFieldClinitCheckRequirement, + kFieldClinitCheckRequirementSize>; + // The target method may refer to different dex file or method index than the original // invoke. This happens for sharpened calls and for calls where a method was redeclared // in derived class to increase visibility. @@ -4585,32 +4718,35 @@ class HParameterValue : public HExpression<0> { : HExpression(parameter_type, SideEffects::None(), kNoDexPc), dex_file_(dex_file), type_index_(type_index), - index_(index), - is_this_(is_this), - can_be_null_(!is_this) {} + index_(index) { + SetPackedFlag<kFlagIsThis>(is_this); + SetPackedFlag<kFlagCanBeNull>(!is_this); + } const DexFile& GetDexFile() const { return dex_file_; } uint16_t GetTypeIndex() const { return type_index_; } uint8_t GetIndex() const { return index_; } - bool IsThis() const { return is_this_; } + bool IsThis() const { return GetPackedFlag<kFlagIsThis>(); } - bool CanBeNull() const OVERRIDE { return can_be_null_; } - void SetCanBeNull(bool can_be_null) { can_be_null_ = can_be_null; } + bool CanBeNull() const OVERRIDE { return GetPackedFlag<kFlagCanBeNull>(); } + void SetCanBeNull(bool can_be_null) { SetPackedFlag<kFlagCanBeNull>(can_be_null); } DECLARE_INSTRUCTION(ParameterValue); private: + // Whether or not the parameter value corresponds to 'this' argument. + static constexpr size_t kFlagIsThis = kNumberOfExpressionPackedBits; + static constexpr size_t kFlagCanBeNull = kFlagIsThis + 1; + static constexpr size_t kNumberOfParameterValuePackedBits = kFlagCanBeNull + 1; + static_assert(kNumberOfParameterValuePackedBits <= kMaxNumberOfPackedBits, + "Too many packed fields."); + const DexFile& dex_file_; const uint16_t type_index_; // The index of this parameter in the parameters list. Must be less // than HGraph::number_of_in_vregs_. const uint8_t index_; - // Whether or not the parameter value corresponds to 'this' argument. - const bool is_this_; - - bool can_be_null_; - DISALLOW_COPY_AND_ASSIGN(HParameterValue); }; @@ -4735,14 +4871,14 @@ class HPhi : public HInstruction { uint32_t dex_pc = kNoDexPc) : HInstruction(SideEffects::None(), dex_pc), inputs_(number_of_inputs, arena->Adapter(kArenaAllocPhiInputs)), - reg_number_(reg_number), - type_(ToPhiType(type)), - // Phis are constructed live and marked dead if conflicting or unused. - // Individual steps of SsaBuilder should assume that if a phi has been - // marked dead, it can be ignored and will be removed by SsaPhiElimination. - is_live_(true), - can_be_null_(true) { - DCHECK_NE(type_, Primitive::kPrimVoid); + reg_number_(reg_number) { + SetPackedField<TypeField>(ToPhiType(type)); + DCHECK_NE(GetType(), Primitive::kPrimVoid); + // Phis are constructed live and marked dead if conflicting or unused. + // Individual steps of SsaBuilder should assume that if a phi has been + // marked dead, it can be ignored and will be removed by SsaPhiElimination. + SetPackedFlag<kFlagIsLive>(true); + SetPackedFlag<kFlagCanBeNull>(true); } // Returns a type equivalent to the given `type`, but that a `HPhi` can hold. @@ -4765,27 +4901,27 @@ class HPhi : public HInstruction { void AddInput(HInstruction* input); void RemoveInputAt(size_t index); - Primitive::Type GetType() const OVERRIDE { return type_; } + Primitive::Type GetType() const OVERRIDE { return GetPackedField<TypeField>(); } void SetType(Primitive::Type new_type) { // Make sure that only valid type changes occur. The following are allowed: // (1) int -> float/ref (primitive type propagation), // (2) long -> double (primitive type propagation). - DCHECK(type_ == new_type || - (type_ == Primitive::kPrimInt && new_type == Primitive::kPrimFloat) || - (type_ == Primitive::kPrimInt && new_type == Primitive::kPrimNot) || - (type_ == Primitive::kPrimLong && new_type == Primitive::kPrimDouble)); - type_ = new_type; + DCHECK(GetType() == new_type || + (GetType() == Primitive::kPrimInt && new_type == Primitive::kPrimFloat) || + (GetType() == Primitive::kPrimInt && new_type == Primitive::kPrimNot) || + (GetType() == Primitive::kPrimLong && new_type == Primitive::kPrimDouble)); + SetPackedField<TypeField>(new_type); } - bool CanBeNull() const OVERRIDE { return can_be_null_; } - void SetCanBeNull(bool can_be_null) { can_be_null_ = can_be_null; } + bool CanBeNull() const OVERRIDE { return GetPackedFlag<kFlagCanBeNull>(); } + void SetCanBeNull(bool can_be_null) { SetPackedFlag<kFlagCanBeNull>(can_be_null); } uint32_t GetRegNumber() const { return reg_number_; } - void SetDead() { is_live_ = false; } - void SetLive() { is_live_ = true; } - bool IsDead() const { return !is_live_; } - bool IsLive() const { return is_live_; } + void SetDead() { SetPackedFlag<kFlagIsLive>(false); } + void SetLive() { SetPackedFlag<kFlagIsLive>(true); } + bool IsDead() const { return !IsLive(); } + bool IsLive() const { return GetPackedFlag<kFlagIsLive>(); } bool IsVRegEquivalentOf(HInstruction* other) const { return other != nullptr @@ -4820,11 +4956,17 @@ class HPhi : public HInstruction { } private: + static constexpr size_t kFieldType = HInstruction::kNumberOfGenericPackedBits; + static constexpr size_t kFieldTypeSize = + MinimumBitsToStore(static_cast<size_t>(Primitive::kPrimLast)); + static constexpr size_t kFlagIsLive = kFieldType + kFieldTypeSize; + static constexpr size_t kFlagCanBeNull = kFlagIsLive + 1; + static constexpr size_t kNumberOfPhiPackedBits = kFlagCanBeNull + 1; + static_assert(kNumberOfPhiPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + using TypeField = BitField<Primitive::Type, kFieldType, kFieldTypeSize>; + ArenaVector<HUserRecord<HInstruction*> > inputs_; const uint32_t reg_number_; - Primitive::Type type_; - bool is_live_; - bool can_be_null_; DISALLOW_COPY_AND_ASSIGN(HPhi); }; @@ -4963,8 +5105,8 @@ class HInstanceFieldSet : public HTemplateInstruction<2> { field_idx, declaring_class_def_index, dex_file, - dex_cache), - value_can_be_null_(true) { + dex_cache) { + SetPackedFlag<kFlagValueCanBeNull>(true); SetRawInputAt(0, object); SetRawInputAt(1, value); } @@ -4978,14 +5120,18 @@ class HInstanceFieldSet : public HTemplateInstruction<2> { Primitive::Type GetFieldType() const { return field_info_.GetFieldType(); } bool IsVolatile() const { return field_info_.IsVolatile(); } HInstruction* GetValue() const { return InputAt(1); } - bool GetValueCanBeNull() const { return value_can_be_null_; } - void ClearValueCanBeNull() { value_can_be_null_ = false; } + bool GetValueCanBeNull() const { return GetPackedFlag<kFlagValueCanBeNull>(); } + void ClearValueCanBeNull() { SetPackedFlag<kFlagValueCanBeNull>(false); } DECLARE_INSTRUCTION(InstanceFieldSet); private: + static constexpr size_t kFlagValueCanBeNull = kNumberOfGenericPackedBits; + static constexpr size_t kNumberOfInstanceFieldSetPackedBits = kFlagValueCanBeNull + 1; + static_assert(kNumberOfInstanceFieldSetPackedBits <= kMaxNumberOfPackedBits, + "Too many packed fields."); + const FieldInfo field_info_; - bool value_can_be_null_; DISALLOW_COPY_AND_ASSIGN(HInstanceFieldSet); }; @@ -5054,11 +5200,11 @@ class HArraySet : public HTemplateInstruction<3> { SideEffects::ArrayWriteOfType(expected_component_type).Union( SideEffectsForArchRuntimeCalls(value->GetType())).Union( additional_side_effects), - dex_pc), - expected_component_type_(expected_component_type), - needs_type_check_(value->GetType() == Primitive::kPrimNot), - value_can_be_null_(true), - static_type_of_array_is_object_array_(false) { + dex_pc) { + SetPackedField<ExpectedComponentTypeField>(expected_component_type); + SetPackedFlag<kFlagNeedsTypeCheck>(value->GetType() == Primitive::kPrimNot); + SetPackedFlag<kFlagValueCanBeNull>(true); + SetPackedFlag<kFlagStaticTypeOfArrayIsObjectArray>(false); SetRawInputAt(0, array); SetRawInputAt(1, index); SetRawInputAt(2, value); @@ -5066,11 +5212,11 @@ class HArraySet : public HTemplateInstruction<3> { bool NeedsEnvironment() const OVERRIDE { // We call a runtime method to throw ArrayStoreException. - return needs_type_check_; + return NeedsTypeCheck(); } // Can throw ArrayStoreException. - bool CanThrow() const OVERRIDE { return needs_type_check_; } + bool CanThrow() const OVERRIDE { return NeedsTypeCheck(); } bool CanDoImplicitNullCheckOn(HInstruction* obj ATTRIBUTE_UNUSED) const OVERRIDE { // TODO: Same as for ArrayGet. @@ -5078,20 +5224,22 @@ class HArraySet : public HTemplateInstruction<3> { } void ClearNeedsTypeCheck() { - needs_type_check_ = false; + SetPackedFlag<kFlagNeedsTypeCheck>(false); } void ClearValueCanBeNull() { - value_can_be_null_ = false; + SetPackedFlag<kFlagValueCanBeNull>(false); } void SetStaticTypeOfArrayIsObjectArray() { - static_type_of_array_is_object_array_ = true; + SetPackedFlag<kFlagStaticTypeOfArrayIsObjectArray>(true); } - bool GetValueCanBeNull() const { return value_can_be_null_; } - bool NeedsTypeCheck() const { return needs_type_check_; } - bool StaticTypeOfArrayIsObjectArray() const { return static_type_of_array_is_object_array_; } + bool GetValueCanBeNull() const { return GetPackedFlag<kFlagValueCanBeNull>(); } + bool NeedsTypeCheck() const { return GetPackedFlag<kFlagNeedsTypeCheck>(); } + bool StaticTypeOfArrayIsObjectArray() const { + return GetPackedFlag<kFlagStaticTypeOfArrayIsObjectArray>(); + } HInstruction* GetArray() const { return InputAt(0); } HInstruction* GetIndex() const { return InputAt(1); } @@ -5105,11 +5253,11 @@ class HArraySet : public HTemplateInstruction<3> { Primitive::Type value_type = GetValue()->GetType(); return ((value_type == Primitive::kPrimFloat) || (value_type == Primitive::kPrimDouble)) ? value_type - : expected_component_type_; + : GetRawExpectedComponentType(); } Primitive::Type GetRawExpectedComponentType() const { - return expected_component_type_; + return GetPackedField<ExpectedComponentTypeField>(); } static SideEffects SideEffectsForArchRuntimeCalls(Primitive::Type value_type) { @@ -5119,12 +5267,20 @@ class HArraySet : public HTemplateInstruction<3> { DECLARE_INSTRUCTION(ArraySet); private: - const Primitive::Type expected_component_type_; - bool needs_type_check_; - bool value_can_be_null_; + static constexpr size_t kFieldExpectedComponentType = kNumberOfGenericPackedBits; + static constexpr size_t kFieldExpectedComponentTypeSize = + MinimumBitsToStore(static_cast<size_t>(Primitive::kPrimLast)); + static constexpr size_t kFlagNeedsTypeCheck = + kFieldExpectedComponentType + kFieldExpectedComponentTypeSize; + static constexpr size_t kFlagValueCanBeNull = kFlagNeedsTypeCheck + 1; // Cached information for the reference_type_info_ so that codegen // does not need to inspect the static type. - bool static_type_of_array_is_object_array_; + static constexpr size_t kFlagStaticTypeOfArrayIsObjectArray = kFlagValueCanBeNull + 1; + static constexpr size_t kNumberOfArraySetPackedBits = + kFlagStaticTypeOfArrayIsObjectArray + 1; + static_assert(kNumberOfArraySetPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + using ExpectedComponentTypeField = + BitField<Primitive::Type, kFieldExpectedComponentType, kFieldExpectedComponentTypeSize>; DISALLOW_COPY_AND_ASSIGN(HArraySet); }; @@ -5234,14 +5390,15 @@ class HLoadClass : public HExpression<1> { : HExpression(Primitive::kPrimNot, SideEffectsForArchRuntimeCalls(), dex_pc), type_index_(type_index), dex_file_(dex_file), - is_referrers_class_(is_referrers_class), - generate_clinit_check_(false), - needs_access_check_(needs_access_check), - is_in_dex_cache_(is_in_dex_cache), loaded_class_rti_(ReferenceTypeInfo::CreateInvalid()) { // Referrers class should not need access check. We never inline unverified // methods so we can't possibly end up in this situation. - DCHECK(!is_referrers_class_ || !needs_access_check_); + DCHECK(!is_referrers_class || !needs_access_check); + + SetPackedFlag<kFlagIsReferrersClass>(is_referrers_class); + SetPackedFlag<kFlagNeedsAccessCheck>(needs_access_check); + SetPackedFlag<kFlagIsInDexCache>(is_in_dex_cache); + SetPackedFlag<kFlagGenerateClInitCheck>(false); SetRawInputAt(0, current_method); } @@ -5252,39 +5409,31 @@ class HLoadClass : public HExpression<1> { // Whether or not we need to generate the clinit check is processed in // prepare_for_register_allocator based on existing HInvokes and HClinitChecks. return other->AsLoadClass()->type_index_ == type_index_ && - other->AsLoadClass()->needs_access_check_ == needs_access_check_; + other->AsLoadClass()->GetPackedFields() == GetPackedFields(); } size_t ComputeHashCode() const OVERRIDE { return type_index_; } uint16_t GetTypeIndex() const { return type_index_; } - bool IsReferrersClass() const { return is_referrers_class_; } bool CanBeNull() const OVERRIDE { return false; } bool NeedsEnvironment() const OVERRIDE { return CanCallRuntime(); } - bool MustGenerateClinitCheck() const { - return generate_clinit_check_; - } - void SetMustGenerateClinitCheck(bool generate_clinit_check) { // The entrypoint the code generator is going to call does not do // clinit of the class. DCHECK(!NeedsAccessCheck()); - generate_clinit_check_ = generate_clinit_check; + SetPackedFlag<kFlagGenerateClInitCheck>(generate_clinit_check); } bool CanCallRuntime() const { return MustGenerateClinitCheck() || - (!is_referrers_class_ && !is_in_dex_cache_) || - needs_access_check_; + (!IsReferrersClass() && !IsInDexCache()) || + NeedsAccessCheck(); } - bool NeedsAccessCheck() const { - return needs_access_check_; - } bool CanThrow() const OVERRIDE { return CanCallRuntime(); @@ -5302,25 +5451,31 @@ class HLoadClass : public HExpression<1> { const DexFile& GetDexFile() { return dex_file_; } - bool NeedsDexCacheOfDeclaringClass() const OVERRIDE { return !is_referrers_class_; } + bool NeedsDexCacheOfDeclaringClass() const OVERRIDE { return !IsReferrersClass(); } static SideEffects SideEffectsForArchRuntimeCalls() { return SideEffects::CanTriggerGC(); } - bool IsInDexCache() const { return is_in_dex_cache_; } + bool IsReferrersClass() const { return GetPackedFlag<kFlagIsReferrersClass>(); } + bool NeedsAccessCheck() const { return GetPackedFlag<kFlagNeedsAccessCheck>(); } + bool IsInDexCache() const { return GetPackedFlag<kFlagIsInDexCache>(); } + bool MustGenerateClinitCheck() const { return GetPackedFlag<kFlagGenerateClInitCheck>(); } DECLARE_INSTRUCTION(LoadClass); private: - const uint16_t type_index_; - const DexFile& dex_file_; - const bool is_referrers_class_; + static constexpr size_t kFlagIsReferrersClass = kNumberOfExpressionPackedBits; + static constexpr size_t kFlagNeedsAccessCheck = kFlagIsReferrersClass + 1; + static constexpr size_t kFlagIsInDexCache = kFlagNeedsAccessCheck + 1; // Whether this instruction must generate the initialization check. // Used for code generation. - bool generate_clinit_check_; - const bool needs_access_check_; - const bool is_in_dex_cache_; + static constexpr size_t kFlagGenerateClInitCheck = kFlagIsInDexCache + 1; + static constexpr size_t kNumberOfLoadClassPackedBits = kFlagGenerateClInitCheck + 1; + static_assert(kNumberOfLoadClassPackedBits < kMaxNumberOfPackedBits, "Too many packed fields."); + + const uint16_t type_index_; + const DexFile& dex_file_; ReferenceTypeInfo loaded_class_rti_; @@ -5334,8 +5489,8 @@ class HLoadString : public HExpression<1> { uint32_t dex_pc, bool is_in_dex_cache) : HExpression(Primitive::kPrimNot, SideEffectsForArchRuntimeCalls(), dex_pc), - string_index_(string_index), - is_in_dex_cache_(is_in_dex_cache) { + string_index_(string_index) { + SetPackedFlag<kFlagIsInDexCache>(is_in_dex_cache); SetRawInputAt(0, current_method); } @@ -5354,18 +5509,22 @@ class HLoadString : public HExpression<1> { bool NeedsDexCacheOfDeclaringClass() const OVERRIDE { return true; } bool CanBeNull() const OVERRIDE { return false; } - bool IsInDexCache() const { return is_in_dex_cache_; } bool CanThrow() const OVERRIDE { return !IsInDexCache(); } static SideEffects SideEffectsForArchRuntimeCalls() { return SideEffects::CanTriggerGC(); } + bool IsInDexCache() const { return GetPackedFlag<kFlagIsInDexCache>(); } + DECLARE_INSTRUCTION(LoadString); private: + static constexpr size_t kFlagIsInDexCache = kNumberOfExpressionPackedBits; + static constexpr size_t kNumberOfLoadStringPackedBits = kFlagIsInDexCache + 1; + static_assert(kNumberOfLoadStringPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + const uint32_t string_index_; - const bool is_in_dex_cache_; DISALLOW_COPY_AND_ASSIGN(HLoadString); }; @@ -5472,8 +5631,8 @@ class HStaticFieldSet : public HTemplateInstruction<2> { field_idx, declaring_class_def_index, dex_file, - dex_cache), - value_can_be_null_(true) { + dex_cache) { + SetPackedFlag<kFlagValueCanBeNull>(true); SetRawInputAt(0, cls); SetRawInputAt(1, value); } @@ -5484,14 +5643,18 @@ class HStaticFieldSet : public HTemplateInstruction<2> { bool IsVolatile() const { return field_info_.IsVolatile(); } HInstruction* GetValue() const { return InputAt(1); } - bool GetValueCanBeNull() const { return value_can_be_null_; } - void ClearValueCanBeNull() { value_can_be_null_ = false; } + bool GetValueCanBeNull() const { return GetPackedFlag<kFlagValueCanBeNull>(); } + void ClearValueCanBeNull() { SetPackedFlag<kFlagValueCanBeNull>(false); } DECLARE_INSTRUCTION(StaticFieldSet); private: + static constexpr size_t kFlagValueCanBeNull = kNumberOfGenericPackedBits; + static constexpr size_t kNumberOfStaticFieldSetPackedBits = kFlagValueCanBeNull + 1; + static_assert(kNumberOfStaticFieldSetPackedBits <= kMaxNumberOfPackedBits, + "Too many packed fields."); + const FieldInfo field_info_; - bool value_can_be_null_; DISALLOW_COPY_AND_ASSIGN(HStaticFieldSet); }; @@ -5529,8 +5692,8 @@ class HUnresolvedInstanceFieldSet : public HTemplateInstruction<2> { uint32_t field_index, uint32_t dex_pc) : HTemplateInstruction(SideEffects::AllExceptGCDependency(), dex_pc), - field_type_(field_type), field_index_(field_index) { + SetPackedField<FieldTypeField>(field_type); DCHECK_EQ(field_type, value->GetType()); SetRawInputAt(0, obj); SetRawInputAt(1, value); @@ -5539,13 +5702,21 @@ class HUnresolvedInstanceFieldSet : public HTemplateInstruction<2> { bool NeedsEnvironment() const OVERRIDE { return true; } bool CanThrow() const OVERRIDE { return true; } - Primitive::Type GetFieldType() const { return field_type_; } + Primitive::Type GetFieldType() const { return GetPackedField<FieldTypeField>(); } uint32_t GetFieldIndex() const { return field_index_; } DECLARE_INSTRUCTION(UnresolvedInstanceFieldSet); private: - const Primitive::Type field_type_; + static constexpr size_t kFieldFieldType = HInstruction::kNumberOfGenericPackedBits; + static constexpr size_t kFieldFieldTypeSize = + MinimumBitsToStore(static_cast<size_t>(Primitive::kPrimLast)); + static constexpr size_t kNumberOfUnresolvedStaticFieldSetPackedBits = + kFieldFieldType + kFieldFieldTypeSize; + static_assert(kNumberOfUnresolvedStaticFieldSetPackedBits <= HInstruction::kMaxNumberOfPackedBits, + "Too many packed fields."); + using FieldTypeField = BitField<Primitive::Type, kFieldFieldType, kFieldFieldTypeSize>; + const uint32_t field_index_; DISALLOW_COPY_AND_ASSIGN(HUnresolvedInstanceFieldSet); @@ -5581,8 +5752,8 @@ class HUnresolvedStaticFieldSet : public HTemplateInstruction<1> { uint32_t field_index, uint32_t dex_pc) : HTemplateInstruction(SideEffects::AllExceptGCDependency(), dex_pc), - field_type_(field_type), field_index_(field_index) { + SetPackedField<FieldTypeField>(field_type); DCHECK_EQ(field_type, value->GetType()); SetRawInputAt(0, value); } @@ -5590,13 +5761,21 @@ class HUnresolvedStaticFieldSet : public HTemplateInstruction<1> { bool NeedsEnvironment() const OVERRIDE { return true; } bool CanThrow() const OVERRIDE { return true; } - Primitive::Type GetFieldType() const { return field_type_; } + Primitive::Type GetFieldType() const { return GetPackedField<FieldTypeField>(); } uint32_t GetFieldIndex() const { return field_index_; } DECLARE_INSTRUCTION(UnresolvedStaticFieldSet); private: - const Primitive::Type field_type_; + static constexpr size_t kFieldFieldType = HInstruction::kNumberOfGenericPackedBits; + static constexpr size_t kFieldFieldTypeSize = + MinimumBitsToStore(static_cast<size_t>(Primitive::kPrimLast)); + static constexpr size_t kNumberOfUnresolvedStaticFieldSetPackedBits = + kFieldFieldType + kFieldFieldTypeSize; + static_assert(kNumberOfUnresolvedStaticFieldSetPackedBits <= HInstruction::kMaxNumberOfPackedBits, + "Too many packed fields."); + using FieldTypeField = BitField<Primitive::Type, kFieldFieldType, kFieldFieldTypeSize>; + const uint32_t field_index_; DISALLOW_COPY_AND_ASSIGN(HUnresolvedStaticFieldSet); @@ -5660,7 +5839,8 @@ enum class TypeCheckKind { kAbstractClassCheck, // Can just walk the super class chain, starting one up. kInterfaceCheck, // No optimization yet when checking against an interface. kArrayObjectCheck, // Can just check if the array is not primitive. - kArrayCheck // No optimization yet when checking against a generic array. + kArrayCheck, // No optimization yet when checking against a generic array. + kLast = kArrayCheck }; std::ostream& operator<<(std::ostream& os, TypeCheckKind rhs); @@ -5673,9 +5853,9 @@ class HInstanceOf : public HExpression<2> { uint32_t dex_pc) : HExpression(Primitive::kPrimBoolean, SideEffectsForArchRuntimeCalls(check_kind), - dex_pc), - check_kind_(check_kind), - must_do_null_check_(true) { + dex_pc) { + SetPackedField<TypeCheckKindField>(check_kind); + SetPackedFlag<kFlagMustDoNullCheck>(true); SetRawInputAt(0, object); SetRawInputAt(1, constant); } @@ -5687,16 +5867,14 @@ class HInstanceOf : public HExpression<2> { } bool NeedsEnvironment() const OVERRIDE { - return CanCallRuntime(check_kind_); + return CanCallRuntime(GetTypeCheckKind()); } - bool IsExactCheck() const { return check_kind_ == TypeCheckKind::kExactCheck; } - - TypeCheckKind GetTypeCheckKind() const { return check_kind_; } - // Used only in code generation. - bool MustDoNullCheck() const { return must_do_null_check_; } - void ClearMustDoNullCheck() { must_do_null_check_ = false; } + bool MustDoNullCheck() const { return GetPackedFlag<kFlagMustDoNullCheck>(); } + void ClearMustDoNullCheck() { SetPackedFlag<kFlagMustDoNullCheck>(false); } + TypeCheckKind GetTypeCheckKind() const { return GetPackedField<TypeCheckKindField>(); } + bool IsExactCheck() const { return GetTypeCheckKind() == TypeCheckKind::kExactCheck; } static bool CanCallRuntime(TypeCheckKind check_kind) { // Mips currently does runtime calls for any other checks. @@ -5710,8 +5888,13 @@ class HInstanceOf : public HExpression<2> { DECLARE_INSTRUCTION(InstanceOf); private: - const TypeCheckKind check_kind_; - bool must_do_null_check_; + static constexpr size_t kFieldTypeCheckKind = kNumberOfExpressionPackedBits; + static constexpr size_t kFieldTypeCheckKindSize = + MinimumBitsToStore(static_cast<size_t>(TypeCheckKind::kLast)); + static constexpr size_t kFlagMustDoNullCheck = kFieldTypeCheckKind + kFieldTypeCheckKindSize; + static constexpr size_t kNumberOfInstanceOfPackedBits = kFlagMustDoNullCheck + 1; + static_assert(kNumberOfInstanceOfPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + using TypeCheckKindField = BitField<TypeCheckKind, kFieldTypeCheckKind, kFieldTypeCheckKindSize>; DISALLOW_COPY_AND_ASSIGN(HInstanceOf); }; @@ -5720,28 +5903,35 @@ class HBoundType : public HExpression<1> { public: HBoundType(HInstruction* input, uint32_t dex_pc = kNoDexPc) : HExpression(Primitive::kPrimNot, SideEffects::None(), dex_pc), - upper_bound_(ReferenceTypeInfo::CreateInvalid()), - upper_can_be_null_(true), - can_be_null_(true) { + upper_bound_(ReferenceTypeInfo::CreateInvalid()) { + SetPackedFlag<kFlagUpperCanBeNull>(true); + SetPackedFlag<kFlagCanBeNull>(true); DCHECK_EQ(input->GetType(), Primitive::kPrimNot); SetRawInputAt(0, input); } // {Get,Set}Upper* should only be used in reference type propagation. const ReferenceTypeInfo& GetUpperBound() const { return upper_bound_; } - bool GetUpperCanBeNull() const { return upper_can_be_null_; } + bool GetUpperCanBeNull() const { return GetPackedFlag<kFlagUpperCanBeNull>(); } void SetUpperBound(const ReferenceTypeInfo& upper_bound, bool can_be_null); void SetCanBeNull(bool can_be_null) { - DCHECK(upper_can_be_null_ || !can_be_null); - can_be_null_ = can_be_null; + DCHECK(GetUpperCanBeNull() || !can_be_null); + SetPackedFlag<kFlagCanBeNull>(can_be_null); } - bool CanBeNull() const OVERRIDE { return can_be_null_; } + bool CanBeNull() const OVERRIDE { return GetPackedFlag<kFlagCanBeNull>(); } DECLARE_INSTRUCTION(BoundType); private: + // Represents the top constraint that can_be_null_ cannot exceed (i.e. if this + // is false then CanBeNull() cannot be true). + static constexpr size_t kFlagUpperCanBeNull = kNumberOfExpressionPackedBits; + static constexpr size_t kFlagCanBeNull = kFlagUpperCanBeNull + 1; + static constexpr size_t kNumberOfBoundTypePackedBits = kFlagCanBeNull + 1; + static_assert(kNumberOfBoundTypePackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + // Encodes the most upper class that this instruction can have. In other words // it is always the case that GetUpperBound().IsSupertypeOf(GetReferenceType()). // It is used to bound the type in cases like: @@ -5749,10 +5939,6 @@ class HBoundType : public HExpression<1> { // // uper_bound_ will be ClassX // } ReferenceTypeInfo upper_bound_; - // Represents the top constraint that can_be_null_ cannot exceed (i.e. if this - // is false then can_be_null_ cannot be true). - bool upper_can_be_null_; - bool can_be_null_; DISALLOW_COPY_AND_ASSIGN(HBoundType); }; @@ -5763,9 +5949,9 @@ class HCheckCast : public HTemplateInstruction<2> { HLoadClass* constant, TypeCheckKind check_kind, uint32_t dex_pc) - : HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc), - check_kind_(check_kind), - must_do_null_check_(true) { + : HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc) { + SetPackedField<TypeCheckKindField>(check_kind); + SetPackedFlag<kFlagMustDoNullCheck>(true); SetRawInputAt(0, object); SetRawInputAt(1, constant); } @@ -5783,17 +5969,21 @@ class HCheckCast : public HTemplateInstruction<2> { bool CanThrow() const OVERRIDE { return true; } - bool MustDoNullCheck() const { return must_do_null_check_; } - void ClearMustDoNullCheck() { must_do_null_check_ = false; } - TypeCheckKind GetTypeCheckKind() const { return check_kind_; } - - bool IsExactCheck() const { return check_kind_ == TypeCheckKind::kExactCheck; } + bool MustDoNullCheck() const { return GetPackedFlag<kFlagMustDoNullCheck>(); } + void ClearMustDoNullCheck() { SetPackedFlag<kFlagMustDoNullCheck>(false); } + TypeCheckKind GetTypeCheckKind() const { return GetPackedField<TypeCheckKindField>(); } + bool IsExactCheck() const { return GetTypeCheckKind() == TypeCheckKind::kExactCheck; } DECLARE_INSTRUCTION(CheckCast); private: - const TypeCheckKind check_kind_; - bool must_do_null_check_; + static constexpr size_t kFieldTypeCheckKind = kNumberOfGenericPackedBits; + static constexpr size_t kFieldTypeCheckKindSize = + MinimumBitsToStore(static_cast<size_t>(TypeCheckKind::kLast)); + static constexpr size_t kFlagMustDoNullCheck = kFieldTypeCheckKind + kFieldTypeCheckKindSize; + static constexpr size_t kNumberOfCheckCastPackedBits = kFlagMustDoNullCheck + 1; + static_assert(kNumberOfCheckCastPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + using TypeCheckKindField = BitField<TypeCheckKind, kFieldTypeCheckKind, kFieldTypeCheckKindSize>; DISALLOW_COPY_AND_ASSIGN(HCheckCast); }; @@ -5802,30 +5992,40 @@ class HMemoryBarrier : public HTemplateInstruction<0> { public: explicit HMemoryBarrier(MemBarrierKind barrier_kind, uint32_t dex_pc = kNoDexPc) : HTemplateInstruction( - SideEffects::AllWritesAndReads(), dex_pc), // Assume write/read on all fields/arrays. - barrier_kind_(barrier_kind) {} + SideEffects::AllWritesAndReads(), dex_pc) { // Assume write/read on all fields/arrays. + SetPackedField<BarrierKindField>(barrier_kind); + } - MemBarrierKind GetBarrierKind() { return barrier_kind_; } + MemBarrierKind GetBarrierKind() { return GetPackedField<BarrierKindField>(); } DECLARE_INSTRUCTION(MemoryBarrier); private: - const MemBarrierKind barrier_kind_; + static constexpr size_t kFieldBarrierKind = HInstruction::kNumberOfGenericPackedBits; + static constexpr size_t kFieldBarrierKindSize = + MinimumBitsToStore(static_cast<size_t>(kLastBarrierKind)); + static constexpr size_t kNumberOfMemoryBarrierPackedBits = + kFieldBarrierKind + kFieldBarrierKindSize; + static_assert(kNumberOfMemoryBarrierPackedBits <= kMaxNumberOfPackedBits, + "Too many packed fields."); + using BarrierKindField = BitField<MemBarrierKind, kFieldBarrierKind, kFieldBarrierKindSize>; DISALLOW_COPY_AND_ASSIGN(HMemoryBarrier); }; class HMonitorOperation : public HTemplateInstruction<1> { public: - enum OperationKind { + enum class OperationKind { kEnter, kExit, + kLast = kExit }; HMonitorOperation(HInstruction* object, OperationKind kind, uint32_t dex_pc) : HTemplateInstruction( - SideEffects::AllExceptGCDependency(), dex_pc), // Assume write/read on all fields/arrays. - kind_(kind) { + SideEffects::AllExceptGCDependency(), // Assume write/read on all fields/arrays. + dex_pc) { + SetPackedField<OperationKindField>(kind); SetRawInputAt(0, object); } @@ -5839,13 +6039,20 @@ class HMonitorOperation : public HTemplateInstruction<1> { return IsEnter(); } - - bool IsEnter() const { return kind_ == kEnter; } + OperationKind GetOperationKind() const { return GetPackedField<OperationKindField>(); } + bool IsEnter() const { return GetOperationKind() == OperationKind::kEnter; } DECLARE_INSTRUCTION(MonitorOperation); private: - const OperationKind kind_; + static constexpr size_t kFieldOperationKind = HInstruction::kNumberOfGenericPackedBits; + static constexpr size_t kFieldOperationKindSize = + MinimumBitsToStore(static_cast<size_t>(OperationKind::kLast)); + static constexpr size_t kNumberOfMonitorOperationPackedBits = + kFieldOperationKind + kFieldOperationKindSize; + static_assert(kNumberOfMonitorOperationPackedBits <= HInstruction::kMaxNumberOfPackedBits, + "Too many packed fields."); + using OperationKindField = BitField<OperationKind, kFieldOperationKind, kFieldOperationKindSize>; private: DISALLOW_COPY_AND_ASSIGN(HMonitorOperation); @@ -6022,6 +6229,9 @@ class HParallelMove : public HTemplateInstruction<0> { } // namespace art +#if defined(ART_ENABLE_CODEGEN_arm) || defined(ART_ENABLE_CODEGEN_arm64) +#include "nodes_shared.h" +#endif #ifdef ART_ENABLE_CODEGEN_arm #include "nodes_arm.h" #endif diff --git a/compiler/optimizing/nodes_arm64.h b/compiler/optimizing/nodes_arm64.h index 445cdab191..75a71e78b8 100644 --- a/compiler/optimizing/nodes_arm64.h +++ b/compiler/optimizing/nodes_arm64.h @@ -118,38 +118,64 @@ class HArm64IntermediateAddress : public HExpression<2> { DISALLOW_COPY_AND_ASSIGN(HArm64IntermediateAddress); }; -class HArm64MultiplyAccumulate : public HExpression<3> { +class HArm64BitwiseNegatedRight : public HBinaryOperation { public: - HArm64MultiplyAccumulate(Primitive::Type type, - InstructionKind op, - HInstruction* accumulator, - HInstruction* mul_left, - HInstruction* mul_right, - uint32_t dex_pc = kNoDexPc) - : HExpression(type, SideEffects::None(), dex_pc), op_kind_(op) { - SetRawInputAt(kInputAccumulatorIndex, accumulator); - SetRawInputAt(kInputMulLeftIndex, mul_left); - SetRawInputAt(kInputMulRightIndex, mul_right); + HArm64BitwiseNegatedRight(Primitive::Type result_type, + InstructionKind op, + HInstruction* left, + HInstruction* right, + uint32_t dex_pc = kNoDexPc) + : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc), + op_kind_(op) { + DCHECK(op == HInstruction::kAnd || op == HInstruction::kOr || op == HInstruction::kXor) << op; } - static constexpr int kInputAccumulatorIndex = 0; - static constexpr int kInputMulLeftIndex = 1; - static constexpr int kInputMulRightIndex = 2; + template <typename T, typename U> + auto Compute(T x, U y) const -> decltype(x & ~y) { + static_assert(std::is_same<decltype(x & ~y), decltype(x | ~y)>::value && + std::is_same<decltype(x & ~y), decltype(x ^ ~y)>::value, + "Inconsistent negated bitwise types"); + switch (op_kind_) { + case HInstruction::kAnd: + return x & ~y; + case HInstruction::kOr: + return x | ~y; + case HInstruction::kXor: + return x ^ ~y; + default: + LOG(FATAL) << "Unreachable"; + UNREACHABLE(); + } + } - bool CanBeMoved() const OVERRIDE { return true; } - bool InstructionDataEquals(HInstruction* other) const OVERRIDE { - return op_kind_ == other->AsArm64MultiplyAccumulate()->op_kind_; + HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant( + Compute(x->GetValue(), y->GetValue()), GetDexPc()); + } + HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetLongConstant( + Compute(x->GetValue(), y->GetValue()), GetDexPc()); + } + HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED, + HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for float values"; + UNREACHABLE(); + } + HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED, + HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for double values"; + UNREACHABLE(); } InstructionKind GetOpKind() const { return op_kind_; } - DECLARE_INSTRUCTION(Arm64MultiplyAccumulate); + DECLARE_INSTRUCTION(Arm64BitwiseNegatedRight); private: - // Indicates if this is a MADD or MSUB. - InstructionKind op_kind_; + // Specifies the bitwise operation, which will be then negated. + const InstructionKind op_kind_; - DISALLOW_COPY_AND_ASSIGN(HArm64MultiplyAccumulate); + DISALLOW_COPY_AND_ASSIGN(HArm64BitwiseNegatedRight); }; } // namespace art diff --git a/compiler/optimizing/nodes_shared.h b/compiler/optimizing/nodes_shared.h new file mode 100644 index 0000000000..b04b622838 --- /dev/null +++ b/compiler/optimizing/nodes_shared.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_OPTIMIZING_NODES_SHARED_H_ +#define ART_COMPILER_OPTIMIZING_NODES_SHARED_H_ + +namespace art { + +class HMultiplyAccumulate : public HExpression<3> { + public: + HMultiplyAccumulate(Primitive::Type type, + InstructionKind op, + HInstruction* accumulator, + HInstruction* mul_left, + HInstruction* mul_right, + uint32_t dex_pc = kNoDexPc) + : HExpression(type, SideEffects::None(), dex_pc), op_kind_(op) { + SetRawInputAt(kInputAccumulatorIndex, accumulator); + SetRawInputAt(kInputMulLeftIndex, mul_left); + SetRawInputAt(kInputMulRightIndex, mul_right); + } + + static constexpr int kInputAccumulatorIndex = 0; + static constexpr int kInputMulLeftIndex = 1; + static constexpr int kInputMulRightIndex = 2; + + bool CanBeMoved() const OVERRIDE { return true; } + bool InstructionDataEquals(HInstruction* other) const OVERRIDE { + return op_kind_ == other->AsMultiplyAccumulate()->op_kind_; + } + + InstructionKind GetOpKind() const { return op_kind_; } + + DECLARE_INSTRUCTION(MultiplyAccumulate); + + private: + // Indicates if this is a MADD or MSUB. + const InstructionKind op_kind_; + + DISALLOW_COPY_AND_ASSIGN(HMultiplyAccumulate); +}; + +} // namespace art + +#endif // ART_COMPILER_OPTIMIZING_NODES_SHARED_H_ diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index b1891c979e..5a9f2583fd 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -60,6 +60,7 @@ #include "induction_var_analysis.h" #include "inliner.h" #include "instruction_simplifier.h" +#include "instruction_simplifier_arm.h" #include "intrinsics.h" #include "jit/debugger_interface.h" #include "jit/jit_code_cache.h" @@ -438,7 +439,10 @@ static void RunArchOptimizations(InstructionSet instruction_set, case kThumb2: case kArm: { arm::DexCacheArrayFixups* fixups = new (arena) arm::DexCacheArrayFixups(graph, stats); + arm::InstructionSimplifierArm* simplifier = + new (arena) arm::InstructionSimplifierArm(graph, stats); HOptimization* arm_optimizations[] = { + simplifier, fixups }; RunOptimizations(arm_optimizations, arraysize(arm_optimizations), pass_observer); diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index 4784de1380..54cbdf8b66 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -63,8 +63,7 @@ void StackMapStream::EndStackMapEntry() { void StackMapStream::AddDexRegisterEntry(DexRegisterLocation::Kind kind, int32_t value) { if (kind != DexRegisterLocation::Kind::kNone) { // Ensure we only use non-compressed location kind at this stage. - DCHECK(DexRegisterLocation::IsShortLocationKind(kind)) - << DexRegisterLocation::PrettyDescriptor(kind); + DCHECK(DexRegisterLocation::IsShortLocationKind(kind)) << kind; DexRegisterLocation location(kind, value); // Look for Dex register `location` in the location catalog (using the @@ -257,6 +256,7 @@ void StackMapStream::FillIn(MemoryRegion region) { // Ensure we reached the end of the Dex registers location_catalog. DCHECK_EQ(location_catalog_offset, dex_register_location_catalog_region.size()); + ArenaBitVector empty_bitmask(allocator_, 0, /* expandable */ false); uintptr_t next_dex_register_map_offset = 0; uintptr_t next_inline_info_offset = 0; for (size_t i = 0, e = stack_maps_.size(); i < e; ++i) { @@ -268,6 +268,9 @@ void StackMapStream::FillIn(MemoryRegion region) { stack_map.SetRegisterMask(stack_map_encoding_, entry.register_mask); if (entry.sp_mask != nullptr) { stack_map.SetStackMask(stack_map_encoding_, *entry.sp_mask); + } else { + // The MemoryRegion does not have to be zeroed, so make sure we clear the bits. + stack_map.SetStackMask(stack_map_encoding_, empty_bitmask); } if (entry.num_dex_registers == 0 || (entry.live_dex_registers_mask->NumSetBits() == 0)) { @@ -344,6 +347,11 @@ void StackMapStream::FillIn(MemoryRegion region) { } } } + + // Verify all written data in debug build. + if (kIsDebugBuild) { + CheckCodeInfo(region); + } } void StackMapStream::FillInDexRegisterMap(DexRegisterMap dex_register_map, @@ -423,4 +431,90 @@ bool StackMapStream::HaveTheSameDexMaps(const StackMapEntry& a, const StackMapEn return true; } +// Helper for CheckCodeInfo - check that register map has the expected content. +void StackMapStream::CheckDexRegisterMap(const CodeInfo& code_info, + const DexRegisterMap& dex_register_map, + size_t num_dex_registers, + BitVector* live_dex_registers_mask, + size_t dex_register_locations_index) const { + StackMapEncoding encoding = code_info.ExtractEncoding(); + for (size_t reg = 0; reg < num_dex_registers; reg++) { + // Find the location we tried to encode. + DexRegisterLocation expected = DexRegisterLocation::None(); + if (live_dex_registers_mask->IsBitSet(reg)) { + size_t catalog_index = dex_register_locations_[dex_register_locations_index++]; + expected = location_catalog_entries_[catalog_index]; + } + // Compare to the seen location. + if (expected.GetKind() == DexRegisterLocation::Kind::kNone) { + DCHECK(!dex_register_map.IsValid() || !dex_register_map.IsDexRegisterLive(reg)); + } else { + DCHECK(dex_register_map.IsDexRegisterLive(reg)); + DexRegisterLocation seen = dex_register_map.GetDexRegisterLocation( + reg, num_dex_registers, code_info, encoding); + DCHECK_EQ(expected.GetKind(), seen.GetKind()); + DCHECK_EQ(expected.GetValue(), seen.GetValue()); + } + } + if (num_dex_registers == 0) { + DCHECK(!dex_register_map.IsValid()); + } +} + +// Check that all StackMapStream inputs are correctly encoded by trying to read them back. +void StackMapStream::CheckCodeInfo(MemoryRegion region) const { + CodeInfo code_info(region); + StackMapEncoding encoding = code_info.ExtractEncoding(); + DCHECK_EQ(code_info.GetNumberOfStackMaps(), stack_maps_.size()); + for (size_t s = 0; s < stack_maps_.size(); ++s) { + const StackMap stack_map = code_info.GetStackMapAt(s, encoding); + StackMapEntry entry = stack_maps_[s]; + + // Check main stack map fields. + DCHECK_EQ(stack_map.GetNativePcOffset(encoding), entry.native_pc_offset); + DCHECK_EQ(stack_map.GetDexPc(encoding), entry.dex_pc); + DCHECK_EQ(stack_map.GetRegisterMask(encoding), entry.register_mask); + MemoryRegion stack_mask = stack_map.GetStackMask(encoding); + if (entry.sp_mask != nullptr) { + DCHECK_GE(stack_mask.size_in_bits(), entry.sp_mask->GetNumberOfBits()); + for (size_t b = 0; b < stack_mask.size_in_bits(); b++) { + DCHECK_EQ(stack_mask.LoadBit(b), entry.sp_mask->IsBitSet(b)); + } + } else { + for (size_t b = 0; b < stack_mask.size_in_bits(); b++) { + DCHECK_EQ(stack_mask.LoadBit(b), 0u); + } + } + + CheckDexRegisterMap(code_info, + code_info.GetDexRegisterMapOf( + stack_map, encoding, entry.num_dex_registers), + entry.num_dex_registers, + entry.live_dex_registers_mask, + entry.dex_register_locations_start_index); + + // Check inline info. + DCHECK_EQ(stack_map.HasInlineInfo(encoding), (entry.inlining_depth != 0)); + if (entry.inlining_depth != 0) { + InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding); + DCHECK_EQ(inline_info.GetDepth(), entry.inlining_depth); + for (size_t d = 0; d < entry.inlining_depth; ++d) { + size_t inline_info_index = entry.inline_infos_start_index + d; + DCHECK_LT(inline_info_index, inline_infos_.size()); + InlineInfoEntry inline_entry = inline_infos_[inline_info_index]; + DCHECK_EQ(inline_info.GetDexPcAtDepth(d), inline_entry.dex_pc); + DCHECK_EQ(inline_info.GetMethodIndexAtDepth(d), inline_entry.method_index); + DCHECK_EQ(inline_info.GetInvokeTypeAtDepth(d), inline_entry.invoke_type); + + CheckDexRegisterMap(code_info, + code_info.GetDexRegisterMapAtDepth( + d, inline_info, encoding, inline_entry.num_dex_registers), + inline_entry.num_dex_registers, + inline_entry.live_dex_registers_mask, + inline_entry.dex_register_locations_start_index); + } + } + } +} + } // namespace art diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h index fc27a2b446..016a911424 100644 --- a/compiler/optimizing/stack_map_stream.h +++ b/compiler/optimizing/stack_map_stream.h @@ -167,6 +167,13 @@ class StackMapStream : public ValueObject { const BitVector& live_dex_registers_mask, uint32_t start_index_in_dex_register_locations) const; + void CheckDexRegisterMap(const CodeInfo& code_info, + const DexRegisterMap& dex_register_map, + size_t num_dex_registers, + BitVector* live_dex_registers_mask, + size_t dex_register_locations_index) const; + void CheckCodeInfo(MemoryRegion region) const; + ArenaAllocator* allocator_; ArenaVector<StackMapEntry> stack_maps_; diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index cac12d1920..dfcb4bcaec 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -217,7 +217,7 @@ NO_RETURN static void Usage(const char* fmt, ...) { UsageError(" --image=<file.art>: specifies an output image filename."); UsageError(" Example: --image=/system/framework/boot.art"); UsageError(""); - UsageError(" --image-format=(uncompressed|lz4):"); + UsageError(" --image-format=(uncompressed|lz4|lz4hc):"); UsageError(" Which format to store the image."); UsageError(" Example: --image-format=lz4"); UsageError(" Default: uncompressed"); @@ -681,6 +681,8 @@ class Dex2Oat FINAL { const StringPiece format_str = option.substr(substr.length()); if (format_str == "lz4") { image_storage_mode_ = ImageHeader::kStorageModeLZ4; + } else if (format_str == "lz4hc") { + image_storage_mode_ = ImageHeader::kStorageModeLZ4HC; } else if (format_str == "uncompressed") { image_storage_mode_ = ImageHeader::kStorageModeUncompressed; } else { @@ -696,11 +698,6 @@ class Dex2Oat FINAL { Usage("Can't have both --image and (--app-image-fd or --app-image-file)"); } - if (IsBootImage()) { - // We need the boot image to always be debuggable. - compiler_options_->debuggable_ = true; - } - if (oat_filenames_.empty() && oat_fd_ == -1) { Usage("Output must be supplied with either --oat-file or --oat-fd"); } diff --git a/runtime/base/bit_field.h b/runtime/base/bit_field.h index fd65d500aa..a80ca28d2e 100644 --- a/runtime/base/bit_field.h +++ b/runtime/base/bit_field.h @@ -26,9 +26,18 @@ static constexpr uintptr_t kUintPtrTOne = 1U; // BitField is a template for encoding and decoding a bit field inside // an unsigned machine word. -template<typename T, int position, int size> +template<typename T, size_t kPosition, size_t kSize> class BitField { public: + typedef T value_type; + static constexpr size_t position = kPosition; + static constexpr size_t size = kSize; + + static_assert(position < sizeof(uintptr_t) * kBitsPerByte, "Invalid position."); + static_assert(size != 0u, "Invalid size."); + static_assert(size <= sizeof(uintptr_t) * kBitsPerByte, "Invalid size."); + static_assert(size + position <= sizeof(uintptr_t) * kBitsPerByte, "Invalid position + size."); + // Tells whether the provided value fits into the bit field. static bool IsValid(T value) { return (static_cast<uintptr_t>(value) & ~((kUintPtrTOne << size) - 1)) == 0; diff --git a/runtime/base/bit_vector.h b/runtime/base/bit_vector.h index 9b55e708c8..424ebb70f6 100644 --- a/runtime/base/bit_vector.h +++ b/runtime/base/bit_vector.h @@ -229,6 +229,11 @@ class BitVector { */ int GetHighestBitSet() const; + // Minimum number of bits required to store this vector, 0 if none are set. + size_t GetNumberOfBits() const { + return GetHighestBitSet() + 1; + } + // Is bit set in storage. (No range check.) static bool IsBitSet(const uint32_t* storage, uint32_t idx) { return (storage[WordIndex(idx)] & BitMask(idx)) != 0; diff --git a/runtime/check_reference_map_visitor.h b/runtime/check_reference_map_visitor.h index b9ea475149..fcf3326c81 100644 --- a/runtime/check_reference_map_visitor.h +++ b/runtime/check_reference_map_visitor.h @@ -100,8 +100,7 @@ class CheckReferenceMapVisitor : public StackVisitor { CHECK_EQ(location.GetValue(), 0); break; default: - LOG(FATAL) << "Unexpected location kind" - << DexRegisterLocation::PrettyDescriptor(location.GetInternalKind()); + LOG(FATAL) << "Unexpected location kind " << location.GetInternalKind(); } } } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 9ea082769a..cd4daeb7bd 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -2608,18 +2608,6 @@ const void* ClassLinker::GetOatMethodQuickCodeFor(ArtMethod* method) { return nullptr; } -const void* ClassLinker::GetQuickOatCodeFor(const DexFile& dex_file, - uint16_t class_def_idx, - uint32_t method_idx) { - bool found; - OatFile::OatClass oat_class = FindOatClass(dex_file, class_def_idx, &found); - if (!found) { - return nullptr; - } - uint32_t oat_method_idx = GetOatMethodIndexFromMethodIndex(dex_file, class_def_idx, method_idx); - return oat_class.GetOatMethod(oat_method_idx).GetQuickCode(); -} - bool ClassLinker::ShouldUseInterpreterEntrypoint(ArtMethod* method, const void* quick_code) { if (UNLIKELY(method->IsNative() || method->IsProxyMethod())) { return false; @@ -2650,6 +2638,11 @@ bool ClassLinker::ShouldUseInterpreterEntrypoint(ArtMethod* method, const void* return true; } + if (Dbg::IsDebuggerActive()) { + // Boot image classes are AOT-compiled as non-debuggable. + return runtime->GetHeap()->IsInBootImageOatFile(quick_code); + } + return false; } diff --git a/runtime/class_linker.h b/runtime/class_linker.h index a9448f732c..aa55dac7be 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -472,12 +472,6 @@ class ClassLinker { const void* GetQuickOatCodeFor(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_); - // Get the oat code for a method from a method index. - const void* GetQuickOatCodeFor(const DexFile& dex_file, - uint16_t class_def_idx, - uint32_t method_idx) - SHARED_REQUIRES(Locks::mutator_lock_); - // Get compiled code for a method, return null if no code // exists. This is unlike Get..OatCodeFor which will return a bridge // or interpreter entrypoint. diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 904490aa8c..bc6589380c 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -28,6 +28,7 @@ #include "class_linker-inl.h" #include "dex_file-inl.h" #include "dex_instruction.h" +#include "entrypoints/runtime_asm_entrypoints.h" #include "gc/accounting/card_table-inl.h" #include "gc/allocation_record.h" #include "gc/scoped_gc_critical_section.h" @@ -570,6 +571,29 @@ bool Dbg::RequiresDeoptimization() { return !Runtime::Current()->GetInstrumentation()->IsForcedInterpretOnly(); } +// Used to patch boot image method entry point to interpreter bridge. +class UpdateEntryPointsClassVisitor : public ClassVisitor { + public: + explicit UpdateEntryPointsClassVisitor(instrumentation::Instrumentation* instrumentation) + : instrumentation_(instrumentation) {} + + bool operator()(mirror::Class* klass) OVERRIDE REQUIRES(Locks::mutator_lock_) { + auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); + for (auto& m : klass->GetMethods(pointer_size)) { + const void* code = m.GetEntryPointFromQuickCompiledCode(); + if (Runtime::Current()->GetHeap()->IsInBootImageOatFile(code) && + !m.IsNative() && + !m.IsProxyMethod()) { + instrumentation_->UpdateMethodsCode(&m, GetQuickToInterpreterBridge()); + } + } + return true; + } + + private: + instrumentation::Instrumentation* const instrumentation_; +}; + void Dbg::GoActive() { // Enable all debugging features, including scans for breakpoints. // This is a no-op if we're already active. @@ -598,6 +622,14 @@ void Dbg::GoActive() { } Runtime* runtime = Runtime::Current(); + // Since boot image code is AOT compiled as not debuggable, we need to patch + // entry points of methods in boot image to interpreter bridge. + if (!runtime->GetInstrumentation()->IsForcedInterpretOnly()) { + ScopedObjectAccess soa(self); + UpdateEntryPointsClassVisitor visitor(runtime->GetInstrumentation()); + runtime->GetClassLinker()->VisitClasses(&visitor); + } + ScopedSuspendAll ssa(__FUNCTION__); if (RequiresDeoptimization()) { runtime->GetInstrumentation()->EnableDeoptimization(); diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index a656fb8faf..4bee46285d 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -4058,6 +4058,15 @@ bool Heap::ObjectIsInBootImageSpace(mirror::Object* obj) const { return false; } +bool Heap::IsInBootImageOatFile(const void* p) const { + for (gc::space::ImageSpace* space : boot_image_spaces_) { + if (space->GetOatFile()->Contains(p)) { + return true; + } + } + return false; +} + void Heap::GetBootImagesSize(uint32_t* boot_image_begin, uint32_t* boot_image_end, uint32_t* boot_oat_begin, diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index a181e23b53..6edb548155 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -605,6 +605,9 @@ class Heap { bool ObjectIsInBootImageSpace(mirror::Object* obj) const SHARED_REQUIRES(Locks::mutator_lock_); + bool IsInBootImageOatFile(const void* p) const + SHARED_REQUIRES(Locks::mutator_lock_); + void GetBootImagesSize(uint32_t* boot_image_begin, uint32_t* boot_image_end, uint32_t* boot_oat_begin, diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index 894ce9af72..4ef36a449d 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -1252,7 +1252,8 @@ ImageSpace* ImageSpace::Init(const char* image_filename, // Only care about the error message for the last address in addresses. We want to avoid the // overhead of printing the process maps if we can relocate. std::string* out_error_msg = (address == addresses.back()) ? &temp_error_msg : nullptr; - if (image_header->GetStorageMode() == ImageHeader::kStorageModeUncompressed) { + const ImageHeader::StorageMode storage_mode = image_header->GetStorageMode(); + if (storage_mode == ImageHeader::kStorageModeUncompressed) { map.reset(MemMap::MapFileAtAddress(address, image_header->GetImageSize(), PROT_READ | PROT_WRITE, @@ -1264,6 +1265,12 @@ ImageSpace* ImageSpace::Init(const char* image_filename, image_filename, /*out*/out_error_msg)); } else { + if (storage_mode != ImageHeader::kStorageModeLZ4 && + storage_mode != ImageHeader::kStorageModeLZ4HC) { + *error_msg = StringPrintf("Invalid storage mode in image header %d", + static_cast<int>(storage_mode)); + return nullptr; + } // Reserve output and decompress into it. map.reset(MemMap::MapAnonymous(image_location, address, @@ -1289,6 +1296,8 @@ ImageSpace* ImageSpace::Init(const char* image_filename, } memcpy(map->Begin(), image_header, sizeof(ImageHeader)); const uint64_t start = NanoTime(); + // LZ4HC and LZ4 have same internal format, both use LZ4_decompress. + TimingLogger::ScopedTiming timing2("LZ4 decompress image", &logger); const size_t decompressed_size = LZ4_decompress_safe( reinterpret_cast<char*>(temp_map->Begin()) + sizeof(ImageHeader), reinterpret_cast<char*>(map->Begin()) + write_offset, diff --git a/runtime/image.h b/runtime/image.h index 146ee00c84..8e5dbad57d 100644 --- a/runtime/image.h +++ b/runtime/image.h @@ -81,6 +81,7 @@ class PACKED(4) ImageHeader { enum StorageMode : uint32_t { kStorageModeUncompressed, kStorageModeLZ4, + kStorageModeLZ4HC, kStorageModeCount, // Number of elements in enum. }; static constexpr StorageMode kDefaultStorageMode = kStorageModeUncompressed; diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index 748463529e..b107b72b55 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -104,6 +104,14 @@ static void UpdateEntrypoints(ArtMethod* method, const void* quick_code) method->SetEntryPointFromQuickCompiledCode(quick_code); } +bool Instrumentation::NeedDebugVersionForBootImageCode(ArtMethod* method, const void* code) const + SHARED_REQUIRES(Locks::mutator_lock_) { + return Dbg::IsDebuggerActive() && + Runtime::Current()->GetHeap()->IsInBootImageOatFile(code) && + !method->IsNative() && + !method->IsProxyMethod(); +} + void Instrumentation::InstallStubsForMethod(ArtMethod* method) { if (!method->IsInvokable() || method->IsProxyMethod()) { // Do not change stubs for these methods. @@ -124,6 +132,9 @@ void Instrumentation::InstallStubsForMethod(ArtMethod* method) { new_quick_code = GetQuickToInterpreterBridge(); } else if (is_class_initialized || !method->IsStatic() || method->IsConstructor()) { new_quick_code = class_linker->GetQuickOatCodeFor(method); + if (NeedDebugVersionForBootImageCode(method, new_quick_code)) { + new_quick_code = GetQuickToInterpreterBridge(); + } } else { new_quick_code = GetQuickResolutionStub(); } @@ -136,10 +147,13 @@ void Instrumentation::InstallStubsForMethod(ArtMethod* method) { // class, all its static methods code will be set to the instrumentation entry point. // For more details, see ClassLinker::FixupStaticTrampolines. if (is_class_initialized || !method->IsStatic() || method->IsConstructor()) { - if (entry_exit_stubs_installed_) { + new_quick_code = class_linker->GetQuickOatCodeFor(method); + if (NeedDebugVersionForBootImageCode(method, new_quick_code)) { + // Oat code should not be used. Don't install instrumentation stub and + // use interpreter for instrumentation. + new_quick_code = GetQuickToInterpreterBridge(); + } else if (entry_exit_stubs_installed_) { new_quick_code = GetQuickInstrumentationEntryPoint(); - } else { - new_quick_code = class_linker->GetQuickOatCodeFor(method); } } else { new_quick_code = GetQuickResolutionStub(); @@ -775,6 +789,9 @@ void Instrumentation::Undeoptimize(ArtMethod* method) { UpdateEntrypoints(method, GetQuickResolutionStub()); } else { const void* quick_code = class_linker->GetQuickOatCodeFor(method); + if (NeedDebugVersionForBootImageCode(method, quick_code)) { + quick_code = GetQuickToInterpreterBridge(); + } UpdateEntrypoints(method, quick_code); } diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h index e3cbf53873..2e4be6b689 100644 --- a/runtime/instrumentation.h +++ b/runtime/instrumentation.h @@ -247,6 +247,11 @@ class Instrumentation { return forced_interpret_only_; } + // Code is in boot image oat file which isn't compiled as debuggable. + // Need debug version (interpreter or jitted) if that's the case. + bool NeedDebugVersionForBootImageCode(ArtMethod* method, const void* code) const + SHARED_REQUIRES(Locks::mutator_lock_); + bool AreExitStubsInstalled() const { return instrumentation_stubs_installed_; } diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc index ca8598e5e6..12d6fdc00d 100644 --- a/runtime/interpreter/interpreter_goto_table_impl.cc +++ b/runtime/interpreter/interpreter_goto_table_impl.cc @@ -792,6 +792,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF } ADVANCE(offset); } else { + BRANCH_INSTRUMENTATION(2); ADVANCE(2); } } @@ -810,6 +811,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF } ADVANCE(offset); } else { + BRANCH_INSTRUMENTATION(2); ADVANCE(2); } } @@ -828,6 +830,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF } ADVANCE(offset); } else { + BRANCH_INSTRUMENTATION(2); ADVANCE(2); } } @@ -846,6 +849,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF } ADVANCE(offset); } else { + BRANCH_INSTRUMENTATION(2); ADVANCE(2); } } @@ -864,6 +868,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF } ADVANCE(offset); } else { + BRANCH_INSTRUMENTATION(2); ADVANCE(2); } } @@ -882,6 +887,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF } ADVANCE(offset); } else { + BRANCH_INSTRUMENTATION(2); ADVANCE(2); } } @@ -899,6 +905,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF } ADVANCE(offset); } else { + BRANCH_INSTRUMENTATION(2); ADVANCE(2); } } @@ -916,6 +923,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF } ADVANCE(offset); } else { + BRANCH_INSTRUMENTATION(2); ADVANCE(2); } } @@ -933,6 +941,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF } ADVANCE(offset); } else { + BRANCH_INSTRUMENTATION(2); ADVANCE(2); } } @@ -950,6 +959,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF } ADVANCE(offset); } else { + BRANCH_INSTRUMENTATION(2); ADVANCE(2); } } @@ -967,6 +977,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF } ADVANCE(offset); } else { + BRANCH_INSTRUMENTATION(2); ADVANCE(2); } } @@ -984,6 +995,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF } ADVANCE(offset); } else { + BRANCH_INSTRUMENTATION(2); ADVANCE(2); } } diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index 25dbab2494..0488dbf028 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -712,6 +712,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } inst = inst->RelativeAt(offset); } else { + BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); } break; @@ -727,6 +728,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } inst = inst->RelativeAt(offset); } else { + BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); } break; @@ -742,6 +744,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } inst = inst->RelativeAt(offset); } else { + BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); } break; @@ -757,6 +760,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } inst = inst->RelativeAt(offset); } else { + BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); } break; @@ -772,6 +776,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } inst = inst->RelativeAt(offset); } else { + BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); } break; @@ -787,6 +792,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } inst = inst->RelativeAt(offset); } else { + BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); } break; @@ -801,6 +807,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } inst = inst->RelativeAt(offset); } else { + BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); } break; @@ -815,6 +822,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } inst = inst->RelativeAt(offset); } else { + BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); } break; @@ -829,6 +837,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } inst = inst->RelativeAt(offset); } else { + BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); } break; @@ -843,6 +852,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } inst = inst->RelativeAt(offset); } else { + BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); } break; @@ -857,6 +867,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } inst = inst->RelativeAt(offset); } else { + BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); } break; @@ -871,6 +882,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } inst = inst->RelativeAt(offset); } else { + BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); } break; diff --git a/runtime/interpreter/mterp/arm/bincmp.S b/runtime/interpreter/mterp/arm/bincmp.S index 774e1676b7..cfad7147e2 100644 --- a/runtime/interpreter/mterp/arm/bincmp.S +++ b/runtime/interpreter/mterp/arm/bincmp.S @@ -6,14 +6,15 @@ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_PROFILE_BRANCHES mov r1, rINST, lsr #12 @ r1<- B ubfx r0, rINST, #8, #4 @ r0<- A GET_VREG r3, r1 @ r3<- vB GET_VREG r2, r0 @ r2<- vA FETCH_S rINST, 1 @ rINST<- branch offset, in code units cmp r2, r3 @ compare (vA, vB) - b${revcmp} .L_${opcode}_not_taken + mov${revcmp} rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -21,28 +22,10 @@ bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif adds r2, rINST, rINST @ convert to bytes, check sign ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction -.L_${opcode}_not_taken: - FETCH_ADVANCE_INST 2 @ update rPC, load rINST - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#else - mov r1, rINST, lsr #12 @ r1<- B - ubfx r0, rINST, #8, #4 @ r0<- A - GET_VREG r3, r1 @ r3<- vB - GET_VREG r2, r0 @ r2<- vA - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - FETCH_S rINST, 1 @ rINST<- branch offset, in code units - cmp r2, r3 @ compare (vA, vB) - mov${revcmp} rINST, #2 @ rINST<- BYTE branch dist for not-taken - adds r2, rINST, rINST @ convert to bytes, check sign - FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST - bmi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#endif diff --git a/runtime/interpreter/mterp/arm/zcmp.S b/runtime/interpreter/mterp/arm/zcmp.S index 800804d95e..3d7dec006d 100644 --- a/runtime/interpreter/mterp/arm/zcmp.S +++ b/runtime/interpreter/mterp/arm/zcmp.S @@ -6,13 +6,14 @@ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES mov r0, rINST, lsr #8 @ r0<- AA GET_VREG r2, r0 @ r2<- vAA FETCH_S rINST, 1 @ rINST<- branch offset, in code units ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] cmp r2, #0 @ compare (vA, 0) - b${revcmp} .L_${opcode}_not_taken + mov${revcmp} rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -20,25 +21,9 @@ bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif adds r1, rINST, rINST @ convert to bytes & set flags FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction -.L_${opcode}_not_taken: - FETCH_ADVANCE_INST 2 @ update rPC, load rINST - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#else - mov r0, rINST, lsr #8 @ r0<- AA - GET_VREG r2, r0 @ r2<- vAA - FETCH_S rINST, 1 @ rINST<- branch offset, in code units - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - cmp r2, #0 @ compare (vA, 0) - mov${revcmp} rINST, #2 @ rINST<- inst branch dist for not-taken - adds r1, rINST, rINST @ convert to bytes & set flags - FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST - bmi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#endif diff --git a/runtime/interpreter/mterp/arm64/bincmp.S b/runtime/interpreter/mterp/arm64/bincmp.S index ed850fc49d..2356ecbb89 100644 --- a/runtime/interpreter/mterp/arm64/bincmp.S +++ b/runtime/interpreter/mterp/arm64/bincmp.S @@ -6,43 +6,26 @@ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_PROFILE_BRANCHES lsr w1, wINST, #12 // w1<- B ubfx w0, wINST, #8, #4 // w0<- A GET_VREG w3, w1 // w3<- vB GET_VREG w2, w0 // w2<- vA - FETCH_S wINST, 1 // wINST<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Offset if branch not taken cmp w2, w3 // compare (vA, vB) - b.${condition} .L_${opcode}_taken - FETCH_ADVANCE_INST 2 // update rPC, load wINST - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -.L_${opcode}_taken: + csel wINST, w1, w0, ${condition} // Branch if true, stashing result in callee save reg. +#if MTERP_PROFILE_BRANCHES + // TUINING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME sbfm x2, xINST, 0, 31 // Sign extend branch offset bl MterpProfileBranch // (self, shadow_frame, offset) cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST +#endif ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] adds w2, wINST, wINST // convert to bytes, check sign FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction -#else - lsr w1, wINST, #12 // w1<- B - ubfx w0, wINST, #8, #4 // w0<- A - GET_VREG w3, w1 // w3<- vB - GET_VREG w2, w0 // w2<- vA - FETCH_S w1, 1 // w1<- branch offset, in code units - mov w0, #2 // Offset if branch not taken - cmp w2, w3 // compare (vA, vB) - csel wINST, w1, w0, ${condition} // Branch if true, stashing result in callee save reg. - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] - adds w2, wINST, wINST // convert to bytes, check sign - FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST - b.mi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -#endif diff --git a/runtime/interpreter/mterp/arm64/zcmp.S b/runtime/interpreter/mterp/arm64/zcmp.S index e528d9f030..3f1e1b180f 100644 --- a/runtime/interpreter/mterp/arm64/zcmp.S +++ b/runtime/interpreter/mterp/arm64/zcmp.S @@ -6,39 +6,24 @@ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES lsr w0, wINST, #8 // w0<- AA GET_VREG w2, w0 // w2<- vAA - FETCH_S wINST, 1 // w1<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Branch offset if not taken cmp w2, #0 // compare (vA, 0) - b.${condition} .L_${opcode}_taken - FETCH_ADVANCE_INST 2 // update rPC, load wINST - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -.L_${opcode}_taken: + csel wINST, w1, w0, ${condition} // Branch if true, stashing result in callee save reg +#if MTERP_PROFILE_BRANCHES + // TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME sbfm x2, xINST, 0, 31 bl MterpProfileBranch // (self, shadow_frame, offset) cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST +#endif ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] adds w2, wINST, wINST // convert to bytes & set flags FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction -#else - lsr w0, wINST, #8 // w0<- AA - GET_VREG w2, w0 // w2<- vAA - FETCH_S w1, 1 // w1<- branch offset, in code units - mov w0, #2 // Branch offset if not taken - cmp w2, #0 // compare (vA, 0) - csel wINST, w1, w0, ${condition} // Branch if true, stashing result in callee save reg - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] - adds w2, wINST, wINST // convert to bytes & set flags - FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST - b.mi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -#endif diff --git a/runtime/interpreter/mterp/out/mterp_arm.S b/runtime/interpreter/mterp/out/mterp_arm.S index 94cbd2d10e..2b74d4c86e 100644 --- a/runtime/interpreter/mterp/out/mterp_arm.S +++ b/runtime/interpreter/mterp/out/mterp_arm.S @@ -1534,14 +1534,15 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_PROFILE_BRANCHES mov r1, rINST, lsr #12 @ r1<- B ubfx r0, rINST, #8, #4 @ r0<- A GET_VREG r3, r1 @ r3<- vB GET_VREG r2, r0 @ r2<- vA FETCH_S rINST, 1 @ rINST<- branch offset, in code units cmp r2, r3 @ compare (vA, vB) - bne .L_op_if_eq_not_taken + movne rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -1549,31 +1550,13 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif adds r2, rINST, rINST @ convert to bytes, check sign ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction -.L_op_if_eq_not_taken: - FETCH_ADVANCE_INST 2 @ update rPC, load rINST - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#else - mov r1, rINST, lsr #12 @ r1<- B - ubfx r0, rINST, #8, #4 @ r0<- A - GET_VREG r3, r1 @ r3<- vB - GET_VREG r2, r0 @ r2<- vA - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - FETCH_S rINST, 1 @ rINST<- branch offset, in code units - cmp r2, r3 @ compare (vA, vB) - movne rINST, #2 @ rINST<- BYTE branch dist for not-taken - adds r2, rINST, rINST @ convert to bytes, check sign - FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST - bmi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#endif /* ------------------------------ */ @@ -1589,14 +1572,15 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_PROFILE_BRANCHES mov r1, rINST, lsr #12 @ r1<- B ubfx r0, rINST, #8, #4 @ r0<- A GET_VREG r3, r1 @ r3<- vB GET_VREG r2, r0 @ r2<- vA FETCH_S rINST, 1 @ rINST<- branch offset, in code units cmp r2, r3 @ compare (vA, vB) - beq .L_op_if_ne_not_taken + moveq rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -1604,31 +1588,13 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif adds r2, rINST, rINST @ convert to bytes, check sign ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction -.L_op_if_ne_not_taken: - FETCH_ADVANCE_INST 2 @ update rPC, load rINST - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#else - mov r1, rINST, lsr #12 @ r1<- B - ubfx r0, rINST, #8, #4 @ r0<- A - GET_VREG r3, r1 @ r3<- vB - GET_VREG r2, r0 @ r2<- vA - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - FETCH_S rINST, 1 @ rINST<- branch offset, in code units - cmp r2, r3 @ compare (vA, vB) - moveq rINST, #2 @ rINST<- BYTE branch dist for not-taken - adds r2, rINST, rINST @ convert to bytes, check sign - FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST - bmi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#endif /* ------------------------------ */ @@ -1644,14 +1610,15 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_PROFILE_BRANCHES mov r1, rINST, lsr #12 @ r1<- B ubfx r0, rINST, #8, #4 @ r0<- A GET_VREG r3, r1 @ r3<- vB GET_VREG r2, r0 @ r2<- vA FETCH_S rINST, 1 @ rINST<- branch offset, in code units cmp r2, r3 @ compare (vA, vB) - bge .L_op_if_lt_not_taken + movge rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -1659,31 +1626,13 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif adds r2, rINST, rINST @ convert to bytes, check sign ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction -.L_op_if_lt_not_taken: - FETCH_ADVANCE_INST 2 @ update rPC, load rINST - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#else - mov r1, rINST, lsr #12 @ r1<- B - ubfx r0, rINST, #8, #4 @ r0<- A - GET_VREG r3, r1 @ r3<- vB - GET_VREG r2, r0 @ r2<- vA - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - FETCH_S rINST, 1 @ rINST<- branch offset, in code units - cmp r2, r3 @ compare (vA, vB) - movge rINST, #2 @ rINST<- BYTE branch dist for not-taken - adds r2, rINST, rINST @ convert to bytes, check sign - FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST - bmi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#endif /* ------------------------------ */ @@ -1699,14 +1648,15 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_PROFILE_BRANCHES mov r1, rINST, lsr #12 @ r1<- B ubfx r0, rINST, #8, #4 @ r0<- A GET_VREG r3, r1 @ r3<- vB GET_VREG r2, r0 @ r2<- vA FETCH_S rINST, 1 @ rINST<- branch offset, in code units cmp r2, r3 @ compare (vA, vB) - blt .L_op_if_ge_not_taken + movlt rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -1714,31 +1664,13 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif adds r2, rINST, rINST @ convert to bytes, check sign ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction -.L_op_if_ge_not_taken: - FETCH_ADVANCE_INST 2 @ update rPC, load rINST - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#else - mov r1, rINST, lsr #12 @ r1<- B - ubfx r0, rINST, #8, #4 @ r0<- A - GET_VREG r3, r1 @ r3<- vB - GET_VREG r2, r0 @ r2<- vA - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - FETCH_S rINST, 1 @ rINST<- branch offset, in code units - cmp r2, r3 @ compare (vA, vB) - movlt rINST, #2 @ rINST<- BYTE branch dist for not-taken - adds r2, rINST, rINST @ convert to bytes, check sign - FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST - bmi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#endif /* ------------------------------ */ @@ -1754,14 +1686,15 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_PROFILE_BRANCHES mov r1, rINST, lsr #12 @ r1<- B ubfx r0, rINST, #8, #4 @ r0<- A GET_VREG r3, r1 @ r3<- vB GET_VREG r2, r0 @ r2<- vA FETCH_S rINST, 1 @ rINST<- branch offset, in code units cmp r2, r3 @ compare (vA, vB) - ble .L_op_if_gt_not_taken + movle rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -1769,31 +1702,13 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif adds r2, rINST, rINST @ convert to bytes, check sign ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction -.L_op_if_gt_not_taken: - FETCH_ADVANCE_INST 2 @ update rPC, load rINST - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#else - mov r1, rINST, lsr #12 @ r1<- B - ubfx r0, rINST, #8, #4 @ r0<- A - GET_VREG r3, r1 @ r3<- vB - GET_VREG r2, r0 @ r2<- vA - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - FETCH_S rINST, 1 @ rINST<- branch offset, in code units - cmp r2, r3 @ compare (vA, vB) - movle rINST, #2 @ rINST<- BYTE branch dist for not-taken - adds r2, rINST, rINST @ convert to bytes, check sign - FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST - bmi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#endif /* ------------------------------ */ @@ -1809,14 +1724,15 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_PROFILE_BRANCHES mov r1, rINST, lsr #12 @ r1<- B ubfx r0, rINST, #8, #4 @ r0<- A GET_VREG r3, r1 @ r3<- vB GET_VREG r2, r0 @ r2<- vA FETCH_S rINST, 1 @ rINST<- branch offset, in code units cmp r2, r3 @ compare (vA, vB) - bgt .L_op_if_le_not_taken + movgt rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -1824,31 +1740,13 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif adds r2, rINST, rINST @ convert to bytes, check sign ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction -.L_op_if_le_not_taken: - FETCH_ADVANCE_INST 2 @ update rPC, load rINST - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#else - mov r1, rINST, lsr #12 @ r1<- B - ubfx r0, rINST, #8, #4 @ r0<- A - GET_VREG r3, r1 @ r3<- vB - GET_VREG r2, r0 @ r2<- vA - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - FETCH_S rINST, 1 @ rINST<- branch offset, in code units - cmp r2, r3 @ compare (vA, vB) - movgt rINST, #2 @ rINST<- BYTE branch dist for not-taken - adds r2, rINST, rINST @ convert to bytes, check sign - FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST - bmi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#endif /* ------------------------------ */ @@ -1864,13 +1762,14 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES mov r0, rINST, lsr #8 @ r0<- AA GET_VREG r2, r0 @ r2<- vAA FETCH_S rINST, 1 @ rINST<- branch offset, in code units ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] cmp r2, #0 @ compare (vA, 0) - bne .L_op_if_eqz_not_taken + movne rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -1878,28 +1777,12 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif adds r1, rINST, rINST @ convert to bytes & set flags FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction -.L_op_if_eqz_not_taken: - FETCH_ADVANCE_INST 2 @ update rPC, load rINST - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#else - mov r0, rINST, lsr #8 @ r0<- AA - GET_VREG r2, r0 @ r2<- vAA - FETCH_S rINST, 1 @ rINST<- branch offset, in code units - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - cmp r2, #0 @ compare (vA, 0) - movne rINST, #2 @ rINST<- inst branch dist for not-taken - adds r1, rINST, rINST @ convert to bytes & set flags - FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST - bmi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#endif /* ------------------------------ */ @@ -1915,13 +1798,14 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES mov r0, rINST, lsr #8 @ r0<- AA GET_VREG r2, r0 @ r2<- vAA FETCH_S rINST, 1 @ rINST<- branch offset, in code units ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] cmp r2, #0 @ compare (vA, 0) - beq .L_op_if_nez_not_taken + moveq rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -1929,28 +1813,12 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif adds r1, rINST, rINST @ convert to bytes & set flags FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction -.L_op_if_nez_not_taken: - FETCH_ADVANCE_INST 2 @ update rPC, load rINST - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#else - mov r0, rINST, lsr #8 @ r0<- AA - GET_VREG r2, r0 @ r2<- vAA - FETCH_S rINST, 1 @ rINST<- branch offset, in code units - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - cmp r2, #0 @ compare (vA, 0) - moveq rINST, #2 @ rINST<- inst branch dist for not-taken - adds r1, rINST, rINST @ convert to bytes & set flags - FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST - bmi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#endif /* ------------------------------ */ @@ -1966,13 +1834,14 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES mov r0, rINST, lsr #8 @ r0<- AA GET_VREG r2, r0 @ r2<- vAA FETCH_S rINST, 1 @ rINST<- branch offset, in code units ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] cmp r2, #0 @ compare (vA, 0) - bge .L_op_if_ltz_not_taken + movge rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -1980,28 +1849,12 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif adds r1, rINST, rINST @ convert to bytes & set flags FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction -.L_op_if_ltz_not_taken: - FETCH_ADVANCE_INST 2 @ update rPC, load rINST - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#else - mov r0, rINST, lsr #8 @ r0<- AA - GET_VREG r2, r0 @ r2<- vAA - FETCH_S rINST, 1 @ rINST<- branch offset, in code units - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - cmp r2, #0 @ compare (vA, 0) - movge rINST, #2 @ rINST<- inst branch dist for not-taken - adds r1, rINST, rINST @ convert to bytes & set flags - FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST - bmi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#endif /* ------------------------------ */ @@ -2017,13 +1870,14 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES mov r0, rINST, lsr #8 @ r0<- AA GET_VREG r2, r0 @ r2<- vAA FETCH_S rINST, 1 @ rINST<- branch offset, in code units ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] cmp r2, #0 @ compare (vA, 0) - blt .L_op_if_gez_not_taken + movlt rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -2031,28 +1885,12 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif adds r1, rINST, rINST @ convert to bytes & set flags FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction -.L_op_if_gez_not_taken: - FETCH_ADVANCE_INST 2 @ update rPC, load rINST - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#else - mov r0, rINST, lsr #8 @ r0<- AA - GET_VREG r2, r0 @ r2<- vAA - FETCH_S rINST, 1 @ rINST<- branch offset, in code units - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - cmp r2, #0 @ compare (vA, 0) - movlt rINST, #2 @ rINST<- inst branch dist for not-taken - adds r1, rINST, rINST @ convert to bytes & set flags - FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST - bmi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#endif /* ------------------------------ */ @@ -2068,13 +1906,14 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES mov r0, rINST, lsr #8 @ r0<- AA GET_VREG r2, r0 @ r2<- vAA FETCH_S rINST, 1 @ rINST<- branch offset, in code units ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] cmp r2, #0 @ compare (vA, 0) - ble .L_op_if_gtz_not_taken + movle rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -2082,28 +1921,12 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif adds r1, rINST, rINST @ convert to bytes & set flags FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction -.L_op_if_gtz_not_taken: - FETCH_ADVANCE_INST 2 @ update rPC, load rINST - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#else - mov r0, rINST, lsr #8 @ r0<- AA - GET_VREG r2, r0 @ r2<- vAA - FETCH_S rINST, 1 @ rINST<- branch offset, in code units - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - cmp r2, #0 @ compare (vA, 0) - movle rINST, #2 @ rINST<- inst branch dist for not-taken - adds r1, rINST, rINST @ convert to bytes & set flags - FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST - bmi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#endif /* ------------------------------ */ @@ -2119,13 +1942,14 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES mov r0, rINST, lsr #8 @ r0<- AA GET_VREG r2, r0 @ r2<- vAA FETCH_S rINST, 1 @ rINST<- branch offset, in code units ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] cmp r2, #0 @ compare (vA, 0) - bgt .L_op_if_lez_not_taken + movgt rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -2133,28 +1957,12 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif adds r1, rINST, rINST @ convert to bytes & set flags FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction -.L_op_if_lez_not_taken: - FETCH_ADVANCE_INST 2 @ update rPC, load rINST - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#else - mov r0, rINST, lsr #8 @ r0<- AA - GET_VREG r2, r0 @ r2<- vAA - FETCH_S rINST, 1 @ rINST<- branch offset, in code units - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - cmp r2, #0 @ compare (vA, 0) - movgt rINST, #2 @ rINST<- inst branch dist for not-taken - adds r1, rINST, rINST @ convert to bytes & set flags - FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST - bmi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip @ extract opcode from rINST - GOTO_OPCODE ip @ jump to next instruction -#endif /* ------------------------------ */ diff --git a/runtime/interpreter/mterp/out/mterp_arm64.S b/runtime/interpreter/mterp/out/mterp_arm64.S index e4825f0489..c7c0fb5b79 100644 --- a/runtime/interpreter/mterp/out/mterp_arm64.S +++ b/runtime/interpreter/mterp/out/mterp_arm64.S @@ -1372,46 +1372,29 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_PROFILE_BRANCHES lsr w1, wINST, #12 // w1<- B ubfx w0, wINST, #8, #4 // w0<- A GET_VREG w3, w1 // w3<- vB GET_VREG w2, w0 // w2<- vA - FETCH_S wINST, 1 // wINST<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Offset if branch not taken cmp w2, w3 // compare (vA, vB) - b.eq .L_op_if_eq_taken - FETCH_ADVANCE_INST 2 // update rPC, load wINST - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -.L_op_if_eq_taken: + csel wINST, w1, w0, eq // Branch if true, stashing result in callee save reg. +#if MTERP_PROFILE_BRANCHES + // TUINING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME sbfm x2, xINST, 0, 31 // Sign extend branch offset bl MterpProfileBranch // (self, shadow_frame, offset) cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST +#endif ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] adds w2, wINST, wINST // convert to bytes, check sign FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction -#else - lsr w1, wINST, #12 // w1<- B - ubfx w0, wINST, #8, #4 // w0<- A - GET_VREG w3, w1 // w3<- vB - GET_VREG w2, w0 // w2<- vA - FETCH_S w1, 1 // w1<- branch offset, in code units - mov w0, #2 // Offset if branch not taken - cmp w2, w3 // compare (vA, vB) - csel wINST, w1, w0, eq // Branch if true, stashing result in callee save reg. - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] - adds w2, wINST, wINST // convert to bytes, check sign - FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST - b.mi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ @@ -1427,46 +1410,29 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_PROFILE_BRANCHES lsr w1, wINST, #12 // w1<- B ubfx w0, wINST, #8, #4 // w0<- A GET_VREG w3, w1 // w3<- vB GET_VREG w2, w0 // w2<- vA - FETCH_S wINST, 1 // wINST<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Offset if branch not taken cmp w2, w3 // compare (vA, vB) - b.ne .L_op_if_ne_taken - FETCH_ADVANCE_INST 2 // update rPC, load wINST - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -.L_op_if_ne_taken: + csel wINST, w1, w0, ne // Branch if true, stashing result in callee save reg. +#if MTERP_PROFILE_BRANCHES + // TUINING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME sbfm x2, xINST, 0, 31 // Sign extend branch offset bl MterpProfileBranch // (self, shadow_frame, offset) cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST +#endif ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] adds w2, wINST, wINST // convert to bytes, check sign FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction -#else - lsr w1, wINST, #12 // w1<- B - ubfx w0, wINST, #8, #4 // w0<- A - GET_VREG w3, w1 // w3<- vB - GET_VREG w2, w0 // w2<- vA - FETCH_S w1, 1 // w1<- branch offset, in code units - mov w0, #2 // Offset if branch not taken - cmp w2, w3 // compare (vA, vB) - csel wINST, w1, w0, ne // Branch if true, stashing result in callee save reg. - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] - adds w2, wINST, wINST // convert to bytes, check sign - FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST - b.mi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ @@ -1482,46 +1448,29 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_PROFILE_BRANCHES lsr w1, wINST, #12 // w1<- B ubfx w0, wINST, #8, #4 // w0<- A GET_VREG w3, w1 // w3<- vB GET_VREG w2, w0 // w2<- vA - FETCH_S wINST, 1 // wINST<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Offset if branch not taken cmp w2, w3 // compare (vA, vB) - b.lt .L_op_if_lt_taken - FETCH_ADVANCE_INST 2 // update rPC, load wINST - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -.L_op_if_lt_taken: + csel wINST, w1, w0, lt // Branch if true, stashing result in callee save reg. +#if MTERP_PROFILE_BRANCHES + // TUINING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME sbfm x2, xINST, 0, 31 // Sign extend branch offset bl MterpProfileBranch // (self, shadow_frame, offset) cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST +#endif ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] adds w2, wINST, wINST // convert to bytes, check sign FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction -#else - lsr w1, wINST, #12 // w1<- B - ubfx w0, wINST, #8, #4 // w0<- A - GET_VREG w3, w1 // w3<- vB - GET_VREG w2, w0 // w2<- vA - FETCH_S w1, 1 // w1<- branch offset, in code units - mov w0, #2 // Offset if branch not taken - cmp w2, w3 // compare (vA, vB) - csel wINST, w1, w0, lt // Branch if true, stashing result in callee save reg. - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] - adds w2, wINST, wINST // convert to bytes, check sign - FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST - b.mi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ @@ -1537,46 +1486,29 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_PROFILE_BRANCHES lsr w1, wINST, #12 // w1<- B ubfx w0, wINST, #8, #4 // w0<- A GET_VREG w3, w1 // w3<- vB GET_VREG w2, w0 // w2<- vA - FETCH_S wINST, 1 // wINST<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Offset if branch not taken cmp w2, w3 // compare (vA, vB) - b.ge .L_op_if_ge_taken - FETCH_ADVANCE_INST 2 // update rPC, load wINST - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -.L_op_if_ge_taken: + csel wINST, w1, w0, ge // Branch if true, stashing result in callee save reg. +#if MTERP_PROFILE_BRANCHES + // TUINING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME sbfm x2, xINST, 0, 31 // Sign extend branch offset bl MterpProfileBranch // (self, shadow_frame, offset) cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST +#endif ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] adds w2, wINST, wINST // convert to bytes, check sign FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction -#else - lsr w1, wINST, #12 // w1<- B - ubfx w0, wINST, #8, #4 // w0<- A - GET_VREG w3, w1 // w3<- vB - GET_VREG w2, w0 // w2<- vA - FETCH_S w1, 1 // w1<- branch offset, in code units - mov w0, #2 // Offset if branch not taken - cmp w2, w3 // compare (vA, vB) - csel wINST, w1, w0, ge // Branch if true, stashing result in callee save reg. - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] - adds w2, wINST, wINST // convert to bytes, check sign - FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST - b.mi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ @@ -1592,46 +1524,29 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_PROFILE_BRANCHES lsr w1, wINST, #12 // w1<- B ubfx w0, wINST, #8, #4 // w0<- A GET_VREG w3, w1 // w3<- vB GET_VREG w2, w0 // w2<- vA - FETCH_S wINST, 1 // wINST<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Offset if branch not taken cmp w2, w3 // compare (vA, vB) - b.gt .L_op_if_gt_taken - FETCH_ADVANCE_INST 2 // update rPC, load wINST - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -.L_op_if_gt_taken: + csel wINST, w1, w0, gt // Branch if true, stashing result in callee save reg. +#if MTERP_PROFILE_BRANCHES + // TUINING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME sbfm x2, xINST, 0, 31 // Sign extend branch offset bl MterpProfileBranch // (self, shadow_frame, offset) cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST +#endif ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] adds w2, wINST, wINST // convert to bytes, check sign FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction -#else - lsr w1, wINST, #12 // w1<- B - ubfx w0, wINST, #8, #4 // w0<- A - GET_VREG w3, w1 // w3<- vB - GET_VREG w2, w0 // w2<- vA - FETCH_S w1, 1 // w1<- branch offset, in code units - mov w0, #2 // Offset if branch not taken - cmp w2, w3 // compare (vA, vB) - csel wINST, w1, w0, gt // Branch if true, stashing result in callee save reg. - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] - adds w2, wINST, wINST // convert to bytes, check sign - FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST - b.mi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ @@ -1647,46 +1562,29 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_PROFILE_BRANCHES lsr w1, wINST, #12 // w1<- B ubfx w0, wINST, #8, #4 // w0<- A GET_VREG w3, w1 // w3<- vB GET_VREG w2, w0 // w2<- vA - FETCH_S wINST, 1 // wINST<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Offset if branch not taken cmp w2, w3 // compare (vA, vB) - b.le .L_op_if_le_taken - FETCH_ADVANCE_INST 2 // update rPC, load wINST - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -.L_op_if_le_taken: + csel wINST, w1, w0, le // Branch if true, stashing result in callee save reg. +#if MTERP_PROFILE_BRANCHES + // TUINING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME sbfm x2, xINST, 0, 31 // Sign extend branch offset bl MterpProfileBranch // (self, shadow_frame, offset) cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST +#endif ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] adds w2, wINST, wINST // convert to bytes, check sign FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction -#else - lsr w1, wINST, #12 // w1<- B - ubfx w0, wINST, #8, #4 // w0<- A - GET_VREG w3, w1 // w3<- vB - GET_VREG w2, w0 // w2<- vA - FETCH_S w1, 1 // w1<- branch offset, in code units - mov w0, #2 // Offset if branch not taken - cmp w2, w3 // compare (vA, vB) - csel wINST, w1, w0, le // Branch if true, stashing result in callee save reg. - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] - adds w2, wINST, wINST // convert to bytes, check sign - FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST - b.mi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ @@ -1702,42 +1600,27 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES lsr w0, wINST, #8 // w0<- AA GET_VREG w2, w0 // w2<- vAA - FETCH_S wINST, 1 // w1<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Branch offset if not taken cmp w2, #0 // compare (vA, 0) - b.eq .L_op_if_eqz_taken - FETCH_ADVANCE_INST 2 // update rPC, load wINST - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -.L_op_if_eqz_taken: + csel wINST, w1, w0, eq // Branch if true, stashing result in callee save reg +#if MTERP_PROFILE_BRANCHES + // TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME sbfm x2, xINST, 0, 31 bl MterpProfileBranch // (self, shadow_frame, offset) cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST +#endif ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] adds w2, wINST, wINST // convert to bytes & set flags FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction -#else - lsr w0, wINST, #8 // w0<- AA - GET_VREG w2, w0 // w2<- vAA - FETCH_S w1, 1 // w1<- branch offset, in code units - mov w0, #2 // Branch offset if not taken - cmp w2, #0 // compare (vA, 0) - csel wINST, w1, w0, eq // Branch if true, stashing result in callee save reg - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] - adds w2, wINST, wINST // convert to bytes & set flags - FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST - b.mi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ @@ -1753,42 +1636,27 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES lsr w0, wINST, #8 // w0<- AA GET_VREG w2, w0 // w2<- vAA - FETCH_S wINST, 1 // w1<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Branch offset if not taken cmp w2, #0 // compare (vA, 0) - b.ne .L_op_if_nez_taken - FETCH_ADVANCE_INST 2 // update rPC, load wINST - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -.L_op_if_nez_taken: + csel wINST, w1, w0, ne // Branch if true, stashing result in callee save reg +#if MTERP_PROFILE_BRANCHES + // TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME sbfm x2, xINST, 0, 31 bl MterpProfileBranch // (self, shadow_frame, offset) cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST +#endif ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] adds w2, wINST, wINST // convert to bytes & set flags FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction -#else - lsr w0, wINST, #8 // w0<- AA - GET_VREG w2, w0 // w2<- vAA - FETCH_S w1, 1 // w1<- branch offset, in code units - mov w0, #2 // Branch offset if not taken - cmp w2, #0 // compare (vA, 0) - csel wINST, w1, w0, ne // Branch if true, stashing result in callee save reg - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] - adds w2, wINST, wINST // convert to bytes & set flags - FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST - b.mi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ @@ -1804,42 +1672,27 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES lsr w0, wINST, #8 // w0<- AA GET_VREG w2, w0 // w2<- vAA - FETCH_S wINST, 1 // w1<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Branch offset if not taken cmp w2, #0 // compare (vA, 0) - b.lt .L_op_if_ltz_taken - FETCH_ADVANCE_INST 2 // update rPC, load wINST - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -.L_op_if_ltz_taken: + csel wINST, w1, w0, lt // Branch if true, stashing result in callee save reg +#if MTERP_PROFILE_BRANCHES + // TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME sbfm x2, xINST, 0, 31 bl MterpProfileBranch // (self, shadow_frame, offset) cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST +#endif ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] adds w2, wINST, wINST // convert to bytes & set flags FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction -#else - lsr w0, wINST, #8 // w0<- AA - GET_VREG w2, w0 // w2<- vAA - FETCH_S w1, 1 // w1<- branch offset, in code units - mov w0, #2 // Branch offset if not taken - cmp w2, #0 // compare (vA, 0) - csel wINST, w1, w0, lt // Branch if true, stashing result in callee save reg - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] - adds w2, wINST, wINST // convert to bytes & set flags - FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST - b.mi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ @@ -1855,42 +1708,27 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES lsr w0, wINST, #8 // w0<- AA GET_VREG w2, w0 // w2<- vAA - FETCH_S wINST, 1 // w1<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Branch offset if not taken cmp w2, #0 // compare (vA, 0) - b.ge .L_op_if_gez_taken - FETCH_ADVANCE_INST 2 // update rPC, load wINST - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -.L_op_if_gez_taken: + csel wINST, w1, w0, ge // Branch if true, stashing result in callee save reg +#if MTERP_PROFILE_BRANCHES + // TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME sbfm x2, xINST, 0, 31 bl MterpProfileBranch // (self, shadow_frame, offset) cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST +#endif ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] adds w2, wINST, wINST // convert to bytes & set flags FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction -#else - lsr w0, wINST, #8 // w0<- AA - GET_VREG w2, w0 // w2<- vAA - FETCH_S w1, 1 // w1<- branch offset, in code units - mov w0, #2 // Branch offset if not taken - cmp w2, #0 // compare (vA, 0) - csel wINST, w1, w0, ge // Branch if true, stashing result in callee save reg - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] - adds w2, wINST, wINST // convert to bytes & set flags - FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST - b.mi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ @@ -1906,42 +1744,27 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES lsr w0, wINST, #8 // w0<- AA GET_VREG w2, w0 // w2<- vAA - FETCH_S wINST, 1 // w1<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Branch offset if not taken cmp w2, #0 // compare (vA, 0) - b.gt .L_op_if_gtz_taken - FETCH_ADVANCE_INST 2 // update rPC, load wINST - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -.L_op_if_gtz_taken: + csel wINST, w1, w0, gt // Branch if true, stashing result in callee save reg +#if MTERP_PROFILE_BRANCHES + // TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME sbfm x2, xINST, 0, 31 bl MterpProfileBranch // (self, shadow_frame, offset) cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST +#endif ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] adds w2, wINST, wINST // convert to bytes & set flags FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction -#else - lsr w0, wINST, #8 // w0<- AA - GET_VREG w2, w0 // w2<- vAA - FETCH_S w1, 1 // w1<- branch offset, in code units - mov w0, #2 // Branch offset if not taken - cmp w2, #0 // compare (vA, 0) - csel wINST, w1, w0, gt // Branch if true, stashing result in callee save reg - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] - adds w2, wINST, wINST // convert to bytes & set flags - FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST - b.mi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ @@ -1957,42 +1780,27 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES lsr w0, wINST, #8 // w0<- AA GET_VREG w2, w0 // w2<- vAA - FETCH_S wINST, 1 // w1<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Branch offset if not taken cmp w2, #0 // compare (vA, 0) - b.le .L_op_if_lez_taken - FETCH_ADVANCE_INST 2 // update rPC, load wINST - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -.L_op_if_lez_taken: + csel wINST, w1, w0, le // Branch if true, stashing result in callee save reg +#if MTERP_PROFILE_BRANCHES + // TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME sbfm x2, xINST, 0, 31 bl MterpProfileBranch // (self, shadow_frame, offset) cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST +#endif ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] adds w2, wINST, wINST // convert to bytes & set flags FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction -#else - lsr w0, wINST, #8 // w0<- AA - GET_VREG w2, w0 // w2<- vAA - FETCH_S w1, 1 // w1<- branch offset, in code units - mov w0, #2 // Branch offset if not taken - cmp w2, #0 // compare (vA, 0) - csel wINST, w1, w0, le // Branch if true, stashing result in callee save reg - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] - adds w2, wINST, wINST // convert to bytes & set flags - FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST - b.mi MterpCheckSuspendAndContinue - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ diff --git a/runtime/interpreter/mterp/out/mterp_x86.S b/runtime/interpreter/mterp/out/mterp_x86.S index b05360b6ae..567550f41f 100644 --- a/runtime/interpreter/mterp/out/mterp_x86.S +++ b/runtime/interpreter/mterp/out/mterp_x86.S @@ -2989,8 +2989,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeVirtual) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT /* * Handle a virtual method call. @@ -3022,8 +3027,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeSuper) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT /* * Handle a "super" method call. @@ -3055,8 +3065,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeDirect) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT /* ------------------------------ */ @@ -3081,8 +3096,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeStatic) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT @@ -3108,8 +3128,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeInterface) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT /* * Handle an interface method call. @@ -3155,8 +3180,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeVirtualRange) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT /* ------------------------------ */ @@ -3181,8 +3211,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeSuperRange) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT /* ------------------------------ */ @@ -3207,8 +3242,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeDirectRange) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT /* ------------------------------ */ @@ -3233,8 +3273,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeStaticRange) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT /* ------------------------------ */ @@ -3259,8 +3304,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeInterfaceRange) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT /* ------------------------------ */ @@ -6002,8 +6052,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeVirtualQuick) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT /* ------------------------------ */ @@ -6028,8 +6083,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeVirtualQuickRange) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT /* ------------------------------ */ @@ -12851,13 +12911,17 @@ MterpException: call SYMBOL(MterpHandleException) testb %al, %al jz MterpExceptionReturn - REFRESH_IBASE movl OFF_FP_CODE_ITEM(rFP), %eax movl OFF_FP_DEX_PC(rFP), %ecx lea CODEITEM_INSNS_OFFSET(%eax), rPC lea (rPC, %ecx, 2), rPC movl rPC, OFF_FP_DEX_PC_PTR(rFP) + /* Do we need to switch interpreters? */ + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback /* resume execution at catch block */ + REFRESH_IBASE FETCH_INST GOTO_NEXT /* NOTE: no fallthrough */ diff --git a/runtime/interpreter/mterp/x86/footer.S b/runtime/interpreter/mterp/x86/footer.S index c67491e577..64d72d7709 100644 --- a/runtime/interpreter/mterp/x86/footer.S +++ b/runtime/interpreter/mterp/x86/footer.S @@ -115,13 +115,17 @@ MterpException: call SYMBOL(MterpHandleException) testb %al, %al jz MterpExceptionReturn - REFRESH_IBASE movl OFF_FP_CODE_ITEM(rFP), %eax movl OFF_FP_DEX_PC(rFP), %ecx lea CODEITEM_INSNS_OFFSET(%eax), rPC lea (rPC, %ecx, 2), rPC movl rPC, OFF_FP_DEX_PC_PTR(rFP) + /* Do we need to switch interpreters? */ + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback /* resume execution at catch block */ + REFRESH_IBASE FETCH_INST GOTO_NEXT /* NOTE: no fallthrough */ diff --git a/runtime/interpreter/mterp/x86/invoke.S b/runtime/interpreter/mterp/x86/invoke.S index bbd88cf40b..c23053becb 100644 --- a/runtime/interpreter/mterp/x86/invoke.S +++ b/runtime/interpreter/mterp/x86/invoke.S @@ -16,5 +16,10 @@ call SYMBOL($helper) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index bdc7ee2428..3e66ce20eb 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -63,7 +63,8 @@ void Jit::DumpInfo(std::ostream& os) { << "JIT data cache size=" << PrettySize(code_cache_->DataCacheSize()) << "\n" << "JIT current capacity=" << PrettySize(code_cache_->GetCurrentCapacity()) << "\n" << "JIT number of compiled code=" << code_cache_->NumberOfCompiledCode() << "\n" - << "JIT total number of compilations=" << code_cache_->NumberOfCompilations() << "\n"; + << "JIT total number of compilations=" << code_cache_->NumberOfCompilations() << "\n" + << "JIT total number of osr compilations=" << code_cache_->NumberOfOsrCompilations() << "\n"; cumulative_timings_.Dump(os); } @@ -369,8 +370,7 @@ bool Jit::MaybeDoOnStackReplacement(Thread* thread, continue; } - DCHECK(location == DexRegisterLocation::Kind::kInStack) - << DexRegisterLocation::PrettyDescriptor(location); + DCHECK_EQ(location, DexRegisterLocation::Kind::kInStack); int32_t vreg_value = shadow_frame->GetVReg(vreg); int32_t slot_offset = vreg_map.GetStackOffsetInBytes(vreg, diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 6348ddad58..8858b486f9 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -126,7 +126,10 @@ JitCodeCache::JitCodeCache(MemMap* code_map, has_done_full_collection_(false), last_update_time_ns_(0), garbage_collect_code_(garbage_collect_code), - number_of_compilations_(0) { + used_memory_for_data_(0), + used_memory_for_code_(0), + number_of_compilations_(0), + number_of_osr_compilations_(0) { DCHECK_GE(max_capacity, initial_code_capacity + initial_data_capacity); code_mspace_ = create_mspace_with_base(code_map_->Begin(), code_end_, false /*locked*/); @@ -336,6 +339,7 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, MutexLock mu(self, lock_); method_code_map_.Put(code_ptr, method); if (osr) { + number_of_osr_compilations_++; osr_code_map_.Put(method, code_ptr); } else { Runtime::Current()->GetInstrumentation()->UpdateMethodsCode( @@ -364,6 +368,11 @@ size_t JitCodeCache::NumberOfCompilations() { return number_of_compilations_; } +size_t JitCodeCache::NumberOfOsrCompilations() { + MutexLock mu(Thread::Current(), lock_); + return number_of_osr_compilations_; +} + size_t JitCodeCache::CodeCacheSize() { MutexLock mu(Thread::Current(), lock_); return CodeCacheSizeLocked(); diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index e5b8e6ca17..4574edfb46 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -73,6 +73,7 @@ class JitCodeCache { // Number of compilations done throughout the lifetime of the JIT. size_t NumberOfCompilations() REQUIRES(!lock_); + size_t NumberOfOsrCompilations() REQUIRES(!lock_); bool NotifyCompilationOf(ArtMethod* method, Thread* self, bool osr) SHARED_REQUIRES(Locks::mutator_lock_) @@ -304,6 +305,7 @@ class JitCodeCache { // Number of compilations done throughout the lifetime of the JIT. size_t number_of_compilations_ GUARDED_BY(lock_); + size_t number_of_osr_compilations_ GUARDED_BY(lock_); DISALLOW_IMPLICIT_CONSTRUCTORS(JitCodeCache); }; diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 910163c787..fb91a8cdff 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -228,6 +228,10 @@ class OatFile { return End() - Begin(); } + bool Contains(const void* p) const { + return p >= Begin() && p < End(); + } + size_t BssSize() const { return BssEnd() - BssBegin(); } diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index 18cf81aa7c..ea26d58767 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -16,6 +16,8 @@ #include "oat_file_manager.h" +#define ATRACE_TAG ATRACE_TAG_DALVIK +#include <cutils/trace.h> #include <memory> #include <queue> #include <vector> @@ -386,13 +388,15 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( ScopedSuspendAll ssa("Add image space"); runtime->GetHeap()->AddSpace(image_space.get()); } - added_image_space = true; - if (runtime->GetClassLinker()->AddImageSpace(image_space.get(), - h_loader, - dex_elements, - dex_location, - /*out*/&dex_files, - /*out*/&temp_error_msg)) { + ATRACE_BEGIN(StringPrintf("Adding image space for location %s", dex_location).c_str()); + added_image_space = runtime->GetClassLinker()->AddImageSpace(image_space.get(), + h_loader, + dex_elements, + dex_location, + /*out*/&dex_files, + /*out*/&temp_error_msg); + ATRACE_END(); + if (added_image_space) { // Successfully added image space to heap, release the map so that it does not get // freed. image_space.release(); @@ -407,7 +411,6 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( ScopedSuspendAll ssa("Remove image space"); runtime->GetHeap()->RemoveSpace(image_space.get()); } - added_image_space = false; // Non-fatal, don't update error_msg. } } diff --git a/runtime/primitive.h b/runtime/primitive.h index ca42c4790c..2454a2117b 100644 --- a/runtime/primitive.h +++ b/runtime/primitive.h @@ -46,6 +46,7 @@ class Primitive { kPrimFloat, kPrimDouble, kPrimVoid, + kPrimLast = kPrimVoid }; static Type GetType(char type) { diff --git a/runtime/quick/inline_method_analyser.h b/runtime/quick/inline_method_analyser.h index 0b09a70be4..7e84b405e7 100644 --- a/runtime/quick/inline_method_analyser.h +++ b/runtime/quick/inline_method_analyser.h @@ -37,6 +37,8 @@ class MethodVerifier; enum InlineMethodOpcode : uint16_t { kIntrinsicDoubleCvt, kIntrinsicFloatCvt, + kIntrinsicFloat2Int, + kIntrinsicDouble2Long, kIntrinsicFloatIsInfinite, kIntrinsicDoubleIsInfinite, kIntrinsicFloatIsNaN, diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc index 2dfa860dcb..6317f5e401 100644 --- a/runtime/quick_exception_handler.cc +++ b/runtime/quick_exception_handler.cc @@ -204,8 +204,7 @@ static VRegKind ToVRegKind(DexRegisterLocation::Kind kind) { return VRegKind::kDoubleHiVReg; default: - LOG(FATAL) << "Unexpected vreg location " - << DexRegisterLocation::PrettyDescriptor(kind); + LOG(FATAL) << "Unexpected vreg location " << kind; UNREACHABLE(); } } @@ -456,12 +455,11 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor { } default: { LOG(FATAL) - << "Unexpected location kind" - << DexRegisterLocation::PrettyDescriptor( - vreg_map.GetLocationInternalKind(vreg, - number_of_vregs, - code_info, - encoding)); + << "Unexpected location kind " + << vreg_map.GetLocationInternalKind(vreg, + number_of_vregs, + code_info, + encoding); UNREACHABLE(); } } diff --git a/runtime/stack.cc b/runtime/stack.cc index 5faff93b97..b1f1ed61b4 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -352,12 +352,11 @@ bool StackVisitor::GetVRegFromOptimizedCode(ArtMethod* m, uint16_t vreg, VRegKin return false; default: LOG(FATAL) - << "Unexpected location kind" - << DexRegisterLocation::PrettyDescriptor( - dex_register_map.GetLocationInternalKind(vreg, - number_of_dex_registers, - code_info, - encoding)); + << "Unexpected location kind " + << dex_register_map.GetLocationInternalKind(vreg, + number_of_dex_registers, + code_info, + encoding); UNREACHABLE(); } } diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc index 5544507c06..30934360ac 100644 --- a/runtime/stack_map.cc +++ b/runtime/stack_map.cc @@ -27,6 +27,31 @@ constexpr size_t DexRegisterLocationCatalog::kNoLocationEntryIndex; constexpr uint32_t StackMap::kNoDexRegisterMap; constexpr uint32_t StackMap::kNoInlineInfo; +std::ostream& operator<<(std::ostream& stream, const DexRegisterLocation::Kind& kind) { + using Kind = DexRegisterLocation::Kind; + switch (kind) { + case Kind::kNone: + return stream << "none"; + case Kind::kInStack: + return stream << "in stack"; + case Kind::kInRegister: + return stream << "in register"; + case Kind::kInRegisterHigh: + return stream << "in register high"; + case Kind::kInFpuRegister: + return stream << "in fpu register"; + case Kind::kInFpuRegisterHigh: + return stream << "in fpu register high"; + case Kind::kConstant: + return stream << "as constant"; + case Kind::kInStackLargeOffset: + return stream << "in stack (large offset)"; + case Kind::kConstantLargeValue: + return stream << "as constant (large value)"; + } + return stream << "Kind<" << static_cast<uint32_t>(kind) << ">"; +} + DexRegisterLocation::Kind DexRegisterMap::GetLocationInternalKind( uint16_t dex_register_number, uint16_t number_of_dex_registers, @@ -97,7 +122,7 @@ static void DumpRegisterMapping(std::ostream& os, const std::string& prefix = "v", const std::string& suffix = "") { os << prefix << dex_register_num << ": " - << DexRegisterLocation::PrettyDescriptor(location.GetInternalKind()) + << location.GetInternalKind() << " (" << location.GetValue() << ")" << suffix << '\n'; } diff --git a/runtime/stack_map.h b/runtime/stack_map.h index 97eb805501..dbf23aafc3 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -110,30 +110,6 @@ class DexRegisterLocation { sizeof(Kind) == 1u, "art::DexRegisterLocation::Kind has a size different from one byte."); - static const char* PrettyDescriptor(Kind kind) { - switch (kind) { - case Kind::kNone: - return "none"; - case Kind::kInStack: - return "in stack"; - case Kind::kInRegister: - return "in register"; - case Kind::kInRegisterHigh: - return "in register high"; - case Kind::kInFpuRegister: - return "in fpu register"; - case Kind::kInFpuRegisterHigh: - return "in fpu register high"; - case Kind::kConstant: - return "as constant"; - case Kind::kInStackLargeOffset: - return "in stack (large offset)"; - case Kind::kConstantLargeValue: - return "as constant (large value)"; - } - UNREACHABLE(); - } - static bool IsShortLocationKind(Kind kind) { switch (kind) { case Kind::kInStack: @@ -149,7 +125,7 @@ class DexRegisterLocation { return false; case Kind::kNone: - LOG(FATAL) << "Unexpected location kind " << PrettyDescriptor(kind); + LOG(FATAL) << "Unexpected location kind"; } UNREACHABLE(); } @@ -215,6 +191,8 @@ class DexRegisterLocation { friend class DexRegisterLocationHashFn; }; +std::ostream& operator<<(std::ostream& stream, const DexRegisterLocation::Kind& kind); + /** * Store information on unique Dex register locations used in a method. * The information is of the form: @@ -349,7 +327,7 @@ class DexRegisterLocationCatalog { case DexRegisterLocation::Kind::kConstantLargeValue: case DexRegisterLocation::Kind::kInStackLargeOffset: case DexRegisterLocation::Kind::kNone: - LOG(FATAL) << "Unexpected location kind " << DexRegisterLocation::PrettyDescriptor(kind); + LOG(FATAL) << "Unexpected location kind " << kind; } UNREACHABLE(); } @@ -373,7 +351,7 @@ class DexRegisterLocationCatalog { case DexRegisterLocation::Kind::kConstantLargeValue: case DexRegisterLocation::Kind::kInStackLargeOffset: case DexRegisterLocation::Kind::kNone: - LOG(FATAL) << "Unexpected location kind " << DexRegisterLocation::PrettyDescriptor(kind); + LOG(FATAL) << "Unexpected location kind " << kind; } UNREACHABLE(); } @@ -515,8 +493,7 @@ class DexRegisterMap { const StackMapEncoding& enc) const { DexRegisterLocation location = GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info, enc); - DCHECK(location.GetKind() == DexRegisterLocation::Kind::kConstant) - << DexRegisterLocation::PrettyDescriptor(location.GetKind()); + DCHECK_EQ(location.GetKind(), DexRegisterLocation::Kind::kConstant); return location.GetValue(); } @@ -530,7 +507,7 @@ class DexRegisterMap { location.GetInternalKind() == DexRegisterLocation::Kind::kInRegisterHigh || location.GetInternalKind() == DexRegisterLocation::Kind::kInFpuRegister || location.GetInternalKind() == DexRegisterLocation::Kind::kInFpuRegisterHigh) - << DexRegisterLocation::PrettyDescriptor(location.GetInternalKind()); + << location.GetInternalKind(); return location.GetValue(); } diff --git a/test/004-JniTest/build b/test/004-JniTest/build new file mode 100755 index 0000000000..e8e9f31ef4 --- /dev/null +++ b/test/004-JniTest/build @@ -0,0 +1,28 @@ +#!/bin/bash +# +# Copyright 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. +# Make us exit on a failure. +# +set -e + +# Hard-wired use of experimental jack. +# TODO: fix this temporary work-around for lambdas, see b/19467889 +export USE_JACK=true +# export JACK_SERVER=false +# export JACK_REPOSITORY="${ANDROID_BUILD_TOP}/prebuilts/sdk/tools/jacks" + +# e.g. /foo/bar/jack-3.10.ALPHA.jar -> 3.10.ALPHA +# export JACK_VERSION="$(find "$JACK_REPOSITORY" -name '*ALPHA*' | sed 's/.*jack-//g' | sed 's/[.]jar//g')" +./default-build "$@" --experimental lambdas diff --git a/test/004-JniTest/expected.txt b/test/004-JniTest/expected.txt index 155c6ae5f3..f7e404d30b 100644 --- a/test/004-JniTest/expected.txt +++ b/test/004-JniTest/expected.txt @@ -55,3 +55,6 @@ Calling method AbstractInterface->JniCallSoftConflictMethod on object of type Co DefaultInterface.JniCallSoftConflictMethod Calling method ConflictInterface->JniCallConflictDefaultMethod on object of type ConcreteClass EXCEPTION OCCURED: java.lang.IncompatibleClassChangeError: Conflicting default method implementations void ConflictInterface.JniCallConflictDefaultMethod() +hi-lambda: λ +hi-default δλ +hi-default δλ diff --git a/test/004-JniTest/jni_test.cc b/test/004-JniTest/jni_test.cc index f632331fe3..2bdf8d1e71 100644 --- a/test/004-JniTest/jni_test.cc +++ b/test/004-JniTest/jni_test.cc @@ -721,3 +721,22 @@ class JniCallDefaultMethodsTest { extern "C" JNIEXPORT void JNICALL Java_Main_testCallDefaultMethods(JNIEnv* env) { JniCallDefaultMethodsTest(env).Test(); } + +static void InvokeSpecificMethod(JNIEnv* env, jobject obj, const char* method) { + jclass lambda_class = env->FindClass("LambdaInterface"); + assert(!env->ExceptionCheck()); + assert(lambda_class != nullptr); + jmethodID method_id = env->GetMethodID(lambda_class, method, "()V"); + assert(!env->ExceptionCheck()); + env->CallVoidMethod(obj, method_id); + assert(!env->ExceptionCheck()); +} + +extern "C" JNIEXPORT void JNICALL Java_Main_testInvokeLambdaDefaultMethod( + JNIEnv* e, jclass, jobject l) { + InvokeSpecificMethod(e, l, "sayHiTwice"); +} + +extern "C" JNIEXPORT void JNICALL Java_Main_testInvokeLambdaMethod(JNIEnv* e, jclass, jobject l) { + InvokeSpecificMethod(e, l, "sayHi"); +} diff --git a/test/004-JniTest/src/Main.java b/test/004-JniTest/src/Main.java index 9f4a8522e7..e0530d8d62 100644 --- a/test/004-JniTest/src/Main.java +++ b/test/004-JniTest/src/Main.java @@ -40,6 +40,10 @@ public class Main { testProxyGetMethodID(); testJniCriticalSectionAndGc(); testCallDefaultMethods(); + String lambda = "λ"; + testInvokeLambdaMethod(() -> { System.out.println("hi-lambda: " + lambda); }); + String def = "δ"; + testInvokeLambdaDefaultMethod(() -> { System.out.println("hi-default " + def + lambda); }); } private static native void testCallDefaultMethods(); @@ -255,6 +259,19 @@ public class Main { } private static native void enterJniCriticalSection(int arraySize, byte[] array0, byte[] array); + + private static native void testInvokeLambdaMethod(LambdaInterface iface); + + private static native void testInvokeLambdaDefaultMethod(LambdaInterface iface); +} + +@FunctionalInterface +interface LambdaInterface { + public void sayHi(); + public default void sayHiTwice() { + sayHi(); + sayHi(); + } } class JniCallNonvirtualTest { diff --git a/test/020-string/expected.txt b/test/020-string/expected.txt index 081fea3a41..76b8929bd7 100644 --- a/test/020-string/expected.txt +++ b/test/020-string/expected.txt @@ -5,3 +5,9 @@ Compare unicode: -65302 Got expected exception subStr is 'uick brown fox jumps over the lazy ' Indexes are: 0:-1:0:43:33:-1:18:13:13:-1:18:18:-1:13:-1:-1:-1 +Got expected exception +Got expected exception +Got expected exception +Got expected exception +Got expected exception +llo And diff --git a/test/020-string/src/Main.java b/test/020-string/src/Main.java index b876e6ad21..710808255c 100644 --- a/test/020-string/src/Main.java +++ b/test/020-string/src/Main.java @@ -25,6 +25,7 @@ public class Main { basicTest(); indexTest(); constructorTest(); + copyTest(); } public static void basicTest() { @@ -117,4 +118,48 @@ public class Main { String s14 = new String(codePoints, 1, 3); String s15 = new String(stringBuilder); } + + public static void copyTest() { + String src = new String("Hello Android"); + char[] dst = new char[7]; + char[] tmp = null; + + try { + src.getChars(2, 9, tmp, 0); + System.out.println("GLITCH: expected exception"); + } catch (NullPointerException npe) { + System.out.println("Got expected exception"); + } + + try { + src.getChars(-1, 9, dst, 0); + System.out.println("GLITCH: expected exception"); + } catch (StringIndexOutOfBoundsException sioobe) { + System.out.println("Got expected exception"); + } + + try { + src.getChars(2, 19, dst, 0); + System.out.println("GLITCH: expected exception"); + } catch (StringIndexOutOfBoundsException sioobe) { + System.out.println("Got expected exception"); + } + + try { + src.getChars(2, 1, dst, 0); + System.out.println("GLITCH: expected exception"); + } catch (StringIndexOutOfBoundsException sioobe) { + System.out.println("Got expected exception"); + } + + try { + src.getChars(2, 10, dst, 0); + System.out.println("GLITCH: expected exception"); + } catch (ArrayIndexOutOfBoundsException aioobe) { + System.out.println("Got expected exception"); + } + + src.getChars(2, 9, dst, 0); + System.out.println(new String(dst)); + } } diff --git a/test/082-inline-execute/src/Main.java b/test/082-inline-execute/src/Main.java index 5b3fa14076..93a9005fe0 100644 --- a/test/082-inline-execute/src/Main.java +++ b/test/082-inline-execute/src/Main.java @@ -1039,6 +1039,7 @@ public class Main { Assert.assertEquals(StrictMath.round(-2.9d), -3l); Assert.assertEquals(StrictMath.round(-3.0d), -3l); Assert.assertEquals(StrictMath.round(0.49999999999999994d), 0l); + Assert.assertEquals(StrictMath.round(9007199254740991.0d), 9007199254740991l); // 2^53 - 1 Assert.assertEquals(StrictMath.round(Double.NaN), (long)+0.0d); Assert.assertEquals(StrictMath.round(Long.MAX_VALUE + 1.0d), Long.MAX_VALUE); Assert.assertEquals(StrictMath.round(Long.MIN_VALUE - 1.0d), Long.MIN_VALUE); @@ -1062,6 +1063,7 @@ public class Main { Assert.assertEquals(StrictMath.round(-3.0f), -3); // 0.4999999701976776123046875 Assert.assertEquals(StrictMath.round(Float.intBitsToFloat(0x3EFFFFFF)), (int)+0.0f); + Assert.assertEquals(StrictMath.round(16777215.0f), 16777215); // 2^24 - 1 Assert.assertEquals(StrictMath.round(Float.NaN), (int)+0.0f); Assert.assertEquals(StrictMath.round(Integer.MAX_VALUE + 1.0f), Integer.MAX_VALUE); Assert.assertEquals(StrictMath.round(Integer.MIN_VALUE - 1.0f), Integer.MIN_VALUE); diff --git a/test/449-checker-bce/src/Main.java b/test/449-checker-bce/src/Main.java index 32bbc5b61d..66e1d92cc2 100644 --- a/test/449-checker-bce/src/Main.java +++ b/test/449-checker-bce/src/Main.java @@ -137,20 +137,16 @@ public class Main { /// CHECK: ArraySet /// CHECK: BoundsCheck /// CHECK: ArraySet - /// CHECK: BoundsCheck - /// CHECK: ArraySet /// CHECK-START: void Main.$opt$noinline$constantIndexing2(int[]) BCE (after) - /// CHECK-NOT: Deoptimize - /// CHECK: BoundsCheck - /// CHECK: ArraySet - /// CHECK: BoundsCheck + /// CHECK: Deoptimize + /// CHECK-NOT: BoundsCheck /// CHECK: ArraySet - /// CHECK: BoundsCheck + /// CHECK-NOT: BoundsCheck /// CHECK: ArraySet - /// CHECK: BoundsCheck + /// CHECK-NOT: BoundsCheck /// CHECK: ArraySet - /// CHECK: BoundsCheck + /// CHECK-NOT: BoundsCheck /// CHECK: ArraySet static void $opt$noinline$constantIndexing2(int[] array) { @@ -158,8 +154,7 @@ public class Main { array[2] = 1; array[3] = 1; array[4] = 1; - array[-1] = 1; // prevents the whole opt on [-1:4] - if (array[1] == 1) { + if (array[1] != 1) { throw new Error(""); } } @@ -173,8 +168,41 @@ public class Main { /// CHECK: ArraySet /// CHECK: BoundsCheck /// CHECK: ArraySet + /// CHECK: BoundsCheck + /// CHECK: ArraySet /// CHECK-START: void Main.constantIndexing2b(int[]) BCE (after) + /// CHECK-NOT: Deoptimize + /// CHECK: BoundsCheck + /// CHECK: ArraySet + /// CHECK: BoundsCheck + /// CHECK: ArraySet + /// CHECK: BoundsCheck + /// CHECK: ArraySet + /// CHECK: BoundsCheck + /// CHECK: ArraySet + /// CHECK: BoundsCheck + /// CHECK: ArraySet + + static void constantIndexing2b(int[] array) { + array[0] = 6; + array[1] = 6; + array[2] = 6; + array[3] = 6; + array[-1] = 1; // prevents the whole opt on [-1:4] + } + + /// CHECK-START: void Main.constantIndexing2c(int[]) BCE (before) + /// CHECK: BoundsCheck + /// CHECK: ArraySet + /// CHECK: BoundsCheck + /// CHECK: ArraySet + /// CHECK: BoundsCheck + /// CHECK: ArraySet + /// CHECK: BoundsCheck + /// CHECK: ArraySet + + /// CHECK-START: void Main.constantIndexing2c(int[]) BCE (after) /// CHECK: Deoptimize /// CHECK-NOT: BoundsCheck /// CHECK: ArraySet @@ -185,7 +213,7 @@ public class Main { /// CHECK-NOT: BoundsCheck /// CHECK: ArraySet - static void constantIndexing2b(int[] array) { + static void constantIndexing2c(int[] array) { array[0] = 7; array[1] = 7; array[2] = 7; @@ -440,31 +468,37 @@ public class Main { System.out.println("constant indices 1 failed!"); } + $opt$noinline$constantIndexing2(a6); + if (a6[0] != 0 || a6[1] != 1 || a6[2] != 1 || + a6[3] != 1 || a6[4] != 1 || a6[5] != 11) { + System.out.println("constant indices 2 failed!"); + } + caught = false; try { - $opt$noinline$constantIndexing2(a6); + constantIndexing2b(a6); } catch (ArrayIndexOutOfBoundsException e) { caught = true; } - if (!caught || a6[0] != 0 || a6[1] != 1 || a6[2] != 1 || - a6[3] != 1 || a6[4] != 1 || a6[5] != 11) { - System.out.println("constant indices 2 failed!"); + if (!caught || a6[0] != 6 || a6[1] != 6 || a6[2] != 6 || + a6[3] != 6 || a6[4] != 1 || a6[5] != 11) { + System.out.println("constant indices 2b failed!"); } caught = false; try { - constantIndexing2b(a1); + constantIndexing2c(a1); } catch (ArrayIndexOutOfBoundsException e) { caught = true; } if (!caught || a1[0] != 7) { - System.out.println("constant indices 2b failed!"); + System.out.println("constant indices 2c failed!"); } - constantIndexing2b(a6); + constantIndexing2c(a6); if (a6[0] != 7 || a6[1] != 7 || a6[2] != 7 || a6[3] != 7 || a6[4] != 1 || a6[5] != 11) { - System.out.println("constant indices 2b failed!"); + System.out.println("constant indices 2c failed!"); } int[] b4 = new int[4]; diff --git a/test/530-checker-loops/src/Main.java b/test/530-checker-loops/src/Main.java index 0c32491e4e..d5111b0c14 100644 --- a/test/530-checker-loops/src/Main.java +++ b/test/530-checker-loops/src/Main.java @@ -394,6 +394,34 @@ public class Main { return result; } + /// CHECK-START: int Main.linearForNEArrayLengthUp(int[]) BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.linearForNEArrayLengthUp(int[]) BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int linearForNEArrayLengthUp(int[] x) { + int result = 0; + for (int i = 0; i != x.length; i++) { + result += x[i]; + } + return result; + } + + /// CHECK-START: int Main.linearForNEArrayLengthDown(int[]) BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.linearForNEArrayLengthDown(int[]) BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int linearForNEArrayLengthDown(int[] x) { + int result = 0; + for (int i = x.length - 1; i != -1; i--) { + result += x[i]; + } + return result; + } + /// CHECK-START: int Main.linearDoWhileUp() BCE (before) /// CHECK-DAG: BoundsCheck // @@ -605,729 +633,7 @@ public class Main { } } - /// CHECK-START: void Main.bubble(int[]) BCE (before) - /// CHECK-DAG: BoundsCheck - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: void Main.bubble(int[]) BCE (after) - /// CHECK-NOT: BoundsCheck - // TODO: also CHECK-NOT: Deoptimize, see b/27151190 - private static void bubble(int[] a) { - for (int i = a.length; --i >= 0;) { - for (int j = 0; j < i; j++) { - if (a[j] > a[j+1]) { - int tmp = a[j]; - a[j] = a[j+1]; - a[j+1] = tmp; - } - } - } - } - - /// CHECK-START: int Main.periodicIdiom(int) BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.periodicIdiom(int) BCE (after) - /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize - private static int periodicIdiom(int tc) { - int[] x = { 1, 3 }; - // Loop with periodic sequence (0, 1). - int k = 0; - int result = 0; - for (int i = 0; i < tc; i++) { - result += x[k]; - k = 1 - k; - } - return result; - } - - /// CHECK-START: int Main.periodicSequence2(int) BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.periodicSequence2(int) BCE (after) - /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize - private static int periodicSequence2(int tc) { - int[] x = { 1, 3 }; - // Loop with periodic sequence (0, 1). - int k = 0; - int l = 1; - int result = 0; - for (int i = 0; i < tc; i++) { - result += x[k]; - int t = l; - l = k; - k = t; - } - return result; - } - - /// CHECK-START: int Main.periodicSequence4(int) BCE (before) - /// CHECK-DAG: BoundsCheck - /// CHECK-DAG: BoundsCheck - /// CHECK-DAG: BoundsCheck - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.periodicSequence4(int) BCE (after) - /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize - private static int periodicSequence4(int tc) { - int[] x = { 1, 3, 5, 7 }; - // Loop with periodic sequence (0, 1, 2, 3). - int k = 0; - int l = 1; - int m = 2; - int n = 3; - int result = 0; - for (int i = 0; i < tc; i++) { - result += x[k] + x[l] + x[m] + x[n]; // all used at once - int t = n; - n = k; - k = l; - l = m; - m = t; - } - return result; - } - - /// CHECK-START: int Main.justRightUp1() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.justRightUp1() BCE (after) - /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize - private static int justRightUp1() { - int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - int result = 0; - for (int i = Integer.MAX_VALUE - 10, k = 0; i < Integer.MAX_VALUE; i++) { - result += x[k++]; - } - return result; - } - - /// CHECK-START: int Main.justRightUp2() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.justRightUp2() BCE (after) - /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize - private static int justRightUp2() { - int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - int result = 0; - for (int i = Integer.MAX_VALUE - 10; i < Integer.MAX_VALUE; i++) { - result += x[i - Integer.MAX_VALUE + 10]; - } - return result; - } - - /// CHECK-START: int Main.justRightUp3() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.justRightUp3() BCE (after) - /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize - private static int justRightUp3() { - int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - int result = 0; - for (int i = Integer.MAX_VALUE - 10, k = 0; i <= Integer.MAX_VALUE - 1; i++) { - result += x[k++]; - } - return result; - } - - /// CHECK-START: int Main.justOOBUp() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.justOOBUp() BCE (after) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.justOOBUp() BCE (after) - /// CHECK-NOT: Deoptimize - private static int justOOBUp() { - int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - int result = 0; - // Infinite loop! - for (int i = Integer.MAX_VALUE - 9, k = 0; i <= Integer.MAX_VALUE; i++) { - result += x[k++]; - } - return result; - } - - /// CHECK-START: int Main.justRightDown1() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.justRightDown1() BCE (after) - /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize - private static int justRightDown1() { - int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - int result = 0; - for (int i = Integer.MIN_VALUE + 10, k = 0; i > Integer.MIN_VALUE; i--) { - result += x[k++]; - } - return result; - } - - /// CHECK-START: int Main.justRightDown2() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.justRightDown2() BCE (after) - /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize - private static int justRightDown2() { - int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - int result = 0; - for (int i = Integer.MIN_VALUE + 10; i > Integer.MIN_VALUE; i--) { - result += x[Integer.MAX_VALUE + i]; - } - return result; - } - - /// CHECK-START: int Main.justRightDown3() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.justRightDown3() BCE (after) - /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize - private static int justRightDown3() { - int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - int result = 0; - for (int i = Integer.MIN_VALUE + 10, k = 0; i >= Integer.MIN_VALUE + 1; i--) { - result += x[k++]; - } - return result; - } - - /// CHECK-START: int Main.justOOBDown() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.justOOBDown() BCE (after) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.justOOBDown() BCE (after) - /// CHECK-NOT: Deoptimize - private static int justOOBDown() { - int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - int result = 0; - // Infinite loop! - for (int i = Integer.MIN_VALUE + 9, k = 0; i >= Integer.MIN_VALUE; i--) { - result += x[k++]; - } - return result; - } - - /// CHECK-START: void Main.lowerOOB(int[]) BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: void Main.lowerOOB(int[]) BCE (after) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: void Main.lowerOOB(int[]) BCE (after) - /// CHECK-NOT: Deoptimize - private static void lowerOOB(int[] x) { - // OOB! - for (int i = -1; i < x.length; i++) { - sResult += x[i]; - } - } - - /// CHECK-START: void Main.upperOOB(int[]) BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: void Main.upperOOB(int[]) BCE (after) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: void Main.upperOOB(int[]) BCE (after) - /// CHECK-NOT: Deoptimize - private static void upperOOB(int[] x) { - // OOB! - for (int i = 0; i <= x.length; i++) { - sResult += x[i]; - } - } - - /// CHECK-START: void Main.doWhileUpOOB() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: void Main.doWhileUpOOB() BCE (after) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: void Main.doWhileUpOOB() BCE (after) - /// CHECK-NOT: Deoptimize - private static void doWhileUpOOB() { - int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - int i = 0; - // OOB! - do { - sResult += x[i++]; - } while (i <= x.length); - } - - /// CHECK-START: void Main.doWhileDownOOB() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: void Main.doWhileDownOOB() BCE (after) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: void Main.doWhileDownOOB() BCE (after) - /// CHECK-NOT: Deoptimize - private static void doWhileDownOOB() { - int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - int i = x.length - 1; - // OOB! - do { - sResult += x[i--]; - } while (-1 <= i); - } - - /// CHECK-START: void Main.hiddenOOB1(int) BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: void Main.hiddenOOB1(int) BCE (after) - /// CHECK-DAG: Deoptimize - // - /// CHECK-START: void Main.hiddenOOB1(int) BCE (after) - /// CHECK-NOT: BoundsCheck - private static void hiddenOOB1(int lo) { - int[] a = { 1 } ; - for (int i = lo; i <= 10; i++) { - // Dangerous loop where careless static range analysis would yield strict upper bound - // on index j of 5. When, for instance, lo and thus i = -2147483648, the upper bound - // becomes really positive due to arithmetic wrap-around, causing OOB. - // Dynamic BCE is feasible though, since it checks the range. - for (int j = 4; j < i - 5; j++) { - sResult += a[j - 4]; - } - } - } - - /// CHECK-START: void Main.hiddenOOB2(int) BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: void Main.hiddenOOB2(int) BCE (after) - /// CHECK-DAG: Deoptimize - // - /// CHECK-START: void Main.hiddenOOB2(int) BCE (after) - /// CHECK-NOT: BoundsCheck - private static void hiddenOOB2(int hi) { - int[] a = { 1 } ; - for (int i = 0; i < hi; i++) { - // Dangerous loop where careless static range analysis would yield strict lower bound - // on index j of 5. When, for instance, hi and thus i = 2147483647, the upper bound - // becomes really negative due to arithmetic wrap-around, causing OOB. - // Dynamic BCE is feasible though, since it checks the range. - for (int j = 6; j > i + 5; j--) { - sResult += a[j - 6]; - } - } - } - - /// CHECK-START: void Main.hiddenInfiniteOOB() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: void Main.hiddenInfiniteOOB() BCE (after) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: void Main.hiddenInfiniteOOB() BCE (after) - /// CHECK-NOT: Deoptimize - private static void hiddenInfiniteOOB() { - int[] a = { 11 } ; - for (int i = -1; i <= 0; i++) { - // Dangerous loop where careless static range analysis would yield a safe upper bound - // of -3. In reality, due to arithmetic wrap-around (when i = -1, j <= 2147483647; - // whereas when i = 0, j <= -3), this is an infinite loop that goes OOB. - for (int j = -3; j <= 2147483646 * i - 3; j++) { - sResult += a[j + 3]; - } - } - } - - /// CHECK-START: void Main.hiddenFiniteOOB() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: void Main.hiddenFiniteOOB() BCE (after) - /// CHECK-DAG: Deoptimize - // - /// CHECK-START: void Main.hiddenFiniteOOB() BCE (after) - /// CHECK-NOT: BoundsCheck - private static void hiddenFiniteOOB() { - int[] a = { 111 } ; - for (int i = -1; i <= 0; i++) { - // Dangerous loop similar as above where the loop is now finite, but the - // loop still goes out of bounds for i = -1 due to the large upper bound. - // Dynamic BCE is feasible though, since it checks the range. - for (int j = -4; j < 2147483646 * i - 3; j++) { - sResult += a[j + 4]; - } - } - } - - /// CHECK-START: int[] Main.add() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int[] Main.add() BCE (after) - /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize - private static int[] add() { - int[] a = new int[10]; - for (int i = 0; i <= 3; i++) { - for (int j = 0; j <= 6; j++) { - a[i + j] += 1; - } - } - return a; - } - - /// CHECK-START: int[] Main.multiply1() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int[] Main.multiply1() BCE (after) - /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize - private static int[] multiply1() { - int[] a = new int[10]; - try { - for (int i = 0; i <= 3; i++) { - for (int j = 0; j <= 3; j++) { - // Range [0,9]: safe. - a[i * j] += 1; - } - } - } catch (Exception e) { - a[0] += 1000; - } - return a; - } - - /// CHECK-START: int[] Main.multiply2() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int[] Main.multiply2() BCE (after) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int[] Main.multiply2() BCE (after) - /// CHECK-NOT: Deoptimize - static int[] multiply2() { - int[] a = new int[10]; - try { - for (int i = -3; i <= 3; i++) { - for (int j = -3; j <= 3; j++) { - // Range [-9,9]: unsafe. - a[i * j] += 1; - } - } - } catch (Exception e) { - a[0] += 1000; - } - return a; - } - - /// CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (before) - /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> - /// CHECK-DAG: NullCheck loop:<<Loop>> - /// CHECK-DAG: ArrayLength loop:<<Loop>> - /// CHECK-DAG: BoundsCheck loop:<<Loop>> - // - /// CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (after) - /// CHECK-DAG: ArrayGet loop:{{B\d+}} - /// CHECK-DAG: Deoptimize loop:none - // - /// CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (after) - /// CHECK-NOT: NullCheck loop:{{B\d+}} - /// CHECK-NOT: ArrayLength loop:{{B\d+}} - /// CHECK-NOT: BoundsCheck loop:{{B\d+}} - private static int linearDynamicBCE1(int[] x, int lo, int hi) { - int result = 0; - for (int i = lo; i < hi; i++) { - sResult += x[i]; - } - return result; - } - - /// CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (before) - /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> - /// CHECK-DAG: NullCheck loop:<<Loop>> - /// CHECK-DAG: ArrayLength loop:<<Loop>> - /// CHECK-DAG: BoundsCheck loop:<<Loop>> - // - /// CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (after) - /// CHECK-DAG: ArrayGet loop:{{B\d+}} - /// CHECK-DAG: Deoptimize loop:none - // - /// CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (after) - /// CHECK-NOT: NullCheck loop:{{B\d+}} - /// CHECK-NOT: ArrayLength loop:{{B\d+}} - /// CHECK-NOT: BoundsCheck loop:{{B\d+}} - private static int linearDynamicBCE2(int[] x, int lo, int hi, int offset) { - int result = 0; - for (int i = lo; i < hi; i++) { - sResult += x[offset + i]; - } - return result; - } - - /// CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (before) - /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> - /// CHECK-DAG: NullCheck loop:<<Loop>> - /// CHECK-DAG: ArrayLength loop:<<Loop>> - /// CHECK-DAG: BoundsCheck loop:<<Loop>> - // - /// CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (after) - /// CHECK-DAG: ArrayGet loop:{{B\d+}} - /// CHECK-DAG: Deoptimize loop:none - // - /// CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (after) - /// CHECK-NOT: NullCheck loop:{{B\d+}} - /// CHECK-NOT: ArrayLength loop:{{B\d+}} - /// CHECK-NOT: BoundsCheck loop:{{B\d+}} - private static int wrapAroundDynamicBCE(int[] x) { - int w = 9; - int result = 0; - for (int i = 0; i < 10; i++) { - result += x[w]; - w = i; - } - return result; - } - - /// CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (before) - /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> - /// CHECK-DAG: NullCheck loop:<<Loop>> - /// CHECK-DAG: ArrayLength loop:<<Loop>> - /// CHECK-DAG: BoundsCheck loop:<<Loop>> - // - /// CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (after) - /// CHECK-DAG: ArrayGet loop:{{B\d+}} - /// CHECK-DAG: Deoptimize loop:none - // - /// CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (after) - /// CHECK-NOT: NullCheck loop:{{B\d+}} - /// CHECK-NOT: ArrayLength loop:{{B\d+}} - /// CHECK-NOT: BoundsCheck loop:{{B\d+}} - private static int periodicDynamicBCE(int[] x) { - int k = 0; - int result = 0; - for (int i = 0; i < 10; i++) { - result += x[k]; - k = 1 - k; - } - return result; - } - - /// CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (before) - /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> - /// CHECK-DAG: NullCheck loop:<<Loop>> - /// CHECK-DAG: ArrayLength loop:<<Loop>> - /// CHECK-DAG: BoundsCheck loop:<<Loop>> - // - /// CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after) - /// CHECK-DAG: ArrayGet loop:{{B\d+}} - /// CHECK-DAG: Deoptimize loop:none - // - /// CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after) - /// CHECK-NOT: NullCheck loop:{{B\d+}} - /// CHECK-NOT: ArrayLength loop:{{B\d+}} - /// CHECK-NOT: BoundsCheck loop:{{B\d+}} - static int dynamicBCEPossiblyInfiniteLoop(int[] x, int lo, int hi) { - // This loop could be infinite for hi = max int. Since i is also used - // as subscript, however, dynamic bce can proceed. - int result = 0; - for (int i = lo; i <= hi; i++) { - result += x[i]; - } - return result; - } - - /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (before) - /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> - /// CHECK-DAG: BoundsCheck loop:<<Loop>> - // - /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after) - /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> - /// CHECK-DAG: BoundsCheck loop:<<Loop>> - // - /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after) - /// CHECK-NOT: Deoptimize - static int noDynamicBCEPossiblyInfiniteLoop(int[] x, int lo, int hi) { - // As above, but now the index is not used as subscript, - // and dynamic bce is not applied. - int result = 0; - for (int k = 0, i = lo; i <= hi; i++) { - result += x[k++]; - } - return result; - } - - /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (before) - /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> - /// CHECK-DAG: BoundsCheck loop:<<Loop>> - // - /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (after) - /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> - /// CHECK-DAG: BoundsCheck loop:<<Loop>> - // - /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (after) - /// CHECK-NOT: Deoptimize - static int noDynamicBCEMixedInductionTypes(int[] x, long lo, long hi) { - int result = 0; - // Mix of int and long induction. - int k = 0; - for (long i = lo; i < hi; i++) { - result += x[k++]; - } - return result; - } - - /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (before) - /// CHECK-DAG: BoundsCheck loop:<<InnerLoop:B\d+>> - /// CHECK-DAG: ArrayGet loop:<<InnerLoop>> - /// CHECK-DAG: If loop:<<InnerLoop>> - /// CHECK-DAG: If loop:<<OuterLoop:B\d+>> - /// CHECK-EVAL: "<<InnerLoop>>" != "<<OuterLoop>>" - // - /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (after) - /// CHECK-DAG: ArrayGet loop:<<InnerLoop:B\d+>> - /// CHECK-DAG: Deoptimize loop:<<OuterLoop:B\d+>> - /// CHECK-EVAL: "<<InnerLoop>>" != "<<OuterLoop>>" - // - /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (after) - /// CHECK-NOT: BoundsCheck - // - // No additional top tests were introduced. - /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (after) - /// CHECK-DAG: If - /// CHECK-DAG: If - /// CHECK-NOT: If - static int dynamicBCEConstantRange(int[] x) { - int result = 0; - for (int i = 2; i <= 6; i++) { - // Range analysis sees that innermost loop is finite and always taken. - for (int j = i - 2; j <= i + 2; j++) { - result += x[j]; - } - } - return result; - } - - /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (before) - /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop:B\d+>> - /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>> - /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>> - // - /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (after) - // Order matters: - /// CHECK: Deoptimize loop:<<Loop:B\d+>> - // CHECK-NOT: Goto loop:<<Loop>> - /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>> - /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>> - /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>> - /// CHECK: Goto loop:<<Loop>> - // - /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (after) - /// CHECK-DAG: Deoptimize loop:none - static int dynamicBCEAndConstantIndices(int[] x, int[][] a, int lo, int hi) { - // Deliberately test array length on a before the loop so that only bounds checks - // on constant subscripts remain, making them a viable candidate for hoisting. - if (a.length == 0) { - return -1; - } - // Loop that allows BCE on x[i]. - int result = 0; - for (int i = lo; i < hi; i++) { - result += x[i]; - if ((i % 10) != 0) { - // None of the subscripts inside a conditional are removed by dynamic bce, - // making them a candidate for deoptimization based on constant indices. - // Compiler should ensure the array loads are not subsequently hoisted - // "above" the deoptimization "barrier" on the bounds. - a[0][i] = 1; - a[1][i] = 2; - a[99][i] = 3; - } - } - return result; - } - - /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (before) - /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> - /// CHECK-DAG: ArrayGet loop:<<Loop>> - /// CHECK-DAG: ArrayGet loop:<<Loop>> - /// CHECK-DAG: ArrayGet loop:<<Loop>> - /// CHECK-DAG: ArrayGet loop:<<Loop>> - /// CHECK-DAG: ArrayGet loop:<<Loop>> - /// CHECK-DAG: ArrayGet loop:<<Loop>> - /// CHECK-DAG: ArrayGet loop:<<Loop>> - /// CHECK-DAG: ArrayGet loop:<<Loop>> - // For brevity, just test occurrence of at least one of each in the loop: - /// CHECK-DAG: NullCheck loop:<<Loop>> - /// CHECK-DAG: ArrayLength loop:<<Loop>> - /// CHECK-DAG: BoundsCheck loop:<<Loop>> - // - /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after) - /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> - /// CHECK-NOT: ArrayGet loop:<<Loop>> - // - /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after) - /// CHECK-NOT: NullCheck loop:{{B\d+}} - /// CHECK-NOT: ArrayLength loop:{{B\d+}} - /// CHECK-NOT: BoundsCheck loop:{{B\d+}} - // - /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after) - /// CHECK-DAG: Deoptimize loop:none - static int dynamicBCEAndConstantIndicesAllPrimTypes(int[] q, - boolean[] r, - byte[] s, - char[] t, - short[] u, - int[] v, - long[] w, - float[] x, - double[] y, int lo, int hi) { - int result = 0; - for (int i = lo; i < hi; i++) { - // All constant index array references can be hoisted out of the loop during BCE on q[i]. - result += q[i] + (r[0] ? 1 : 0) + (int) s[0] + (int) t[0] + (int) u[0] + (int) v[0] + - (int) w[0] + (int) x[0] + (int) y[0]; - } - return result; - } - - /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (before) - /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> - /// CHECK-DAG: NullCheck loop:<<Loop>> - /// CHECK-DAG: ArrayLength loop:<<Loop>> - /// CHECK-DAG: BoundsCheck loop:<<Loop>> - /// CHECK-DAG: ArrayGet loop:<<Loop>> - /// CHECK-DAG: NullCheck loop:<<Loop>> - /// CHECK-DAG: ArrayLength loop:<<Loop>> - /// CHECK-DAG: BoundsCheck loop:<<Loop>> - // - /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (after) - /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> - /// CHECK-DAG: Deoptimize loop:none - // - /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (after) - /// CHECK-NOT: ArrayLength loop:{{B\d+}} - /// CHECK-NOT: BoundsCheck loop:{{B\d+}} - static int dynamicBCEAndConstantIndexRefType(int[] q, Integer[] z, int lo, int hi) { - int result = 0; - for (int i = lo; i < hi; i++) { - // Similar to above, but now implicit call to intValue() may prevent hoisting - // z[0] itself during BCE on q[i]. Therefore, we just check BCE on q[i]. - result += q[i] + z[0]; - } - return result; - } - - // - // Verifier. - // - public static void main(String[] args) { - // Set to run expensive tests for correctness too. - boolean HEAVY = false; - int[] empty = { }; int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; @@ -1392,6 +698,8 @@ public class Main { // Special forms. expectEquals(55, linearForNEUp()); expectEquals(55, linearForNEDown()); + expectEquals(55, linearForNEArrayLengthUp(x)); + expectEquals(55, linearForNEArrayLengthDown(x)); expectEquals(55, linearDoWhileUp()); expectEquals(55, linearDoWhileDown()); expectEquals(55, linearShort()); @@ -1401,246 +709,6 @@ public class Main { linearTriangularOnParameter(10); linearTriangularVariationsInnerStrict(10); linearTriangularVariationsInnerNonStrict(10); - - // Sorting. - int[] sort = { 5, 4, 1, 9, 10, 2, 7, 6, 3, 8 }; - bubble(sort); - for (int i = 0; i < 10; i++) { - expectEquals(sort[i], x[i]); - } - - // Periodic adds (1, 3), one at the time. - expectEquals(0, periodicIdiom(-1)); - for (int tc = 0; tc < 32; tc++) { - int expected = (tc >> 1) << 2; - if ((tc & 1) != 0) - expected += 1; - expectEquals(expected, periodicIdiom(tc)); - } - - // Periodic adds (1, 3), one at the time. - expectEquals(0, periodicSequence2(-1)); - for (int tc = 0; tc < 32; tc++) { - int expected = (tc >> 1) << 2; - if ((tc & 1) != 0) - expected += 1; - expectEquals(expected, periodicSequence2(tc)); - } - - // Periodic adds (1, 3, 5, 7), all at once. - expectEquals(0, periodicSequence4(-1)); - for (int tc = 0; tc < 32; tc++) { - expectEquals(tc * 16, periodicSequence4(tc)); - } - - // Large bounds. - expectEquals(55, justRightUp1()); - expectEquals(55, justRightUp2()); - expectEquals(55, justRightUp3()); - expectEquals(55, justRightDown1()); - expectEquals(55, justRightDown2()); - expectEquals(55, justRightDown3()); - sResult = 0; - try { - justOOBUp(); - } catch (ArrayIndexOutOfBoundsException e) { - sResult = 1; - } - expectEquals(1, sResult); - sResult = 0; - try { - justOOBDown(); - } catch (ArrayIndexOutOfBoundsException e) { - sResult = 1; - } - expectEquals(1, sResult); - - // Lower bound goes OOB. - sResult = 0; - try { - lowerOOB(x); - } catch (ArrayIndexOutOfBoundsException e) { - sResult += 1000; - } - expectEquals(1000, sResult); - - // Upper bound goes OOB. - sResult = 0; - try { - upperOOB(x); - } catch (ArrayIndexOutOfBoundsException e) { - sResult += 1000; - } - expectEquals(1055, sResult); - - // Do while up goes OOB. - sResult = 0; - try { - doWhileUpOOB(); - } catch (ArrayIndexOutOfBoundsException e) { - sResult += 1000; - } - expectEquals(1055, sResult); - - // Do while down goes OOB. - sResult = 0; - try { - doWhileDownOOB(); - } catch (ArrayIndexOutOfBoundsException e) { - sResult += 1000; - } - expectEquals(1055, sResult); - - // Hidden OOB. - sResult = 0; - try { - hiddenOOB1(10); // no OOB - } catch (ArrayIndexOutOfBoundsException e) { - sResult += 1000; - } - expectEquals(1, sResult); - sResult = 0; - try { - hiddenOOB1(-2147483648); // OOB - } catch (ArrayIndexOutOfBoundsException e) { - sResult += 1000; - } - expectEquals(1001, sResult); - sResult = 0; - try { - hiddenOOB2(1); // no OOB - } catch (ArrayIndexOutOfBoundsException e) { - sResult += 1000; - } - expectEquals(1, sResult); - if (HEAVY) { - sResult = 0; - try { - hiddenOOB2(2147483647); // OOB - } catch (ArrayIndexOutOfBoundsException e) { - sResult += 1000; - } - expectEquals(1002, sResult); - } - sResult = 0; - try { - hiddenInfiniteOOB(); - } catch (ArrayIndexOutOfBoundsException e) { - sResult += 1000; - } - expectEquals(1011, sResult); - sResult = 0; - try { - hiddenFiniteOOB(); - } catch (ArrayIndexOutOfBoundsException e) { - sResult += 1000; - } - expectEquals(1111, sResult); - - // Addition. - { - int[] e1 ={ 1, 2, 3, 4, 4, 4, 4, 3, 2, 1 }; - int[] a1 = add(); - for (int i = 0; i < 10; i++) { - expectEquals(a1[i], e1[i]); - } - } - - // Multiplication. - { - int[] e1 = { 7, 1, 2, 2, 1, 0, 2, 0, 0, 1 }; - int[] a1 = multiply1(); - for (int i = 0; i < 10; i++) { - expectEquals(a1[i], e1[i]); - } - int[] e2 = { 1001, 0, 0, 1, 0, 0, 1, 0, 0, 1 }; - int[] a2 = multiply2(); - for (int i = 0; i < 10; i++) { - expectEquals(a2[i], e2[i]); - } - } - - // Dynamic BCE. - sResult = 0; - try { - linearDynamicBCE1(x, -1, x.length); - } catch (ArrayIndexOutOfBoundsException e) { - sResult += 1000; - } - expectEquals(1000, sResult); - sResult = 0; - linearDynamicBCE1(x, 0, x.length); - expectEquals(55, sResult); - sResult = 0; - try { - linearDynamicBCE1(x, 0, x.length + 1); - } catch (ArrayIndexOutOfBoundsException e) { - sResult += 1000; - } - expectEquals(1055, sResult); - - // Dynamic BCE with offset. - sResult = 0; - try { - linearDynamicBCE2(x, 0, x.length, -1); - } catch (ArrayIndexOutOfBoundsException e) { - sResult += 1000; - } - expectEquals(1000, sResult); - sResult = 0; - linearDynamicBCE2(x, 0, x.length, 0); - expectEquals(55, sResult); - sResult = 0; - try { - linearDynamicBCE2(x, 0, x.length, 1); - } catch (ArrayIndexOutOfBoundsException e) { - sResult += 1000; - } - expectEquals(1054, sResult); - - // Dynamic BCE candidates. - expectEquals(55, wrapAroundDynamicBCE(x)); - expectEquals(15, periodicDynamicBCE(x)); - expectEquals(55, dynamicBCEPossiblyInfiniteLoop(x, 0, 9)); - expectEquals(55, noDynamicBCEPossiblyInfiniteLoop(x, 0, 9)); - expectEquals(55, noDynamicBCEMixedInductionTypes(x, 0, 10)); - expectEquals(125, dynamicBCEConstantRange(x)); - - // Dynamic BCE combined with constant indices. - int[][] a; - a = new int[0][0]; - expectEquals(-1, dynamicBCEAndConstantIndices(x, a, 0, 10)); - a = new int[100][10]; - expectEquals(55, dynamicBCEAndConstantIndices(x, a, 0, 10)); - for (int i = 0; i < 10; i++) { - expectEquals((i % 10) != 0 ? 1 : 0, a[0][i]); - expectEquals((i % 10) != 0 ? 2 : 0, a[1][i]); - expectEquals((i % 10) != 0 ? 3 : 0, a[99][i]); - } - a = new int[2][10]; - sResult = 0; - try { - expectEquals(55, dynamicBCEAndConstantIndices(x, a, 0, 10)); - } catch (ArrayIndexOutOfBoundsException e) { - sResult = 1; - } - expectEquals(1, sResult); - expectEquals(a[0][1], 1); - expectEquals(a[1][1], 2); - - // Dynamic BCE combined with constant indices of all types. - boolean[] x1 = { true }; - byte[] x2 = { 2 }; - char[] x3 = { 3 }; - short[] x4 = { 4 }; - int[] x5 = { 5 }; - long[] x6 = { 6 }; - float[] x7 = { 7 }; - double[] x8 = { 8 }; - expectEquals(415, - dynamicBCEAndConstantIndicesAllPrimTypes(x, x1, x2, x3, x4, x5, x6, x7, x8, 0, 10)); - Integer[] x9 = { 9 }; - expectEquals(145, dynamicBCEAndConstantIndexRefType(x, x9, 0, 10)); } private static void expectEquals(int expected, int result) { diff --git a/test/530-checker-loops2/expected.txt b/test/530-checker-loops2/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/530-checker-loops2/expected.txt diff --git a/test/530-checker-loops2/info.txt b/test/530-checker-loops2/info.txt new file mode 100644 index 0000000000..f5d334d011 --- /dev/null +++ b/test/530-checker-loops2/info.txt @@ -0,0 +1 @@ +Test on loop optimizations. diff --git a/test/530-checker-loops2/src/Main.java b/test/530-checker-loops2/src/Main.java new file mode 100644 index 0000000000..64be1a2be4 --- /dev/null +++ b/test/530-checker-loops2/src/Main.java @@ -0,0 +1,999 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// Test on loop optimizations. +// +public class Main { + + static int sResult; + + // + // Various sequence variables used in bound checks. + // + + /// CHECK-START: void Main.bubble(int[]) BCE (before) + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.bubble(int[]) BCE (after) + /// CHECK-NOT: BoundsCheck + // TODO: also CHECK-NOT: Deoptimize, see b/27151190 + private static void bubble(int[] a) { + for (int i = a.length; --i >= 0;) { + for (int j = 0; j < i; j++) { + if (a[j] > a[j+1]) { + int tmp = a[j]; + a[j] = a[j+1]; + a[j+1] = tmp; + } + } + } + } + + /// CHECK-START: int Main.periodicIdiom(int) BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.periodicIdiom(int) BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int periodicIdiom(int tc) { + int[] x = { 1, 3 }; + // Loop with periodic sequence (0, 1). + int k = 0; + int result = 0; + for (int i = 0; i < tc; i++) { + result += x[k]; + k = 1 - k; + } + return result; + } + + /// CHECK-START: int Main.periodicSequence2(int) BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.periodicSequence2(int) BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int periodicSequence2(int tc) { + int[] x = { 1, 3 }; + // Loop with periodic sequence (0, 1). + int k = 0; + int l = 1; + int result = 0; + for (int i = 0; i < tc; i++) { + result += x[k]; + int t = l; + l = k; + k = t; + } + return result; + } + + /// CHECK-START: int Main.periodicSequence4(int) BCE (before) + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.periodicSequence4(int) BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int periodicSequence4(int tc) { + int[] x = { 1, 3, 5, 7 }; + // Loop with periodic sequence (0, 1, 2, 3). + int k = 0; + int l = 1; + int m = 2; + int n = 3; + int result = 0; + for (int i = 0; i < tc; i++) { + result += x[k] + x[l] + x[m] + x[n]; // all used at once + int t = n; + n = k; + k = l; + l = m; + m = t; + } + return result; + } + + /// CHECK-START: int Main.justRightUp1() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.justRightUp1() BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int justRightUp1() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int result = 0; + for (int i = Integer.MAX_VALUE - 10, k = 0; i < Integer.MAX_VALUE; i++) { + result += x[k++]; + } + return result; + } + + /// CHECK-START: int Main.justRightUp2() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.justRightUp2() BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int justRightUp2() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int result = 0; + for (int i = Integer.MAX_VALUE - 10; i < Integer.MAX_VALUE; i++) { + result += x[i - Integer.MAX_VALUE + 10]; + } + return result; + } + + /// CHECK-START: int Main.justRightUp3() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.justRightUp3() BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int justRightUp3() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int result = 0; + for (int i = Integer.MAX_VALUE - 10, k = 0; i <= Integer.MAX_VALUE - 1; i++) { + result += x[k++]; + } + return result; + } + + /// CHECK-START: int Main.justOOBUp() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.justOOBUp() BCE (after) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.justOOBUp() BCE (after) + /// CHECK-NOT: Deoptimize + private static int justOOBUp() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int result = 0; + // Infinite loop! + for (int i = Integer.MAX_VALUE - 9, k = 0; i <= Integer.MAX_VALUE; i++) { + result += x[k++]; + } + return result; + } + + /// CHECK-START: int Main.justRightDown1() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.justRightDown1() BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int justRightDown1() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int result = 0; + for (int i = Integer.MIN_VALUE + 10, k = 0; i > Integer.MIN_VALUE; i--) { + result += x[k++]; + } + return result; + } + + /// CHECK-START: int Main.justRightDown2() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.justRightDown2() BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int justRightDown2() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int result = 0; + for (int i = Integer.MIN_VALUE + 10; i > Integer.MIN_VALUE; i--) { + result += x[Integer.MAX_VALUE + i]; + } + return result; + } + + /// CHECK-START: int Main.justRightDown3() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.justRightDown3() BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int justRightDown3() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int result = 0; + for (int i = Integer.MIN_VALUE + 10, k = 0; i >= Integer.MIN_VALUE + 1; i--) { + result += x[k++]; + } + return result; + } + + /// CHECK-START: int Main.justOOBDown() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.justOOBDown() BCE (after) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.justOOBDown() BCE (after) + /// CHECK-NOT: Deoptimize + private static int justOOBDown() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int result = 0; + // Infinite loop! + for (int i = Integer.MIN_VALUE + 9, k = 0; i >= Integer.MIN_VALUE; i--) { + result += x[k++]; + } + return result; + } + + /// CHECK-START: void Main.lowerOOB(int[]) BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.lowerOOB(int[]) BCE (after) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.lowerOOB(int[]) BCE (after) + /// CHECK-NOT: Deoptimize + private static void lowerOOB(int[] x) { + // OOB! + for (int i = -1; i < x.length; i++) { + sResult += x[i]; + } + } + + /// CHECK-START: void Main.upperOOB(int[]) BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.upperOOB(int[]) BCE (after) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.upperOOB(int[]) BCE (after) + /// CHECK-NOT: Deoptimize + private static void upperOOB(int[] x) { + // OOB! + for (int i = 0; i <= x.length; i++) { + sResult += x[i]; + } + } + + /// CHECK-START: void Main.doWhileUpOOB() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.doWhileUpOOB() BCE (after) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.doWhileUpOOB() BCE (after) + /// CHECK-NOT: Deoptimize + private static void doWhileUpOOB() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int i = 0; + // OOB! + do { + sResult += x[i++]; + } while (i <= x.length); + } + + /// CHECK-START: void Main.doWhileDownOOB() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.doWhileDownOOB() BCE (after) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.doWhileDownOOB() BCE (after) + /// CHECK-NOT: Deoptimize + private static void doWhileDownOOB() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int i = x.length - 1; + // OOB! + do { + sResult += x[i--]; + } while (-1 <= i); + } + + /// CHECK-START: void Main.hiddenOOB1(int) BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.hiddenOOB1(int) BCE (after) + /// CHECK-DAG: Deoptimize + // + /// CHECK-START: void Main.hiddenOOB1(int) BCE (after) + /// CHECK-NOT: BoundsCheck + private static void hiddenOOB1(int lo) { + int[] a = { 1 } ; + for (int i = lo; i <= 10; i++) { + // Dangerous loop where careless static range analysis would yield strict upper bound + // on index j of 5. When, for instance, lo and thus i = -2147483648, the upper bound + // becomes really positive due to arithmetic wrap-around, causing OOB. + // Dynamic BCE is feasible though, since it checks the range. + for (int j = 4; j < i - 5; j++) { + sResult += a[j - 4]; + } + } + } + + /// CHECK-START: void Main.hiddenOOB2(int) BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.hiddenOOB2(int) BCE (after) + /// CHECK-DAG: Deoptimize + // + /// CHECK-START: void Main.hiddenOOB2(int) BCE (after) + /// CHECK-NOT: BoundsCheck + private static void hiddenOOB2(int hi) { + int[] a = { 1 } ; + for (int i = 0; i < hi; i++) { + // Dangerous loop where careless static range analysis would yield strict lower bound + // on index j of 5. When, for instance, hi and thus i = 2147483647, the upper bound + // becomes really negative due to arithmetic wrap-around, causing OOB. + // Dynamic BCE is feasible though, since it checks the range. + for (int j = 6; j > i + 5; j--) { + sResult += a[j - 6]; + } + } + } + + /// CHECK-START: void Main.hiddenInfiniteOOB() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.hiddenInfiniteOOB() BCE (after) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.hiddenInfiniteOOB() BCE (after) + /// CHECK-NOT: Deoptimize + private static void hiddenInfiniteOOB() { + int[] a = { 11 } ; + for (int i = -1; i <= 0; i++) { + // Dangerous loop where careless static range analysis would yield a safe upper bound + // of -3. In reality, due to arithmetic wrap-around (when i = -1, j <= 2147483647; + // whereas when i = 0, j <= -3), this is an infinite loop that goes OOB. + for (int j = -3; j <= 2147483646 * i - 3; j++) { + sResult += a[j + 3]; + } + } + } + + /// CHECK-START: void Main.hiddenFiniteOOB() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.hiddenFiniteOOB() BCE (after) + /// CHECK-DAG: Deoptimize + // + /// CHECK-START: void Main.hiddenFiniteOOB() BCE (after) + /// CHECK-NOT: BoundsCheck + private static void hiddenFiniteOOB() { + int[] a = { 111 } ; + for (int i = -1; i <= 0; i++) { + // Dangerous loop similar as above where the loop is now finite, but the + // loop still goes out of bounds for i = -1 due to the large upper bound. + // Dynamic BCE is feasible though, since it checks the range. + for (int j = -4; j < 2147483646 * i - 3; j++) { + sResult += a[j + 4]; + } + } + } + + /// CHECK-START: int[] Main.add() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int[] Main.add() BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int[] add() { + int[] a = new int[10]; + for (int i = 0; i <= 3; i++) { + for (int j = 0; j <= 6; j++) { + a[i + j] += 1; + } + } + return a; + } + + /// CHECK-START: int[] Main.multiply1() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int[] Main.multiply1() BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int[] multiply1() { + int[] a = new int[10]; + try { + for (int i = 0; i <= 3; i++) { + for (int j = 0; j <= 3; j++) { + // Range [0,9]: safe. + a[i * j] += 1; + } + } + } catch (Exception e) { + a[0] += 1000; + } + return a; + } + + /// CHECK-START: int[] Main.multiply2() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int[] Main.multiply2() BCE (after) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int[] Main.multiply2() BCE (after) + /// CHECK-NOT: Deoptimize + static int[] multiply2() { + int[] a = new int[10]; + try { + for (int i = -3; i <= 3; i++) { + for (int j = -3; j <= 3; j++) { + // Range [-9,9]: unsafe. + a[i * j] += 1; + } + } + } catch (Exception e) { + a[0] += 1000; + } + return a; + } + + /// CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (before) + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: NullCheck loop:<<Loop>> + /// CHECK-DAG: ArrayLength loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + // + /// CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (after) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: Deoptimize loop:none + // + /// CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (after) + /// CHECK-NOT: NullCheck loop:{{B\d+}} + /// CHECK-NOT: ArrayLength loop:{{B\d+}} + /// CHECK-NOT: BoundsCheck loop:{{B\d+}} + private static int linearDynamicBCE1(int[] x, int lo, int hi) { + int result = 0; + for (int i = lo; i < hi; i++) { + sResult += x[i]; + } + return result; + } + + /// CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (before) + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: NullCheck loop:<<Loop>> + /// CHECK-DAG: ArrayLength loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + // + /// CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (after) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: Deoptimize loop:none + // + /// CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (after) + /// CHECK-NOT: NullCheck loop:{{B\d+}} + /// CHECK-NOT: ArrayLength loop:{{B\d+}} + /// CHECK-NOT: BoundsCheck loop:{{B\d+}} + private static int linearDynamicBCE2(int[] x, int lo, int hi, int offset) { + int result = 0; + for (int i = lo; i < hi; i++) { + sResult += x[offset + i]; + } + return result; + } + + /// CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (before) + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: NullCheck loop:<<Loop>> + /// CHECK-DAG: ArrayLength loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + // + /// CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (after) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: Deoptimize loop:none + // + /// CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (after) + /// CHECK-NOT: NullCheck loop:{{B\d+}} + /// CHECK-NOT: ArrayLength loop:{{B\d+}} + /// CHECK-NOT: BoundsCheck loop:{{B\d+}} + private static int wrapAroundDynamicBCE(int[] x) { + int w = 9; + int result = 0; + for (int i = 0; i < 10; i++) { + result += x[w]; + w = i; + } + return result; + } + + /// CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (before) + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: NullCheck loop:<<Loop>> + /// CHECK-DAG: ArrayLength loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + // + /// CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (after) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: Deoptimize loop:none + // + /// CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (after) + /// CHECK-NOT: NullCheck loop:{{B\d+}} + /// CHECK-NOT: ArrayLength loop:{{B\d+}} + /// CHECK-NOT: BoundsCheck loop:{{B\d+}} + private static int periodicDynamicBCE(int[] x) { + int k = 0; + int result = 0; + for (int i = 0; i < 10; i++) { + result += x[k]; + k = 1 - k; + } + return result; + } + + /// CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (before) + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: NullCheck loop:<<Loop>> + /// CHECK-DAG: ArrayLength loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + // + /// CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: Deoptimize loop:none + // + /// CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after) + /// CHECK-NOT: NullCheck loop:{{B\d+}} + /// CHECK-NOT: ArrayLength loop:{{B\d+}} + /// CHECK-NOT: BoundsCheck loop:{{B\d+}} + static int dynamicBCEPossiblyInfiniteLoop(int[] x, int lo, int hi) { + // This loop could be infinite for hi = max int. Since i is also used + // as subscript, however, dynamic bce can proceed. + int result = 0; + for (int i = lo; i <= hi; i++) { + result += x[i]; + } + return result; + } + + /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (before) + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + // + /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after) + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + // + /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after) + /// CHECK-NOT: Deoptimize + static int noDynamicBCEPossiblyInfiniteLoop(int[] x, int lo, int hi) { + // As above, but now the index is not used as subscript, + // and dynamic bce is not applied. + int result = 0; + for (int k = 0, i = lo; i <= hi; i++) { + result += x[k++]; + } + return result; + } + + /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (before) + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + // + /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (after) + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + // + /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (after) + /// CHECK-NOT: Deoptimize + static int noDynamicBCEMixedInductionTypes(int[] x, long lo, long hi) { + int result = 0; + // Mix of int and long induction. + int k = 0; + for (long i = lo; i < hi; i++) { + result += x[k++]; + } + return result; + } + + /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (before) + /// CHECK-DAG: BoundsCheck loop:<<InnerLoop:B\d+>> + /// CHECK-DAG: ArrayGet loop:<<InnerLoop>> + /// CHECK-DAG: If loop:<<InnerLoop>> + /// CHECK-DAG: If loop:<<OuterLoop:B\d+>> + /// CHECK-EVAL: "<<InnerLoop>>" != "<<OuterLoop>>" + // + /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (after) + /// CHECK-DAG: ArrayGet loop:<<InnerLoop:B\d+>> + /// CHECK-DAG: Deoptimize loop:<<OuterLoop:B\d+>> + /// CHECK-EVAL: "<<InnerLoop>>" != "<<OuterLoop>>" + // + /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (after) + /// CHECK-NOT: BoundsCheck + // + // No additional top tests were introduced. + /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (after) + /// CHECK-DAG: If + /// CHECK-DAG: If + /// CHECK-NOT: If + static int dynamicBCEConstantRange(int[] x) { + int result = 0; + for (int i = 2; i <= 6; i++) { + // Range analysis sees that innermost loop is finite and always taken. + for (int j = i - 2; j <= i + 2; j++) { + result += x[j]; + } + } + return result; + } + + /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (before) + /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>> + /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>> + // + /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (after) + // Order matters: + /// CHECK: Deoptimize loop:<<Loop:B\d+>> + // CHECK-NOT: Goto loop:<<Loop>> + /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>> + /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>> + /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>> + /// CHECK: Goto loop:<<Loop>> + // + /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (after) + /// CHECK-DAG: Deoptimize loop:none + static int dynamicBCEAndConstantIndices(int[] x, int[][] a, int lo, int hi) { + // Deliberately test array length on a before the loop so that only bounds checks + // on constant subscripts remain, making them a viable candidate for hoisting. + if (a.length == 0) { + return -1; + } + // Loop that allows BCE on x[i]. + int result = 0; + for (int i = lo; i < hi; i++) { + result += x[i]; + if ((i % 10) != 0) { + // None of the subscripts inside a conditional are removed by dynamic bce, + // making them a candidate for deoptimization based on constant indices. + // Compiler should ensure the array loads are not subsequently hoisted + // "above" the deoptimization "barrier" on the bounds. + a[0][i] = 1; + a[1][i] = 2; + a[99][i] = 3; + } + } + return result; + } + + /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (before) + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: ArrayGet loop:<<Loop>> + /// CHECK-DAG: ArrayGet loop:<<Loop>> + /// CHECK-DAG: ArrayGet loop:<<Loop>> + /// CHECK-DAG: ArrayGet loop:<<Loop>> + /// CHECK-DAG: ArrayGet loop:<<Loop>> + /// CHECK-DAG: ArrayGet loop:<<Loop>> + /// CHECK-DAG: ArrayGet loop:<<Loop>> + /// CHECK-DAG: ArrayGet loop:<<Loop>> + // For brevity, just test occurrence of at least one of each in the loop: + /// CHECK-DAG: NullCheck loop:<<Loop>> + /// CHECK-DAG: ArrayLength loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + // + /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after) + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-NOT: ArrayGet loop:<<Loop>> + // + /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after) + /// CHECK-NOT: NullCheck loop:{{B\d+}} + /// CHECK-NOT: ArrayLength loop:{{B\d+}} + /// CHECK-NOT: BoundsCheck loop:{{B\d+}} + // + /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after) + /// CHECK-DAG: Deoptimize loop:none + static int dynamicBCEAndConstantIndicesAllPrimTypes(int[] q, + boolean[] r, + byte[] s, + char[] t, + short[] u, + int[] v, + long[] w, + float[] x, + double[] y, int lo, int hi) { + int result = 0; + for (int i = lo; i < hi; i++) { + // All constant index array references can be hoisted out of the loop during BCE on q[i]. + result += q[i] + (r[0] ? 1 : 0) + (int) s[0] + (int) t[0] + (int) u[0] + (int) v[0] + + (int) w[0] + (int) x[0] + (int) y[0]; + } + return result; + } + + /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (before) + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: NullCheck loop:<<Loop>> + /// CHECK-DAG: ArrayLength loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + /// CHECK-DAG: ArrayGet loop:<<Loop>> + /// CHECK-DAG: NullCheck loop:<<Loop>> + /// CHECK-DAG: ArrayLength loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + // + /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (after) + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: Deoptimize loop:none + // + /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (after) + /// CHECK-NOT: ArrayLength loop:{{B\d+}} + /// CHECK-NOT: BoundsCheck loop:{{B\d+}} + static int dynamicBCEAndConstantIndexRefType(int[] q, Integer[] z, int lo, int hi) { + int result = 0; + for (int i = lo; i < hi; i++) { + // Similar to above, but now implicit call to intValue() may prevent hoisting + // z[0] itself during BCE on q[i]. Therefore, we just check BCE on q[i]. + result += q[i] + z[0]; + } + return result; + } + + // + // Verifier. + // + + public static void main(String[] args) { + // Set to run expensive tests for correctness too. + boolean HEAVY = false; + + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + // Sorting. + int[] sort = { 5, 4, 1, 9, 10, 2, 7, 6, 3, 8 }; + bubble(sort); + for (int i = 0; i < 10; i++) { + expectEquals(sort[i], x[i]); + } + + // Periodic adds (1, 3), one at the time. + expectEquals(0, periodicIdiom(-1)); + for (int tc = 0; tc < 32; tc++) { + int expected = (tc >> 1) << 2; + if ((tc & 1) != 0) + expected += 1; + expectEquals(expected, periodicIdiom(tc)); + } + + // Periodic adds (1, 3), one at the time. + expectEquals(0, periodicSequence2(-1)); + for (int tc = 0; tc < 32; tc++) { + int expected = (tc >> 1) << 2; + if ((tc & 1) != 0) + expected += 1; + expectEquals(expected, periodicSequence2(tc)); + } + + // Periodic adds (1, 3, 5, 7), all at once. + expectEquals(0, periodicSequence4(-1)); + for (int tc = 0; tc < 32; tc++) { + expectEquals(tc * 16, periodicSequence4(tc)); + } + + // Large bounds. + expectEquals(55, justRightUp1()); + expectEquals(55, justRightUp2()); + expectEquals(55, justRightUp3()); + expectEquals(55, justRightDown1()); + expectEquals(55, justRightDown2()); + expectEquals(55, justRightDown3()); + sResult = 0; + try { + justOOBUp(); + } catch (ArrayIndexOutOfBoundsException e) { + sResult = 1; + } + expectEquals(1, sResult); + sResult = 0; + try { + justOOBDown(); + } catch (ArrayIndexOutOfBoundsException e) { + sResult = 1; + } + expectEquals(1, sResult); + + // Lower bound goes OOB. + sResult = 0; + try { + lowerOOB(x); + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1000, sResult); + + // Upper bound goes OOB. + sResult = 0; + try { + upperOOB(x); + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1055, sResult); + + // Do while up goes OOB. + sResult = 0; + try { + doWhileUpOOB(); + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1055, sResult); + + // Do while down goes OOB. + sResult = 0; + try { + doWhileDownOOB(); + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1055, sResult); + + // Hidden OOB. + sResult = 0; + try { + hiddenOOB1(10); // no OOB + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1, sResult); + sResult = 0; + try { + hiddenOOB1(-2147483648); // OOB + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1001, sResult); + sResult = 0; + try { + hiddenOOB2(1); // no OOB + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1, sResult); + if (HEAVY) { + sResult = 0; + try { + hiddenOOB2(2147483647); // OOB + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1002, sResult); + } + sResult = 0; + try { + hiddenInfiniteOOB(); + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1011, sResult); + sResult = 0; + try { + hiddenFiniteOOB(); + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1111, sResult); + + // Addition. + { + int[] e1 ={ 1, 2, 3, 4, 4, 4, 4, 3, 2, 1 }; + int[] a1 = add(); + for (int i = 0; i < 10; i++) { + expectEquals(a1[i], e1[i]); + } + } + + // Multiplication. + { + int[] e1 = { 7, 1, 2, 2, 1, 0, 2, 0, 0, 1 }; + int[] a1 = multiply1(); + for (int i = 0; i < 10; i++) { + expectEquals(a1[i], e1[i]); + } + int[] e2 = { 1001, 0, 0, 1, 0, 0, 1, 0, 0, 1 }; + int[] a2 = multiply2(); + for (int i = 0; i < 10; i++) { + expectEquals(a2[i], e2[i]); + } + } + + // Dynamic BCE. + sResult = 0; + try { + linearDynamicBCE1(x, -1, x.length); + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1000, sResult); + sResult = 0; + linearDynamicBCE1(x, 0, x.length); + expectEquals(55, sResult); + sResult = 0; + try { + linearDynamicBCE1(x, 0, x.length + 1); + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1055, sResult); + + // Dynamic BCE with offset. + sResult = 0; + try { + linearDynamicBCE2(x, 0, x.length, -1); + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1000, sResult); + sResult = 0; + linearDynamicBCE2(x, 0, x.length, 0); + expectEquals(55, sResult); + sResult = 0; + try { + linearDynamicBCE2(x, 0, x.length, 1); + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1054, sResult); + + // Dynamic BCE candidates. + expectEquals(55, wrapAroundDynamicBCE(x)); + expectEquals(15, periodicDynamicBCE(x)); + expectEquals(55, dynamicBCEPossiblyInfiniteLoop(x, 0, 9)); + expectEquals(55, noDynamicBCEPossiblyInfiniteLoop(x, 0, 9)); + expectEquals(55, noDynamicBCEMixedInductionTypes(x, 0, 10)); + expectEquals(125, dynamicBCEConstantRange(x)); + + // Dynamic BCE combined with constant indices. + int[][] a; + a = new int[0][0]; + expectEquals(-1, dynamicBCEAndConstantIndices(x, a, 0, 10)); + a = new int[100][10]; + expectEquals(55, dynamicBCEAndConstantIndices(x, a, 0, 10)); + for (int i = 0; i < 10; i++) { + expectEquals((i % 10) != 0 ? 1 : 0, a[0][i]); + expectEquals((i % 10) != 0 ? 2 : 0, a[1][i]); + expectEquals((i % 10) != 0 ? 3 : 0, a[99][i]); + } + a = new int[2][10]; + sResult = 0; + try { + expectEquals(55, dynamicBCEAndConstantIndices(x, a, 0, 10)); + } catch (ArrayIndexOutOfBoundsException e) { + sResult = 1; + } + expectEquals(1, sResult); + expectEquals(a[0][1], 1); + expectEquals(a[1][1], 2); + + // Dynamic BCE combined with constant indices of all types. + boolean[] x1 = { true }; + byte[] x2 = { 2 }; + char[] x3 = { 3 }; + short[] x4 = { 4 }; + int[] x5 = { 5 }; + long[] x6 = { 6 }; + float[] x7 = { 7 }; + double[] x8 = { 8 }; + expectEquals(415, + dynamicBCEAndConstantIndicesAllPrimTypes(x, x1, x2, x3, x4, x5, x6, x7, x8, 0, 10)); + Integer[] x9 = { 9 }; + expectEquals(145, dynamicBCEAndConstantIndexRefType(x, x9, 0, 10)); + } + + private static void expectEquals(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } +} diff --git a/test/550-checker-multiply-accumulate/src/Main.java b/test/550-checker-multiply-accumulate/src/Main.java index 87a89bd9dc..09376a2054 100644 --- a/test/550-checker-multiply-accumulate/src/Main.java +++ b/test/550-checker-multiply-accumulate/src/Main.java @@ -47,7 +47,7 @@ public class Main { /// CHECK: <<Acc:i\d+>> ParameterValue /// CHECK: <<Left:i\d+>> ParameterValue /// CHECK: <<Right:i\d+>> ParameterValue - /// CHECK: <<MulAdd:i\d+>> Arm64MultiplyAccumulate [<<Acc>>,<<Left>>,<<Right>>] kind:Add + /// CHECK: <<MulAdd:i\d+>> MultiplyAccumulate [<<Acc>>,<<Left>>,<<Right>>] kind:Add /// CHECK: Return [<<MulAdd>>] /// CHECK-START-ARM64: int Main.$opt$noinline$mulAdd(int, int, int) instruction_simplifier_arm64 (after) @@ -57,6 +57,28 @@ public class Main { /// CHECK-START-ARM64: int Main.$opt$noinline$mulAdd(int, int, int) disassembly (after) /// CHECK: madd w{{\d+}}, w{{\d+}}, w{{\d+}}, w{{\d+}} + /// CHECK-START-ARM: int Main.$opt$noinline$mulAdd(int, int, int) instruction_simplifier_arm (before) + /// CHECK: <<Acc:i\d+>> ParameterValue + /// CHECK: <<Left:i\d+>> ParameterValue + /// CHECK: <<Right:i\d+>> ParameterValue + /// CHECK: <<Mul:i\d+>> Mul [<<Left>>,<<Right>>] + /// CHECK: <<Add:i\d+>> Add [<<Acc>>,<<Mul>>] + /// CHECK: Return [<<Add>>] + + /// CHECK-START-ARM: int Main.$opt$noinline$mulAdd(int, int, int) instruction_simplifier_arm (after) + /// CHECK: <<Acc:i\d+>> ParameterValue + /// CHECK: <<Left:i\d+>> ParameterValue + /// CHECK: <<Right:i\d+>> ParameterValue + /// CHECK: <<MulAdd:i\d+>> MultiplyAccumulate [<<Acc>>,<<Left>>,<<Right>>] kind:Add + /// CHECK: Return [<<MulAdd>>] + + /// CHECK-START-ARM: int Main.$opt$noinline$mulAdd(int, int, int) instruction_simplifier_arm (after) + /// CHECK-NOT: Mul + /// CHECK-NOT: Add + + /// CHECK-START-ARM: int Main.$opt$noinline$mulAdd(int, int, int) disassembly (after) + /// CHECK: mla r{{\d+}}, r{{\d+}}, r{{\d+}}, r{{\d+}} + public static int $opt$noinline$mulAdd(int acc, int left, int right) { if (doThrow) throw new Error(); return acc + left * right; @@ -78,7 +100,7 @@ public class Main { /// CHECK: <<Acc:j\d+>> ParameterValue /// CHECK: <<Left:j\d+>> ParameterValue /// CHECK: <<Right:j\d+>> ParameterValue - /// CHECK: <<MulSub:j\d+>> Arm64MultiplyAccumulate [<<Acc>>,<<Left>>,<<Right>>] kind:Sub + /// CHECK: <<MulSub:j\d+>> MultiplyAccumulate [<<Acc>>,<<Left>>,<<Right>>] kind:Sub /// CHECK: Return [<<MulSub>>] /// CHECK-START-ARM64: long Main.$opt$noinline$mulSub(long, long, long) instruction_simplifier_arm64 (after) @@ -88,6 +110,17 @@ public class Main { /// CHECK-START-ARM64: long Main.$opt$noinline$mulSub(long, long, long) disassembly (after) /// CHECK: msub x{{\d+}}, x{{\d+}}, x{{\d+}}, x{{\d+}} + /// CHECK-START-ARM: long Main.$opt$noinline$mulSub(long, long, long) instruction_simplifier_arm (before) + /// CHECK: <<Acc:j\d+>> ParameterValue + /// CHECK: <<Left:j\d+>> ParameterValue + /// CHECK: <<Right:j\d+>> ParameterValue + /// CHECK: <<Mul:j\d+>> Mul [<<Left>>,<<Right>>] + /// CHECK: <<Sub:j\d+>> Sub [<<Acc>>,<<Mul>>] + /// CHECK: Return [<<Sub>>] + + /// CHECK-START-ARM: long Main.$opt$noinline$mulSub(long, long, long) instruction_simplifier_arm (after) + /// CHECK-NOT: MultiplyAccumulate + public static long $opt$noinline$mulSub(long acc, long left, long right) { if (doThrow) throw new Error(); return acc - left * right; @@ -117,7 +150,28 @@ public class Main { /// CHECK: Return [<<Or>>] /// CHECK-START-ARM64: int Main.$opt$noinline$multipleUses1(int, int, int) instruction_simplifier_arm64 (after) - /// CHECK-NOT: Arm64MultiplyAccumulate + /// CHECK-NOT: MultiplyAccumulate + + /// CHECK-START-ARM: int Main.$opt$noinline$multipleUses1(int, int, int) instruction_simplifier_arm (before) + /// CHECK: <<Acc:i\d+>> ParameterValue + /// CHECK: <<Left:i\d+>> ParameterValue + /// CHECK: <<Right:i\d+>> ParameterValue + /// CHECK: <<Mul:i\d+>> Mul [<<Left>>,<<Right>>] + /// CHECK: <<Add:i\d+>> Add [<<Acc>>,<<Mul>>] + /// CHECK: <<Or:i\d+>> Or [<<Mul>>,<<Add>>] + /// CHECK: Return [<<Or>>] + + /// CHECK-START-ARM: int Main.$opt$noinline$multipleUses1(int, int, int) instruction_simplifier_arm (after) + /// CHECK: <<Acc:i\d+>> ParameterValue + /// CHECK: <<Left:i\d+>> ParameterValue + /// CHECK: <<Right:i\d+>> ParameterValue + /// CHECK: <<Mul:i\d+>> Mul [<<Left>>,<<Right>>] + /// CHECK: <<Add:i\d+>> Add [<<Acc>>,<<Mul>>] + /// CHECK: <<Or:i\d+>> Or [<<Mul>>,<<Add>>] + /// CHECK: Return [<<Or>>] + + /// CHECK-START-ARM: int Main.$opt$noinline$multipleUses1(int, int, int) instruction_simplifier_arm (after) + /// CHECK-NOT: MultiplyAccumulate public static int $opt$noinline$multipleUses1(int acc, int left, int right) { if (doThrow) throw new Error(); @@ -151,7 +205,30 @@ public class Main { /// CHECK: Return [<<Res>>] /// CHECK-START-ARM64: long Main.$opt$noinline$multipleUses2(long, long, long) instruction_simplifier_arm64 (after) - /// CHECK-NOT: Arm64MultiplyAccumulate + /// CHECK-NOT: MultiplyAccumulate + + /// CHECK-START-ARM: long Main.$opt$noinline$multipleUses2(long, long, long) instruction_simplifier_arm (before) + /// CHECK: <<Acc:j\d+>> ParameterValue + /// CHECK: <<Left:j\d+>> ParameterValue + /// CHECK: <<Right:j\d+>> ParameterValue + /// CHECK: <<Mul:j\d+>> Mul [<<Left>>,<<Right>>] + /// CHECK: <<Add:j\d+>> Add [<<Acc>>,<<Mul>>] + /// CHECK: <<Sub:j\d+>> Sub [<<Acc>>,<<Mul>>] + /// CHECK: <<Res:j\d+>> Add [<<Add>>,<<Sub>>] + /// CHECK: Return [<<Res>>] + + /// CHECK-START-ARM: long Main.$opt$noinline$multipleUses2(long, long, long) instruction_simplifier_arm (after) + /// CHECK: <<Acc:j\d+>> ParameterValue + /// CHECK: <<Left:j\d+>> ParameterValue + /// CHECK: <<Right:j\d+>> ParameterValue + /// CHECK: <<Mul:j\d+>> Mul [<<Left>>,<<Right>>] + /// CHECK: <<Add:j\d+>> Add [<<Acc>>,<<Mul>>] + /// CHECK: <<Sub:j\d+>> Sub [<<Acc>>,<<Mul>>] + /// CHECK: <<Res:j\d+>> Add [<<Add>>,<<Sub>>] + /// CHECK: Return [<<Res>>] + + /// CHECK-START-ARM: long Main.$opt$noinline$multipleUses2(long, long, long) instruction_simplifier_arm (after) + /// CHECK-NOT: MultiplyAccumulate public static long $opt$noinline$multipleUses2(long acc, long left, long right) { @@ -176,7 +253,7 @@ public class Main { /// CHECK-START-ARM64: int Main.$opt$noinline$mulPlusOne(int, int) instruction_simplifier_arm64 (after) /// CHECK: <<Acc:i\d+>> ParameterValue /// CHECK: <<Var:i\d+>> ParameterValue - /// CHECK: <<MulAdd:i\d+>> Arm64MultiplyAccumulate [<<Acc>>,<<Acc>>,<<Var>>] kind:Add + /// CHECK: <<MulAdd:i\d+>> MultiplyAccumulate [<<Acc>>,<<Acc>>,<<Var>>] kind:Add /// CHECK: Return [<<MulAdd>>] /// CHECK-START-ARM64: int Main.$opt$noinline$mulPlusOne(int, int) instruction_simplifier_arm64 (after) @@ -186,6 +263,27 @@ public class Main { /// CHECK-START-ARM64: int Main.$opt$noinline$mulPlusOne(int, int) disassembly (after) /// CHECK: madd w{{\d+}}, w{{\d+}}, w{{\d+}}, w{{\d+}} + /// CHECK-START-ARM: int Main.$opt$noinline$mulPlusOne(int, int) instruction_simplifier_arm (before) + /// CHECK: <<Acc:i\d+>> ParameterValue + /// CHECK: <<Var:i\d+>> ParameterValue + /// CHECK: <<Const1:i\d+>> IntConstant 1 + /// CHECK: <<Add:i\d+>> Add [<<Var>>,<<Const1>>] + /// CHECK: <<Mul:i\d+>> Mul [<<Acc>>,<<Add>>] + /// CHECK: Return [<<Mul>>] + + /// CHECK-START-ARM: int Main.$opt$noinline$mulPlusOne(int, int) instruction_simplifier_arm (after) + /// CHECK: <<Acc:i\d+>> ParameterValue + /// CHECK: <<Var:i\d+>> ParameterValue + /// CHECK: <<MulAdd:i\d+>> MultiplyAccumulate [<<Acc>>,<<Acc>>,<<Var>>] kind:Add + /// CHECK: Return [<<MulAdd>>] + + /// CHECK-START-ARM: int Main.$opt$noinline$mulPlusOne(int, int) instruction_simplifier_arm (after) + /// CHECK-NOT: Mul + /// CHECK-NOT: Add + + /// CHECK-START-ARM: int Main.$opt$noinline$mulPlusOne(int, int) disassembly (after) + /// CHECK: mla r{{\d+}}, r{{\d+}}, r{{\d+}}, r{{\d+}} + public static int $opt$noinline$mulPlusOne(int acc, int var) { if (doThrow) throw new Error(); return acc * (var + 1); @@ -207,7 +305,7 @@ public class Main { /// CHECK-START-ARM64: long Main.$opt$noinline$mulMinusOne(long, long) instruction_simplifier_arm64 (after) /// CHECK: <<Acc:j\d+>> ParameterValue /// CHECK: <<Var:j\d+>> ParameterValue - /// CHECK: <<MulSub:j\d+>> Arm64MultiplyAccumulate [<<Acc>>,<<Acc>>,<<Var>>] kind:Sub + /// CHECK: <<MulSub:j\d+>> MultiplyAccumulate [<<Acc>>,<<Acc>>,<<Var>>] kind:Sub /// CHECK: Return [<<MulSub>>] /// CHECK-START-ARM64: long Main.$opt$noinline$mulMinusOne(long, long) instruction_simplifier_arm64 (after) @@ -217,11 +315,114 @@ public class Main { /// CHECK-START-ARM64: long Main.$opt$noinline$mulMinusOne(long, long) disassembly (after) /// CHECK: msub x{{\d+}}, x{{\d+}}, x{{\d+}}, x{{\d+}} + /// CHECK-START-ARM: long Main.$opt$noinline$mulMinusOne(long, long) instruction_simplifier_arm (before) + /// CHECK: <<Acc:j\d+>> ParameterValue + /// CHECK: <<Var:j\d+>> ParameterValue + /// CHECK: <<Const1:j\d+>> LongConstant 1 + /// CHECK: <<Sub:j\d+>> Sub [<<Const1>>,<<Var>>] + /// CHECK: <<Mul:j\d+>> Mul [<<Acc>>,<<Sub>>] + /// CHECK: Return [<<Mul>>] + + /// CHECK-START-ARM: long Main.$opt$noinline$mulMinusOne(long, long) instruction_simplifier_arm (after) + /// CHECK-NOT: MultiplyAccumulate public static long $opt$noinline$mulMinusOne(long acc, long var) { if (doThrow) throw new Error(); return acc * (1 - var); } + /** + * Test basic merging of `MUL+NEG` into `MULNEG`. + */ + + /// CHECK-START-ARM64: int Main.$opt$noinline$mulNeg(int, int) instruction_simplifier_arm64 (before) + /// CHECK: <<Left:i\d+>> ParameterValue + /// CHECK: <<Right:i\d+>> ParameterValue + /// CHECK: <<Mul:i\d+>> Mul [<<Left>>,<<Right>>] + /// CHECK: <<Neg:i\d+>> Neg [<<Mul>>] + /// CHECK: Return [<<Neg>>] + + /// CHECK-START-ARM64: int Main.$opt$noinline$mulNeg(int, int) instruction_simplifier_arm64 (after) + /// CHECK: <<Left:i\d+>> ParameterValue + /// CHECK: <<Right:i\d+>> ParameterValue + /// CHECK: <<Const0:i\d+>> IntConstant 0 + /// CHECK: <<MulNeg:i\d+>> MultiplyAccumulate [<<Const0>>,<<Left>>,<<Right>>] kind:Sub + /// CHECK: Return [<<MulNeg>>] + + /// CHECK-START-ARM64: int Main.$opt$noinline$mulNeg(int, int) instruction_simplifier_arm64 (after) + /// CHECK-NOT: Mul + /// CHECK-NOT: Neg + + /// CHECK-START-ARM64: int Main.$opt$noinline$mulNeg(int, int) disassembly (after) + /// CHECK: mneg w{{\d+}}, w{{\d+}}, w{{\d+}} + + /// CHECK-START-ARM: int Main.$opt$noinline$mulNeg(int, int) instruction_simplifier_arm (before) + /// CHECK: <<Left:i\d+>> ParameterValue + /// CHECK: <<Right:i\d+>> ParameterValue + /// CHECK: <<Mul:i\d+>> Mul [<<Left>>,<<Right>>] + /// CHECK: <<Neg:i\d+>> Neg [<<Mul>>] + /// CHECK: Return [<<Neg>>] + + /// CHECK-START-ARM: int Main.$opt$noinline$mulNeg(int, int) instruction_simplifier_arm (after) + /// CHECK: <<Left:i\d+>> ParameterValue + /// CHECK: <<Right:i\d+>> ParameterValue + /// CHECK: <<Mul:i\d+>> Mul [<<Left>>,<<Right>>] + /// CHECK: <<Neg:i\d+>> Neg [<<Mul>>] + /// CHECK: Return [<<Neg>>] + + /// CHECK-START-ARM: int Main.$opt$noinline$mulNeg(int, int) instruction_simplifier_arm (after) + /// CHECK-NOT: MultiplyAccumulate + + public static int $opt$noinline$mulNeg(int left, int right) { + if (doThrow) throw new Error(); + return - (left * right); + } + + /** + * Test basic merging of `MUL+NEG` into `MULNEG`. + */ + + /// CHECK-START-ARM64: long Main.$opt$noinline$mulNeg(long, long) instruction_simplifier_arm64 (before) + /// CHECK: <<Left:j\d+>> ParameterValue + /// CHECK: <<Right:j\d+>> ParameterValue + /// CHECK: <<Mul:j\d+>> Mul [<<Left>>,<<Right>>] + /// CHECK: <<Neg:j\d+>> Neg [<<Mul>>] + /// CHECK: Return [<<Neg>>] + + /// CHECK-START-ARM64: long Main.$opt$noinline$mulNeg(long, long) instruction_simplifier_arm64 (after) + /// CHECK: <<Left:j\d+>> ParameterValue + /// CHECK: <<Right:j\d+>> ParameterValue + /// CHECK: <<Const0:j\d+>> LongConstant 0 + /// CHECK: <<MulNeg:j\d+>> MultiplyAccumulate [<<Const0>>,<<Left>>,<<Right>>] kind:Sub + /// CHECK: Return [<<MulNeg>>] + + /// CHECK-START-ARM64: long Main.$opt$noinline$mulNeg(long, long) instruction_simplifier_arm64 (after) + /// CHECK-NOT: Mul + /// CHECK-NOT: Neg + + /// CHECK-START-ARM64: long Main.$opt$noinline$mulNeg(long, long) disassembly (after) + /// CHECK: mneg x{{\d+}}, x{{\d+}}, x{{\d+}} + + /// CHECK-START-ARM: long Main.$opt$noinline$mulNeg(long, long) instruction_simplifier_arm (before) + /// CHECK: <<Left:j\d+>> ParameterValue + /// CHECK: <<Right:j\d+>> ParameterValue + /// CHECK: <<Mul:j\d+>> Mul [<<Left>>,<<Right>>] + /// CHECK: <<Neg:j\d+>> Neg [<<Mul>>] + /// CHECK: Return [<<Neg>>] + + /// CHECK-START-ARM: long Main.$opt$noinline$mulNeg(long, long) instruction_simplifier_arm (after) + /// CHECK: <<Left:j\d+>> ParameterValue + /// CHECK: <<Right:j\d+>> ParameterValue + /// CHECK: <<Mul:j\d+>> Mul [<<Left>>,<<Right>>] + /// CHECK: <<Neg:j\d+>> Neg [<<Mul>>] + /// CHECK: Return [<<Neg>>] + + /// CHECK-START-ARM: long Main.$opt$noinline$mulNeg(long, long) instruction_simplifier_arm (after) + /// CHECK-NOT: MultiplyAccumulate + + public static long $opt$noinline$mulNeg(long left, long right) { + if (doThrow) throw new Error(); + return - (left * right); + } public static void main(String[] args) { assertIntEquals(7, $opt$noinline$mulAdd(1, 2, 3)); @@ -230,5 +431,7 @@ public class Main { assertLongEquals(20, $opt$noinline$multipleUses2(10, 11, 12)); assertIntEquals(195, $opt$noinline$mulPlusOne(13, 14)); assertLongEquals(-225, $opt$noinline$mulMinusOne(15, 16)); + assertIntEquals(-306, $opt$noinline$mulNeg(17, 18)); + assertLongEquals(-380, $opt$noinline$mulNeg(19, 20)); } } diff --git a/test/564-checker-negbitwise/expected.txt b/test/564-checker-negbitwise/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/564-checker-negbitwise/expected.txt diff --git a/test/564-checker-negbitwise/info.txt b/test/564-checker-negbitwise/info.txt new file mode 100644 index 0000000000..28b9e9e832 --- /dev/null +++ b/test/564-checker-negbitwise/info.txt @@ -0,0 +1 @@ +Test negated bitwise operations simplification on ARM64. diff --git a/test/564-checker-negbitwise/src/Main.java b/test/564-checker-negbitwise/src/Main.java new file mode 100644 index 0000000000..3de7be7161 --- /dev/null +++ b/test/564-checker-negbitwise/src/Main.java @@ -0,0 +1,207 @@ +/* + * 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. + */ + +public class Main { + + // A dummy value to defeat inlining of these routines. + static boolean doThrow = false; + + public static void assertIntEquals(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public static void assertLongEquals(long expected, long result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + /** + * Test merging of `NOT+AND` into `BIC`. + */ + + /// CHECK-START-ARM64: int Main.$opt$noinline$notAnd(int, int) instruction_simplifier_arm64 (before) + /// CHECK: <<Base:i\d+>> ParameterValue + /// CHECK: <<Mask:i\d+>> ParameterValue + /// CHECK: <<Not:i\d+>> Not [<<Mask>>] + /// CHECK: <<Op:i\d+>> And [<<Base>>,<<Not>>] + /// CHECK: Return [<<Op>>] + + /// CHECK-START-ARM64: int Main.$opt$noinline$notAnd(int, int) instruction_simplifier_arm64 (after) + /// CHECK: <<Base:i\d+>> ParameterValue + /// CHECK: <<Mask:i\d+>> ParameterValue + /// CHECK: <<NegOp:i\d+>> Arm64BitwiseNegatedRight [<<Base>>,<<Mask>>] kind:And + /// CHECK: Return [<<NegOp>>] + + /// CHECK-START-ARM64: int Main.$opt$noinline$notAnd(int, int) instruction_simplifier_arm64 (after) + /// CHECK-NOT: Not + /// CHECK-NOT: And + + /// CHECK-START-ARM64: int Main.$opt$noinline$notAnd(int, int) disassembly (after) + /// CHECK: bic w{{\d+}}, w{{\d+}}, w{{\d+}} + + public static int $opt$noinline$notAnd(int base, int mask) { + if (doThrow) throw new Error(); + return base & ~mask; + } + + /** + * Test merging of `NOT+ORR` into `ORN`. + */ + + /// CHECK-START-ARM64: long Main.$opt$noinline$notOr(long, long) instruction_simplifier_arm64 (before) + /// CHECK: <<Base:j\d+>> ParameterValue + /// CHECK: <<Mask:j\d+>> ParameterValue + /// CHECK: <<Not:j\d+>> Not [<<Mask>>] + /// CHECK: <<Op:j\d+>> Or [<<Base>>,<<Not>>] + /// CHECK: Return [<<Op>>] + + /// CHECK-START-ARM64: long Main.$opt$noinline$notOr(long, long) instruction_simplifier_arm64 (after) + /// CHECK: <<Base:j\d+>> ParameterValue + /// CHECK: <<Mask:j\d+>> ParameterValue + /// CHECK: <<NegOp:j\d+>> Arm64BitwiseNegatedRight [<<Base>>,<<Mask>>] kind:Or + /// CHECK: Return [<<NegOp>>] + + /// CHECK-START-ARM64: long Main.$opt$noinline$notOr(long, long) instruction_simplifier_arm64 (after) + /// CHECK-NOT: Not + /// CHECK-NOT: Or + + /// CHECK-START-ARM64: long Main.$opt$noinline$notOr(long, long) disassembly (after) + /// CHECK: orn x{{\d+}}, x{{\d+}}, x{{\d+}} + + public static long $opt$noinline$notOr(long base, long mask) { + if (doThrow) throw new Error(); + return base | ~mask; + } + + /** + * Test merging of `NOT+EOR` into `EON`. + */ + + /// CHECK-START-ARM64: int Main.$opt$noinline$notXor(int, int) instruction_simplifier_arm64 (before) + /// CHECK: <<Base:i\d+>> ParameterValue + /// CHECK: <<Mask:i\d+>> ParameterValue + /// CHECK: <<Not:i\d+>> Not [<<Mask>>] + /// CHECK: <<Op:i\d+>> Xor [<<Base>>,<<Not>>] + /// CHECK: Return [<<Op>>] + + /// CHECK-START-ARM64: int Main.$opt$noinline$notXor(int, int) instruction_simplifier_arm64 (after) + /// CHECK: <<Base:i\d+>> ParameterValue + /// CHECK: <<Mask:i\d+>> ParameterValue + /// CHECK: <<NegOp:i\d+>> Arm64BitwiseNegatedRight [<<Base>>,<<Mask>>] kind:Xor + /// CHECK: Return [<<NegOp>>] + + /// CHECK-START-ARM64: int Main.$opt$noinline$notXor(int, int) instruction_simplifier_arm64 (after) + /// CHECK-NOT: Not + /// CHECK-NOT: Xor + + /// CHECK-START-ARM64: int Main.$opt$noinline$notXor(int, int) disassembly (after) + /// CHECK: eon w{{\d+}}, w{{\d+}}, w{{\d+}} + + public static int $opt$noinline$notXor(int base, int mask) { + if (doThrow) throw new Error(); + return base ^ ~mask; + } + + /** + * Check that the transformation is also done when the base is a constant. + */ + + /// CHECK-START-ARM64: int Main.$opt$noinline$notXorConstant(int) instruction_simplifier_arm64 (before) + /// CHECK: <<Mask:i\d+>> ParameterValue + /// CHECK: <<Constant:i\d+>> IntConstant + /// CHECK: <<Not:i\d+>> Not [<<Mask>>] + /// CHECK: <<Op:i\d+>> Xor [<<Not>>,<<Constant>>] + /// CHECK: Return [<<Op>>] + + /// CHECK-START-ARM64: int Main.$opt$noinline$notXorConstant(int) instruction_simplifier_arm64 (after) + /// CHECK: <<Mask:i\d+>> ParameterValue + /// CHECK: <<Constant:i\d+>> IntConstant + /// CHECK: <<NegOp:i\d+>> Arm64BitwiseNegatedRight [<<Constant>>,<<Mask>>] kind:Xor + /// CHECK: Return [<<NegOp>>] + + /// CHECK-START-ARM64: int Main.$opt$noinline$notXorConstant(int) instruction_simplifier_arm64 (after) + /// CHECK-NOT: Not + /// CHECK-NOT: Xor + + /// CHECK-START-ARM64: int Main.$opt$noinline$notXorConstant(int) disassembly (after) + /// CHECK: mov <<Reg:w\d+>>, #0xf + /// CHECK: eon w{{\d+}}, <<Reg>>, w{{\d+}} + + public static int $opt$noinline$notXorConstant(int mask) { + if (doThrow) throw new Error(); + return 0xf ^ ~mask; + } + + /** + * Check that no transformation is done when Not has multiple uses. + */ + + /// CHECK-START-ARM64: int Main.$opt$noinline$notAndMultipleUses(int, int) instruction_simplifier_arm64 (before) + /// CHECK: <<Base:i\d+>> ParameterValue + /// CHECK: <<Mask:i\d+>> ParameterValue + /// CHECK: <<One:i\d+>> IntConstant + /// CHECK: <<Not:i\d+>> Not [<<Mask>>] + /// CHECK: <<Op1:i\d+>> And [<<Not>>,<<One>>] + /// CHECK: <<Op2:i\d+>> And [<<Base>>,<<Not>>] + /// CHECK: <<Add:i\d+>> Add [<<Op1>>,<<Op2>>] + /// CHECK: Return [<<Add>>] + + /// CHECK-START-ARM64: int Main.$opt$noinline$notAndMultipleUses(int, int) instruction_simplifier_arm64 (after) + /// CHECK: <<Base:i\d+>> ParameterValue + /// CHECK: <<Mask:i\d+>> ParameterValue + /// CHECK: <<One:i\d+>> IntConstant + /// CHECK: <<Not:i\d+>> Not [<<Mask>>] + /// CHECK: <<Op1:i\d+>> And [<<Not>>,<<One>>] + /// CHECK: <<Op2:i\d+>> And [<<Base>>,<<Not>>] + /// CHECK: <<Add:i\d+>> Add [<<Op1>>,<<Op2>>] + /// CHECK: Return [<<Add>>] + + /// CHECK-START-ARM64: int Main.$opt$noinline$notAndMultipleUses(int, int) instruction_simplifier_arm64 (after) + /// CHECK-NOT: Arm64BitwiseNegatedRight + + public static int $opt$noinline$notAndMultipleUses(int base, int mask) { + if (doThrow) throw new Error(); + int tmp = ~mask; + return (tmp & 0x1) + (base & tmp); + } + + /** + * Check that no transformation is done when both inputs are Not's. + */ + + // We don't check the instructions before the pass, since if De Morgan's laws + // have been applied then Not/Not/Or is replaced by And/Not. + + /// CHECK-START-ARM64: int Main.$opt$noinline$deMorganOr(int, int) instruction_simplifier_arm64 (after) + /// CHECK-NOT: Arm64BitwiseNegatedRight + + public static int $opt$noinline$deMorganOr(int a, int b) { + if (doThrow) throw new Error(); + return ~a | ~b; + } + + public static void main(String[] args) { + assertIntEquals(0xe, $opt$noinline$notAnd(0xf, 0x1)); + assertLongEquals(~0x0, $opt$noinline$notOr(0xf, 0x1)); + assertIntEquals(~0xe, $opt$noinline$notXor(0xf, 0x1)); + assertIntEquals(~0xe, $opt$noinline$notXorConstant(0x1)); + assertIntEquals(0xe, $opt$noinline$notAndMultipleUses(0xf, 0x1)); + assertIntEquals(~0x1, $opt$noinline$deMorganOr(0x3, 0x1)); + } +} diff --git a/test/577-checker-fp2int/expected.txt b/test/577-checker-fp2int/expected.txt new file mode 100644 index 0000000000..b0aad4deb5 --- /dev/null +++ b/test/577-checker-fp2int/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/577-checker-fp2int/info.txt b/test/577-checker-fp2int/info.txt new file mode 100644 index 0000000000..d22a0eab9d --- /dev/null +++ b/test/577-checker-fp2int/info.txt @@ -0,0 +1 @@ +Unit test for float/double to raw bits conversions. diff --git a/test/577-checker-fp2int/src/Main.java b/test/577-checker-fp2int/src/Main.java new file mode 100644 index 0000000000..e3f1230beb --- /dev/null +++ b/test/577-checker-fp2int/src/Main.java @@ -0,0 +1,122 @@ +/* + * 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. + */ + +public class Main { + + /// CHECK-START: int Main.f2int(float) instruction_simplifier (before) + /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:FloatFloatToIntBits + /// CHECK-DAG: Return [<<Result>>] + // + /// CHECK-START: int Main.f2int(float) instruction_simplifier (after) + /// CHECK-DAG: <<Raw:i\d+>> InvokeStaticOrDirect [<<Arg:f\d+>>] intrinsic:FloatFloatToRawIntBits + /// CHECK-DAG: <<Cond:z\d+>> NotEqual [<<Arg>>,<<Arg>>] + /// CHECK-DAG: <<Result:i\d+>> Select [<<Raw>>,{{i\d+}},<<Cond>>] + /// CHECK-DAG: Return [<<Result>>] + private static int f2int(float f) { + return Float.floatToIntBits(f); + } + + /// CHECK-START: long Main.d2long(double) instruction_simplifier (before) + /// CHECK-DAG: <<Result:j\d+>> InvokeStaticOrDirect intrinsic:DoubleDoubleToLongBits + /// CHECK-DAG: Return [<<Result>>] + // + /// CHECK-START: long Main.d2long(double) instruction_simplifier (after) + /// CHECK-DAG: <<Raw:j\d+>> InvokeStaticOrDirect [<<Arg:d\d+>>] intrinsic:DoubleDoubleToRawLongBits + /// CHECK-DAG: <<Cond:z\d+>> NotEqual [<<Arg>>,<<Arg>>] + /// CHECK-DAG: <<Result:j\d+>> Select [<<Raw>>,{{j\d+}},<<Cond>>] + /// CHECK-DAG: Return [<<Result>>] + private static long d2long(double d) { + return Double.doubleToLongBits(d); + } + + public static void main(String args[]) { + // A few distinct numbers. + expectEquals32(0xff800000, f2int(Float.NEGATIVE_INFINITY)); + expectEquals32(0xbf800000, f2int(-1.0f)); + expectEquals32(0x80000000, f2int(-0.0f)); + expectEquals32(0x00000000, f2int(+0.0f)); + expectEquals32(0x3f800000, f2int(+1.0f)); + expectEquals32(0x7f800000, f2int(Float.POSITIVE_INFINITY)); + + // A few others. + for (int i = 0; i <= 100; i++) { + expectEquals32(i, f2int(Float.intBitsToFloat(i))); + } + + // A few NaN numbers. + float[] fvals = { + Float.intBitsToFloat(0x7f800001), + Float.intBitsToFloat(0x7fa00000), + Float.intBitsToFloat(0x7fc00000), + Float.intBitsToFloat(0x7fffffff), + Float.intBitsToFloat(0xff800001), + Float.intBitsToFloat(0xffa00000), + Float.intBitsToFloat(0xffc00000), + Float.intBitsToFloat(0xffffffff) + }; + for (int i = 0; i < fvals.length; i++) { + expectEquals32(0x7fc00000, f2int(fvals[i])); + } + + // A few distinct numbers. + expectEquals64(0xfff0000000000000L, d2long(Double.NEGATIVE_INFINITY)); + expectEquals64(0xbff0000000000000L, d2long(-1.0d)); + expectEquals64(0x8000000000000000L, d2long(-0.0d)); + expectEquals64(0x0000000000000000L, d2long(+0.0d)); + expectEquals64(0x3ff0000000000000L, d2long(+1.0d)); + expectEquals64(0x7ff0000000000000L, d2long(Double.POSITIVE_INFINITY)); + + // A few others. + for (long l = 0; l <= 100; l++) { + expectEquals64(l, d2long(Double.longBitsToDouble(l))); + } + + // A few NaN numbers. + double[] dvals = { + Double.longBitsToDouble(0x7ff0000000000001L), + Double.longBitsToDouble(0x7ff4000000000000L), + Double.longBitsToDouble(0x7ff8000000000000L), + Double.longBitsToDouble(0x7fffffffffffffffL), + Double.longBitsToDouble(0xfff0000000000001L), + Double.longBitsToDouble(0xfff4000000000000L), + Double.longBitsToDouble(0xfff8000000000000L), + Double.longBitsToDouble(0xffffffffffffffffL) + }; + for (int i = 0; i < dvals.length; i++) { + expectEquals64(0x7ff8000000000000L, d2long(dvals[i])); + } + + System.out.println("passed"); + } + + private static void expectEquals32(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + + Integer.toHexString(expected) + + ", found: " + + Integer.toHexString(result)); + } + } + + private static void expectEquals64(long expected, long result) { + if (expected != result) { + throw new Error("Expected: " + + Long.toHexString(expected) + + ", found: " + + Long.toHexString(result)); + } + } +} diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 364be59919..167ad859d2 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -220,6 +220,18 @@ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES), $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ $(IMAGE_TYPES), $(PICTEST_TYPES), $(DEBUGGABLE_TYPES), $(ART_TEST_RUN_TEST_SKIP), $(ALL_ADDRESS_SIZES)) + +# Disable 097-duplicate-method while investigation (broken by latest Jack release, b/27358065) +TEST_ART_BROKEN_ALL_TARGET_TESTS := \ + 097-duplicate-method + +ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ + $(COMPILER_TYPES), $(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ + $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), $(TEST_ART_BROKEN_ALL_TARGET_TESTS), \ + $(ALL_ADDRESS_SIZES)) + +TEST_ART_BROKEN_ALL_TARGET_TESTS := + # Tests that are timing sensitive and flaky on heavily loaded systems. TEST_ART_TIMING_SENSITIVE_RUN_TESTS := \ 002-sleep \ diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt index e6394a999f..46100ae15c 100644 --- a/tools/libcore_failures.txt +++ b/tools/libcore_failures.txt @@ -208,13 +208,6 @@ "org.apache.harmony.tests.java.util.TimerTaskTest#test_scheduledExecutionTime"] }, { - description: "'cat -' does not work anymore", - result: EXEC_FAILED, - bug: 26395656, - modes: [device], - names: ["org.apache.harmony.tests.java.lang.ProcessTest#test_getOutputStream"] -}, -{ description: "Missing resource in classpath", result: EXEC_FAILED, modes: [device], |