diff options
117 files changed, 3073 insertions, 886 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 5a3236d958..730e61d488 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -26,6 +26,7 @@ GTEST_DEX_DIRECTORIES := \    AllFields \    ExceptionHandle \    GetMethodSignature \ +  Instrumentation \    Interfaces \    Main \    MultiDex \ @@ -64,6 +65,7 @@ ART_GTEST_class_linker_test_DEX_DEPS := Interfaces MultiDex MyClass Nested Stati  ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods  ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Main Nested  ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle +ART_GTEST_instrumentation_test_DEX_DEPS := Instrumentation  ART_GTEST_jni_compiler_test_DEX_DEPS := MyClassNatives  ART_GTEST_jni_internal_test_DEX_DEPS := AllFields StaticLeafMethods  ART_GTEST_oat_file_assistant_test_DEX_DEPS := Main MainStripped MultiDex Nested @@ -157,6 +159,7 @@ RUNTIME_GTEST_COMMON_SRC_FILES := \    runtime/handle_scope_test.cc \    runtime/indenter_test.cc \    runtime/indirect_reference_table_test.cc \ +  runtime/instrumentation_test.cc \    runtime/intern_table_test.cc \    runtime/interpreter/safe_math_test.cc \    runtime/java_vm_ext_test.cc \ diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h index 0acdd422df..b78b3d7d75 100644 --- a/compiler/dex/compiler_enums.h +++ b/compiler/dex/compiler_enums.h @@ -172,7 +172,6 @@ enum ExtendedMIROpcode {    kMirOpRangeCheck,    kMirOpDivZeroCheck,    kMirOpCheck, -  kMirOpCheckPart2,    kMirOpSelect,    // Vector opcodes: diff --git a/compiler/dex/gvn_dead_code_elimination.cc b/compiler/dex/gvn_dead_code_elimination.cc index 4f0e9d1b67..a4319c3dc5 100644 --- a/compiler/dex/gvn_dead_code_elimination.cc +++ b/compiler/dex/gvn_dead_code_elimination.cc @@ -533,7 +533,7 @@ MIR* GvnDeadCodeElimination::RenameSRegDefOrCreatePhi(uint16_t def_change, uint1    // Just before we kill mir_to_kill, we need to replace the previous SSA reg assigned to the    // same dalvik reg to keep consistency with subsequent instructions. However, if there's no -  // defining MIR for that dalvik reg, the preserved valus must come from its predecessors +  // defining MIR for that dalvik reg, the preserved values must come from its predecessors    // and we need to create a new Phi (a degenerate Phi if there's only a single predecessor).    if (def_change == kNPos) {      if (wide) { @@ -541,7 +541,21 @@ MIR* GvnDeadCodeElimination::RenameSRegDefOrCreatePhi(uint16_t def_change, uint1        DCHECK_EQ(mir_graph_->SRegToVReg(new_s_reg) + 1, mir_graph_->SRegToVReg(new_s_reg + 1));        CreatePhi(new_s_reg + 1);  // High word Phi.      } -    return CreatePhi(new_s_reg); +    MIR* phi = CreatePhi(new_s_reg); +    // If this is a degenerate Phi with all inputs being the same SSA reg, we need to its uses. +    DCHECK_NE(phi->ssa_rep->num_uses, 0u); +    int old_s_reg = phi->ssa_rep->uses[0]; +    bool all_same = true; +    for (size_t i = 1u, num = phi->ssa_rep->num_uses; i != num; ++i) { +      if (phi->ssa_rep->uses[i] != old_s_reg) { +        all_same = false; +        break; +      } +    } +    if (all_same) { +      vreg_chains_.RenameSRegUses(0u, last_change, old_s_reg, new_s_reg, wide); +    } +    return phi;    } else {      DCHECK_LT(def_change, last_change);      DCHECK_LE(last_change, vreg_chains_.NumMIRs()); diff --git a/compiler/dex/gvn_dead_code_elimination_test.cc b/compiler/dex/gvn_dead_code_elimination_test.cc index f9f0882f08..efb32bb39e 100644 --- a/compiler/dex/gvn_dead_code_elimination_test.cc +++ b/compiler/dex/gvn_dead_code_elimination_test.cc @@ -1629,6 +1629,52 @@ TEST_F(GvnDeadCodeEliminationTestDiamond, CreatePhi1) {  }  TEST_F(GvnDeadCodeEliminationTestDiamond, CreatePhi2) { +  static const MIRDef mirs[] = { +      DEF_CONST(3, Instruction::CONST, 0u, 1000), +      DEF_MOVE(4, Instruction::MOVE, 1u, 0u), +      DEF_CONST(4, Instruction::CONST, 2u, 1000), +  }; + +  static const int32_t sreg_to_vreg_map[] = { 0, 1, 0 }; +  PrepareSRegToVRegMap(sreg_to_vreg_map); + +  PrepareMIRs(mirs); +  PerformGVN_DCE(); + +  ASSERT_EQ(arraysize(mirs), value_names_.size()); +  EXPECT_EQ(value_names_[0], value_names_[1]); +  EXPECT_EQ(value_names_[0], value_names_[2]); + +  static const bool eliminated[] = { +      false, false, true, +  }; +  static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); +  for (size_t i = 0; i != arraysize(eliminated); ++i) { +    bool actually_eliminated = (static_cast<int>(mirs_[i].dalvikInsn.opcode) == kMirOpNop); +    EXPECT_EQ(eliminated[i], actually_eliminated) << i; +  } +  // Check that we've created a single-input Phi to replace the CONST 3u. +  BasicBlock* bb4 = cu_.mir_graph->GetBasicBlock(4); +  MIR* phi = bb4->first_mir_insn; +  ASSERT_TRUE(phi != nullptr); +  ASSERT_EQ(kMirOpPhi, static_cast<int>(phi->dalvikInsn.opcode)); +  ASSERT_EQ(1, phi->ssa_rep->num_uses); +  EXPECT_EQ(0, phi->ssa_rep->uses[0]); +  ASSERT_EQ(1, phi->ssa_rep->num_defs); +  EXPECT_EQ(2, phi->ssa_rep->defs[0]); +  EXPECT_EQ(0u, phi->dalvikInsn.vA); +  MIR* move = phi->next; +  ASSERT_TRUE(move != nullptr); +  ASSERT_EQ(Instruction::MOVE, move->dalvikInsn.opcode); +  ASSERT_EQ(1, move->ssa_rep->num_uses); +  EXPECT_EQ(2, move->ssa_rep->uses[0]); +  ASSERT_EQ(1, move->ssa_rep->num_defs); +  EXPECT_EQ(1, move->ssa_rep->defs[0]); +  EXPECT_EQ(1u, move->dalvikInsn.vA); +  EXPECT_EQ(0u, move->dalvikInsn.vB); +} + +TEST_F(GvnDeadCodeEliminationTestDiamond, CreatePhi3) {    static const IFieldDef ifields[] = {        { 0u, 1u, 0u, false, kDexMemAccessWord },    }; diff --git a/compiler/dex/mir_dataflow.cc b/compiler/dex/mir_dataflow.cc index b4aec98e01..a7ba061984 100644 --- a/compiler/dex/mir_dataflow.cc +++ b/compiler/dex/mir_dataflow.cc @@ -834,9 +834,6 @@ const uint64_t MIRGraph::oat_data_flow_attributes_[kMirOpLast] = {    // 10B MIR_CHECK    0, -  // 10C MIR_CHECKPART2 -  0, -    // 10D MIR_SELECT    DF_DA | DF_UB, diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc index 9e3fbbc967..1871f07106 100644 --- a/compiler/dex/mir_graph.cc +++ b/compiler/dex/mir_graph.cc @@ -52,8 +52,7 @@ const char* MIRGraph::extended_mir_op_names_[kMirOpLast - kMirOpFirst] = {    "OpNullCheck",    "OpRangeCheck",    "OpDivZeroCheck", -  "Check1", -  "Check2", +  "Check",    "Select",    "ConstVector",    "MoveVector", @@ -1508,7 +1507,7 @@ char* MIRGraph::GetDalvikDisassembly(const MIR* mir) {    Instruction::Format dalvik_format = Instruction::k10x;  // Default to no-operand format.    // Handle special cases that recover the original dalvik instruction. -  if ((opcode == kMirOpCheck) || (opcode == kMirOpCheckPart2)) { +  if (opcode == kMirOpCheck) {      str.append(extended_mir_op_names_[opcode - kMirOpFirst]);      str.append(": ");      // Recover the original Dex instruction. @@ -2517,8 +2516,6 @@ int MIR::DecodedInstruction::FlagsOf() const {        return Instruction::kContinue | Instruction::kThrow;      case kMirOpCheck:        return Instruction::kContinue | Instruction::kThrow; -    case kMirOpCheckPart2: -      return Instruction::kContinue;      case kMirOpSelect:        return Instruction::kContinue;      case kMirOpConstVector: diff --git a/compiler/dex/mir_method_info.cc b/compiler/dex/mir_method_info.cc index 5654604797..94be1fd4a5 100644 --- a/compiler/dex/mir_method_info.cc +++ b/compiler/dex/mir_method_info.cc @@ -169,7 +169,8 @@ void MirMethodLoweringInfo::Resolve(CompilerDriver* compiler_driver,          ~(kFlagFastPath | kFlagIsIntrinsic | kFlagIsSpecial | kFlagClassIsInitialized |              (kInvokeTypeMask << kBitSharpTypeBegin));      it->flags_ = other_flags | -        (fast_path_flags != 0 ? kFlagFastPath : 0u) | +        // String init path is a special always-fast path. +        (fast_path_flags != 0 || string_init ? kFlagFastPath : 0u) |          ((is_intrinsic_or_special & kInlineIntrinsic) != 0 ? kFlagIsIntrinsic : 0u) |          ((is_intrinsic_or_special & kInlineSpecial) != 0 ? kFlagIsSpecial : 0u) |          (static_cast<uint16_t>(invoke_type) << kBitSharpTypeBegin) | diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index fb68335e6e..86bb69d01e 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -1391,22 +1391,6 @@ void Mir2Lir::InitReferenceVRegs(BasicBlock* bb, BitVector* references) {        }      }    } -  if (bb->block_type != kEntryBlock && bb->first_mir_insn != nullptr && -      static_cast<int>(bb->first_mir_insn->dalvikInsn.opcode) == kMirOpCheckPart2) { -    // In Mir2Lir::MethodBlockCodeGen() we have artificially moved the throwing -    // instruction to the previous block. However, the MIRGraph data used above -    // doesn't reflect that, so we still need to process that MIR insn here. -    MIR* mir = nullptr; -    BasicBlock* pred_bb = bb; -    // Traverse empty blocks. -    while (mir == nullptr && pred_bb->predecessors.size() == 1u) { -      pred_bb = mir_graph_->GetBasicBlock(bb->predecessors[0]); -      DCHECK(pred_bb != nullptr); -      mir = pred_bb->last_mir_insn; -    } -    DCHECK(mir != nullptr); -    UpdateReferenceVRegsLocal(nullptr, mir, references); -  }  }  bool Mir2Lir::UpdateReferenceVRegsLocal(MIR* mir, MIR* prev_mir, BitVector* references) { diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc index e9e9161a1c..e3e87ecb13 100644 --- a/compiler/dex/quick/mir_to_lir.cc +++ b/compiler/dex/quick/mir_to_lir.cc @@ -1187,7 +1187,6 @@ void Mir2Lir::HandleExtendedMethodMIR(BasicBlock* bb, MIR* mir) {      case kMirOpRangeCheck:      case kMirOpDivZeroCheck:      case kMirOpCheck: -    case kMirOpCheckPart2:        // Ignore these known opcodes        break;      default: @@ -1276,20 +1275,6 @@ bool Mir2Lir::MethodBlockCodeGen(BasicBlock* bb) {        head_lir->u.m.def_mask = &kEncodeAll;      } -    if (opcode == kMirOpCheck) { -      // Combine check and work halves of throwing instruction. -      MIR* work_half = mir->meta.throw_insn; -      mir->dalvikInsn = work_half->dalvikInsn; -      mir->optimization_flags = work_half->optimization_flags; -      mir->meta = work_half->meta;  // Whatever the work_half had, we need to copy it. -      opcode = work_half->dalvikInsn.opcode; -      SSARepresentation* ssa_rep = work_half->ssa_rep; -      work_half->ssa_rep = mir->ssa_rep; -      mir->ssa_rep = ssa_rep; -      work_half->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpCheckPart2); -      work_half->meta.throw_insn = mir; -    } -      if (MIR::DecodedInstruction::IsPseudoMirOp(opcode)) {        HandleExtendedMethodMIR(bb, mir);        continue; diff --git a/compiler/dex/quick/quick_compiler.cc b/compiler/dex/quick/quick_compiler.cc index 73cfe92c45..7ca438225f 100644 --- a/compiler/dex/quick/quick_compiler.cc +++ b/compiler/dex/quick/quick_compiler.cc @@ -403,7 +403,6 @@ static int kAllOpcodes[] = {      kMirOpRangeCheck,      kMirOpDivZeroCheck,      kMirOpCheck, -    kMirOpCheckPart2,      kMirOpSelect,  }; diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc index 92fa6db507..b2b54965b5 100644 --- a/compiler/optimizing/bounds_check_elimination.cc +++ b/compiler/optimizing/bounds_check_elimination.cc @@ -281,15 +281,22 @@ class ArrayAccessInsideLoopFinder : public ValueObject {      return false;    } +  static bool DominatesAllBackEdges(HBasicBlock* block, HLoopInformation* loop_info) { +    for (size_t i = 0, e = loop_info->GetBackEdges().Size(); i < e; ++i) { +      HBasicBlock* back_edge = loop_info->GetBackEdges().Get(i); +      if (!block->Dominates(back_edge)) { +        return false; +      } +    } +    return true; +  } +    void Run() {      HLoopInformation* loop_info = induction_variable_->GetBlock()->GetLoopInformation(); -    // Must be simplified loop. -    DCHECK_EQ(loop_info->GetBackEdges().Size(), 1U);      for (HBlocksInLoopIterator it_loop(*loop_info); !it_loop.Done(); it_loop.Advance()) {        HBasicBlock* block = it_loop.Current();        DCHECK(block->IsInLoop()); -      HBasicBlock* back_edge = loop_info->GetBackEdges().Get(0); -      if (!block->Dominates(back_edge)) { +      if (!DominatesAllBackEdges(block, loop_info)) {          // In order not to trigger deoptimization unnecessarily, make sure          // that all array accesses collected are really executed in the loop.          // For array accesses in a branch inside the loop, don't collect the @@ -1151,9 +1158,26 @@ class BCEVisitor : public HGraphVisitor {      bounds_check->GetBlock()->RemoveInstruction(bounds_check);    } +  static bool HasSameInputAtBackEdges(HPhi* phi) { +    DCHECK(phi->IsLoopHeaderPhi()); +    // Start with input 1. Input 0 is from the incoming block. +    HInstruction* input1 = phi->InputAt(1); +    DCHECK(phi->GetBlock()->GetLoopInformation()->IsBackEdge( +        *phi->GetBlock()->GetPredecessors().Get(1))); +    for (size_t i = 2, e = phi->InputCount(); i < e; ++i) { +      DCHECK(phi->GetBlock()->GetLoopInformation()->IsBackEdge( +          *phi->GetBlock()->GetPredecessors().Get(i))); +      if (input1 != phi->InputAt(i)) { +        return false; +      } +    } +    return true; +  } +    void VisitPhi(HPhi* phi) { -    if (phi->IsLoopHeaderPhi() && phi->GetType() == Primitive::kPrimInt) { -      DCHECK_EQ(phi->InputCount(), 2U); +    if (phi->IsLoopHeaderPhi() +        && (phi->GetType() == Primitive::kPrimInt) +        && HasSameInputAtBackEdges(phi)) {        HInstruction* instruction = phi->InputAt(1);        HInstruction *left;        int32_t increment; diff --git a/compiler/optimizing/bounds_check_elimination_test.cc b/compiler/optimizing/bounds_check_elimination_test.cc index 97be778dbd..163458f75c 100644 --- a/compiler/optimizing/bounds_check_elimination_test.cc +++ b/compiler/optimizing/bounds_check_elimination_test.cc @@ -42,7 +42,7 @@ TEST(BoundsCheckEliminationTest, NarrowingRangeArrayBoundsElimination) {    ArenaPool pool;    ArenaAllocator allocator(&pool); -  HGraph* graph = new (&allocator) HGraph(&allocator); +  HGraph* graph = CreateGraph(&allocator);    graph->SetHasBoundsChecks(true);    HBasicBlock* entry = new (&allocator) HBasicBlock(graph); @@ -147,7 +147,7 @@ TEST(BoundsCheckEliminationTest, OverflowArrayBoundsElimination) {    ArenaPool pool;    ArenaAllocator allocator(&pool); -  HGraph* graph = new (&allocator) HGraph(&allocator); +  HGraph* graph = CreateGraph(&allocator);    graph->SetHasBoundsChecks(true);    HBasicBlock* entry = new (&allocator) HBasicBlock(graph); @@ -219,7 +219,7 @@ TEST(BoundsCheckEliminationTest, UnderflowArrayBoundsElimination) {    ArenaPool pool;    ArenaAllocator allocator(&pool); -  HGraph* graph = new (&allocator) HGraph(&allocator); +  HGraph* graph = CreateGraph(&allocator);    graph->SetHasBoundsChecks(true);    HBasicBlock* entry = new (&allocator) HBasicBlock(graph); @@ -291,7 +291,7 @@ TEST(BoundsCheckEliminationTest, ConstantArrayBoundsElimination) {    ArenaPool pool;    ArenaAllocator allocator(&pool); -  HGraph* graph = new (&allocator) HGraph(&allocator); +  HGraph* graph = CreateGraph(&allocator);    graph->SetHasBoundsChecks(true);    HBasicBlock* entry = new (&allocator) HBasicBlock(graph); @@ -364,7 +364,7 @@ static HGraph* BuildSSAGraph1(ArenaAllocator* allocator,                                int initial,                                int increment,                                IfCondition cond = kCondGE) { -  HGraph* graph = new (allocator) HGraph(allocator); +  HGraph* graph = CreateGraph(allocator);    graph->SetHasBoundsChecks(true);    HBasicBlock* entry = new (allocator) HBasicBlock(graph); @@ -501,7 +501,7 @@ static HGraph* BuildSSAGraph2(ArenaAllocator* allocator,                                int initial,                                int increment = -1,                                IfCondition cond = kCondLE) { -  HGraph* graph = new (allocator) HGraph(allocator); +  HGraph* graph = CreateGraph(allocator);    graph->SetHasBoundsChecks(true);    HBasicBlock* entry = new (allocator) HBasicBlock(graph); @@ -632,7 +632,7 @@ static HGraph* BuildSSAGraph3(ArenaAllocator* allocator,                                int initial,                                int increment,                                IfCondition cond) { -  HGraph* graph = new (allocator) HGraph(allocator); +  HGraph* graph = CreateGraph(allocator);    graph->SetHasBoundsChecks(true);    HBasicBlock* entry = new (allocator) HBasicBlock(graph); @@ -743,7 +743,7 @@ static HGraph* BuildSSAGraph4(ArenaAllocator* allocator,                                HInstruction** bounds_check,                                int initial,                                IfCondition cond = kCondGE) { -  HGraph* graph = new (allocator) HGraph(allocator); +  HGraph* graph = CreateGraph(allocator);    graph->SetHasBoundsChecks(true);    HBasicBlock* entry = new (allocator) HBasicBlock(graph); @@ -868,7 +868,7 @@ TEST(BoundsCheckEliminationTest, BubbleSortArrayBoundsElimination) {    ArenaPool pool;    ArenaAllocator allocator(&pool); -  HGraph* graph = new (&allocator) HGraph(&allocator); +  HGraph* graph = CreateGraph(&allocator);    graph->SetHasBoundsChecks(true);    HBasicBlock* entry = new (&allocator) HBasicBlock(graph); diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index 0f44af07b8..a5c6f23343 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -282,7 +282,10 @@ bool HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item) {    // To avoid splitting blocks, we compute ahead of time the instructions that    // start a new block, and create these blocks. -  ComputeBranchTargets(code_ptr, code_end, &number_of_branches); +  if (!ComputeBranchTargets(code_ptr, code_end, &number_of_branches)) { +    MaybeRecordStat(MethodCompilationStat::kNotCompiledBranchOutsideMethodCode); +    return false; +  }    // Note that the compiler driver is null when unit testing.    if ((compiler_driver_ != nullptr) && SkipCompilation(code_item, number_of_branches)) { @@ -349,7 +352,7 @@ void HGraphBuilder::MaybeUpdateCurrentBlock(size_t index) {    current_block_ = block;  } -void HGraphBuilder::ComputeBranchTargets(const uint16_t* code_ptr, +bool HGraphBuilder::ComputeBranchTargets(const uint16_t* code_ptr,                                           const uint16_t* code_end,                                           size_t* number_of_branches) {    branch_targets_.SetSize(code_end - code_ptr); @@ -374,7 +377,14 @@ void HGraphBuilder::ComputeBranchTargets(const uint16_t* code_ptr,        }        dex_pc += instruction.SizeInCodeUnits();        code_ptr += instruction.SizeInCodeUnits(); -      if ((code_ptr < code_end) && (FindBlockStartingAt(dex_pc) == nullptr)) { + +      if (code_ptr >= code_end) { +        if (instruction.CanFlowThrough()) { +          // In the normal case we should never hit this but someone can artificially forge a dex +          // file to fall-through out the method code. In this case we bail out compilation. +          return false; +        } +      } else if (FindBlockStartingAt(dex_pc) == nullptr) {          block = new (arena_) HBasicBlock(graph_, dex_pc);          branch_targets_.Put(dex_pc, block);        } @@ -406,7 +416,12 @@ void HGraphBuilder::ComputeBranchTargets(const uint16_t* code_ptr,        // Fall-through. Add a block if there is more code afterwards.        dex_pc += instruction.SizeInCodeUnits();        code_ptr += instruction.SizeInCodeUnits(); -      if ((code_ptr < code_end) && (FindBlockStartingAt(dex_pc) == nullptr)) { +      if (code_ptr >= code_end) { +        // In the normal case we should never hit this but someone can artificially forge a dex +        // file to fall-through out the method code. In this case we bail out compilation. +        // (A switch can fall-through so we don't need to check CanFlowThrough().) +        return false; +      } else if (FindBlockStartingAt(dex_pc) == nullptr) {          block = new (arena_) HBasicBlock(graph_, dex_pc);          branch_targets_.Put(dex_pc, block);        } @@ -415,6 +430,7 @@ void HGraphBuilder::ComputeBranchTargets(const uint16_t* code_ptr,        dex_pc += instruction.SizeInCodeUnits();      }    } +  return true;  }  HBasicBlock* HGraphBuilder::FindBlockStartingAt(int32_t index) const { diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index dc6d97eb0c..36503ce43a 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -88,7 +88,10 @@ class HGraphBuilder : public ValueObject {    // the newly created blocks.    // As a side effect, also compute the number of dex instructions, blocks, and    // branches. -  void ComputeBranchTargets(const uint16_t* start, +  // Returns true if all the branches fall inside the method code, false otherwise. +  // (In normal cases this should always return true but someone can artificially +  // create a code unit in which branches fall-through out of it). +  bool ComputeBranchTargets(const uint16_t* start,                              const uint16_t* end,                              size_t* number_of_branches);    void MaybeUpdateCurrentBlock(size_t index); diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index cfe121e0ec..0e776b31f7 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -100,11 +100,11 @@ static bool CheckTypeConsistency(HInstruction* instruction) {    for (size_t i = 0; i < instruction->EnvironmentSize(); ++i) {      if (environment->GetInstructionAt(i) != nullptr) {        Primitive::Type type = environment->GetInstructionAt(i)->GetType(); -      DCHECK(CheckType(type, locations->GetEnvironmentAt(i))) -        << type << " " << locations->GetEnvironmentAt(i); +      DCHECK(CheckType(type, environment->GetLocationAt(i))) +        << type << " " << environment->GetLocationAt(i);      } else { -      DCHECK(locations->GetEnvironmentAt(i).IsInvalid()) -        << locations->GetEnvironmentAt(i); +      DCHECK(environment->GetLocationAt(i).IsInvalid()) +        << environment->GetLocationAt(i);      }    }    return true; @@ -680,6 +680,11 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction,                                         locations->GetStackMask(),                                         environment_size,                                         inlining_depth); +  if (environment != nullptr) { +    // TODO: Handle parent environment. +    DCHECK(environment->GetParent() == nullptr); +    DCHECK_EQ(environment->GetDexPc(), dex_pc); +  }    // Walk over the environment, and record the location of dex registers.    for (size_t i = 0; i < environment_size; ++i) { @@ -689,7 +694,7 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction,        continue;      } -    Location location = locations->GetEnvironmentAt(i); +    Location location = environment->GetLocationAt(i);      switch (location.GetKind()) {        case Location::kConstant: {          DCHECK_EQ(current, location.GetConstant()); diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index e4c37deb8b..f56e446605 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -112,6 +112,10 @@ class SuspendCheckSlowPathARM : public SlowPathCodeARM {      return &return_label_;    } +  HBasicBlock* GetSuccessor() const { +    return successor_; +  } +   private:    HSuspendCheck* const instruction_;    // If not null, the block to branch to after the suspend check. @@ -3539,8 +3543,18 @@ void InstructionCodeGeneratorARM::VisitSuspendCheck(HSuspendCheck* instruction)  void InstructionCodeGeneratorARM::GenerateSuspendCheck(HSuspendCheck* instruction,                                                         HBasicBlock* successor) {    SuspendCheckSlowPathARM* slow_path = -      new (GetGraph()->GetArena()) SuspendCheckSlowPathARM(instruction, successor); -  codegen_->AddSlowPath(slow_path); +      down_cast<SuspendCheckSlowPathARM*>(instruction->GetSlowPath()); +  if (slow_path == nullptr) { +    slow_path = new (GetGraph()->GetArena()) SuspendCheckSlowPathARM(instruction, successor); +    instruction->SetSlowPath(slow_path); +    codegen_->AddSlowPath(slow_path); +    if (successor != nullptr) { +      DCHECK(successor->IsLoopHeader()); +      codegen_->ClearSpillSlotsFromLoopPhisInStackMap(instruction); +    } +  } else { +    DCHECK_EQ(slow_path->GetSuccessor(), successor); +  }    __ LoadFromOffset(        kLoadUnsignedHalfword, IP, TR, Thread::ThreadFlagsOffset<kArmWordSize>().Int32Value()); diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 9e02a1d850..0222f93da4 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -285,6 +285,10 @@ class SuspendCheckSlowPathARM64 : public SlowPathCodeARM64 {      return &return_label_;    } +  HBasicBlock* GetSuccessor() const { +    return successor_; +  } +   private:    HSuspendCheck* const instruction_;    // If not null, the block to branch to after the suspend check. @@ -1034,8 +1038,19 @@ void InstructionCodeGeneratorARM64::GenerateMemoryBarrier(MemBarrierKind kind) {  void InstructionCodeGeneratorARM64::GenerateSuspendCheck(HSuspendCheck* instruction,                                                           HBasicBlock* successor) {    SuspendCheckSlowPathARM64* slow_path = -    new (GetGraph()->GetArena()) SuspendCheckSlowPathARM64(instruction, successor); -  codegen_->AddSlowPath(slow_path); +      down_cast<SuspendCheckSlowPathARM64*>(instruction->GetSlowPath()); +  if (slow_path == nullptr) { +    slow_path = new (GetGraph()->GetArena()) SuspendCheckSlowPathARM64(instruction, successor); +    instruction->SetSlowPath(slow_path); +    codegen_->AddSlowPath(slow_path); +    if (successor != nullptr) { +      DCHECK(successor->IsLoopHeader()); +      codegen_->ClearSpillSlotsFromLoopPhisInStackMap(instruction); +    } +  } else { +    DCHECK_EQ(slow_path->GetSuccessor(), successor); +  } +    UseScratchRegisterScope temps(codegen_->GetVIXLAssembler());    Register temp = temps.AcquireW(); diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 8aa77969fc..2848a48a64 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -153,6 +153,10 @@ class SuspendCheckSlowPathX86 : public SlowPathCodeX86 {      return &return_label_;    } +  HBasicBlock* GetSuccessor() const { +    return successor_; +  } +   private:    HSuspendCheck* const instruction_;    HBasicBlock* const successor_; @@ -809,7 +813,6 @@ void InstructionCodeGeneratorX86::VisitGoto(HGoto* got) {    HLoopInformation* info = block->GetLoopInformation();    if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) { -    codegen_->ClearSpillSlotsFromLoopPhisInStackMap(info->GetSuspendCheck());      GenerateSuspendCheck(info->GetSuspendCheck(), successor);      return;    } @@ -2827,7 +2830,11 @@ void InstructionCodeGeneratorX86::HandleShift(HBinaryOperation* op) {  void InstructionCodeGeneratorX86::GenerateShlLong(const Location& loc, int shift) {    Register low = loc.AsRegisterPairLow<Register>();    Register high = loc.AsRegisterPairHigh<Register>(); -  if (shift == 32) { +  if (shift == 1) { +    // This is just an addition. +    __ addl(low, low); +    __ adcl(high, high); +  } else if (shift == 32) {      // Shift by 32 is easy. High gets low, and low gets 0.      codegen_->EmitParallelMoves(          loc.ToLow(), @@ -3993,8 +4000,19 @@ void InstructionCodeGeneratorX86::VisitSuspendCheck(HSuspendCheck* instruction)  void InstructionCodeGeneratorX86::GenerateSuspendCheck(HSuspendCheck* instruction,                                                         HBasicBlock* successor) {    SuspendCheckSlowPathX86* slow_path = -      new (GetGraph()->GetArena()) SuspendCheckSlowPathX86(instruction, successor); -  codegen_->AddSlowPath(slow_path); +      down_cast<SuspendCheckSlowPathX86*>(instruction->GetSlowPath()); +  if (slow_path == nullptr) { +    slow_path = new (GetGraph()->GetArena()) SuspendCheckSlowPathX86(instruction, successor); +    instruction->SetSlowPath(slow_path); +    codegen_->AddSlowPath(slow_path); +    if (successor != nullptr) { +      DCHECK(successor->IsLoopHeader()); +      codegen_->ClearSpillSlotsFromLoopPhisInStackMap(instruction); +    } +  } else { +    DCHECK_EQ(slow_path->GetSuccessor(), successor); +  } +    __ fs()->cmpw(Address::Absolute(        Thread::ThreadFlagsOffset<kX86WordSize>().Int32Value()), Immediate(0));    if (successor == nullptr) { diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 5ac68668ba..e633970279 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -99,7 +99,7 @@ class DivRemMinusOneSlowPathX86_64 : public SlowPathCodeX86_64 {        if (is_div_) {          __ negq(cpu_reg_);        } else { -        __ movq(cpu_reg_, Immediate(0)); +        __ xorl(cpu_reg_, cpu_reg_);        }      }      __ jmp(GetExitLabel()); @@ -136,6 +136,10 @@ class SuspendCheckSlowPathX86_64 : public SlowPathCodeX86_64 {      return &return_label_;    } +  HBasicBlock* GetSuccessor() const { +    return successor_; +  } +   private:    HSuspendCheck* const instruction_;    HBasicBlock* const successor_; @@ -671,7 +675,7 @@ void CodeGeneratorX86_64::Move(Location destination, Location source) {          DCHECK(constant->IsLongConstant());          value = constant->AsLongConstant()->GetValue();        } -      __ movq(CpuRegister(TMP), Immediate(value)); +      Load64BitValue(CpuRegister(TMP), value);        __ movq(Address(CpuRegister(RSP), destination.GetStackIndex()), CpuRegister(TMP));      } else {        DCHECK(source.IsDoubleStackSlot()); @@ -704,9 +708,9 @@ void CodeGeneratorX86_64::Move(HInstruction* instruction,      } else if (const_to_move->IsLongConstant()) {        int64_t value = const_to_move->AsLongConstant()->GetValue();        if (location.IsRegister()) { -        __ movq(location.AsRegister<CpuRegister>(), Immediate(value)); +        Load64BitValue(location.AsRegister<CpuRegister>(), value);        } else if (location.IsDoubleStackSlot()) { -        __ movq(CpuRegister(TMP), Immediate(value)); +        Load64BitValue(CpuRegister(TMP), value);          __ movq(Address(CpuRegister(RSP), location.GetStackIndex()), CpuRegister(TMP));        } else {          DCHECK(location.IsConstant()); @@ -771,7 +775,6 @@ void InstructionCodeGeneratorX86_64::VisitGoto(HGoto* got) {    HLoopInformation* info = block->GetLoopInformation();    if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) { -    codegen_->ClearSpillSlotsFromLoopPhisInStackMap(info->GetSuspendCheck());      GenerateSuspendCheck(info->GetSuspendCheck(), successor);      return;    } @@ -956,7 +959,7 @@ void InstructionCodeGeneratorX86_64::VisitCondition(HCondition* comp) {      LocationSummary* locations = comp->GetLocations();      CpuRegister reg = locations->Out().AsRegister<CpuRegister>();      // Clear register: setcc only sets the low byte. -    __ xorq(reg, reg); +    __ xorl(reg, reg);      Location lhs = locations->InAt(0);      Location rhs = locations->InAt(1);      if (rhs.IsRegister()) { @@ -1419,8 +1422,8 @@ void InstructionCodeGeneratorX86_64::VisitInvokeInterface(HInvokeInterface* invo    size_t class_offset = mirror::Object::ClassOffset().SizeValue();    // Set the hidden argument. -  __ movq(invoke->GetLocations()->GetTemp(1).AsRegister<CpuRegister>(), -          Immediate(invoke->GetDexMethodIndex())); +  CpuRegister hidden_reg = invoke->GetLocations()->GetTemp(1).AsRegister<CpuRegister>(); +  codegen_->Load64BitValue(hidden_reg, invoke->GetDexMethodIndex());    // temp = object->GetClass();    if (receiver.IsStackSlot()) { @@ -1856,7 +1859,7 @@ void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conver            XmmRegister temp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();            Label done, nan; -          __ movq(output, Immediate(kPrimLongMax)); +          codegen_->Load64BitValue(output, kPrimLongMax);            // temp = long-to-float(output)            __ cvtsi2ss(temp, output, true);            // if input >= temp goto done @@ -1869,7 +1872,7 @@ void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conver            __ jmp(&done);            __ Bind(&nan);            //  output = 0 -          __ xorq(output, output); +          __ xorl(output, output);            __ Bind(&done);            break;          } @@ -1881,7 +1884,7 @@ void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conver            XmmRegister temp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();            Label done, nan; -          __ movq(output, Immediate(kPrimLongMax)); +          codegen_->Load64BitValue(output, kPrimLongMax);            // temp = long-to-double(output)            __ cvtsi2sd(temp, output, true);            // if input >= temp goto done @@ -1894,7 +1897,7 @@ void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conver            __ jmp(&done);            __ Bind(&nan);            //  output = 0 -          __ xorq(output, output); +          __ xorl(output, output);            __ Bind(&done);            break;          } @@ -2483,7 +2486,7 @@ void InstructionCodeGeneratorX86_64::DivRemOneOrMinusOne(HBinaryOperation* instr      case Primitive::kPrimLong: {        if (instruction->IsRem()) { -        __ xorq(output_register, output_register); +        __ xorl(output_register, output_register);        } else {          __ movq(output_register, input_register);          if (imm == -1) { @@ -2527,7 +2530,7 @@ void InstructionCodeGeneratorX86_64::DivByPowerOfTwo(HDiv* instruction) {      DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong);      CpuRegister rdx = locations->GetTemp(0).AsRegister<CpuRegister>(); -    __ movq(rdx, Immediate(std::abs(imm) - 1)); +    codegen_->Load64BitValue(rdx, std::abs(imm) - 1);      __ addq(rdx, numerator);      __ testq(numerator, numerator);      __ cmov(kGreaterEqual, rdx, numerator); @@ -2624,7 +2627,7 @@ void InstructionCodeGeneratorX86_64::GenerateDivRemWithAnyConstant(HBinaryOperat      __ movq(numerator, rax);      // RAX = magic -    __ movq(rax, Immediate(magic)); +    codegen_->Load64BitValue(rax, magic);      // RDX:RAX = magic * numerator      __ imulq(numerator); @@ -2653,8 +2656,7 @@ void InstructionCodeGeneratorX86_64::GenerateDivRemWithAnyConstant(HBinaryOperat        if (IsInt<32>(imm)) {          __ imulq(rdx, Immediate(static_cast<int32_t>(imm)));        } else { -        __ movq(numerator, Immediate(imm)); -        __ imulq(rdx, numerator); +        __ imulq(rdx, codegen_->LiteralInt64Address(imm));        }        __ subq(rax, rdx); @@ -3020,8 +3022,8 @@ void LocationsBuilderX86_64::VisitNewInstance(HNewInstance* instruction) {  void InstructionCodeGeneratorX86_64::VisitNewInstance(HNewInstance* instruction) {    InvokeRuntimeCallingConvention calling_convention;    codegen_->LoadCurrentMethod(CpuRegister(calling_convention.GetRegisterAt(1))); -  __ movq(CpuRegister(calling_convention.GetRegisterAt(0)), Immediate(instruction->GetTypeIndex())); - +  codegen_->Load64BitValue(CpuRegister(calling_convention.GetRegisterAt(0)), +                           instruction->GetTypeIndex());    __ gs()->call(        Address::Absolute(GetThreadOffset<kX86_64WordSize>(instruction->GetEntrypoint()), true)); @@ -3042,7 +3044,8 @@ void LocationsBuilderX86_64::VisitNewArray(HNewArray* instruction) {  void InstructionCodeGeneratorX86_64::VisitNewArray(HNewArray* instruction) {    InvokeRuntimeCallingConvention calling_convention;    codegen_->LoadCurrentMethod(CpuRegister(calling_convention.GetRegisterAt(2))); -  __ movq(CpuRegister(calling_convention.GetRegisterAt(0)), Immediate(instruction->GetTypeIndex())); +  codegen_->Load64BitValue(CpuRegister(calling_convention.GetRegisterAt(0)), +                           instruction->GetTypeIndex());    __ gs()->call(        Address::Absolute(GetThreadOffset<kX86_64WordSize>(instruction->GetEntrypoint()), true)); @@ -3864,8 +3867,19 @@ void InstructionCodeGeneratorX86_64::VisitSuspendCheck(HSuspendCheck* instructio  void InstructionCodeGeneratorX86_64::GenerateSuspendCheck(HSuspendCheck* instruction,                                                            HBasicBlock* successor) {    SuspendCheckSlowPathX86_64* slow_path = -      new (GetGraph()->GetArena()) SuspendCheckSlowPathX86_64(instruction, successor); -  codegen_->AddSlowPath(slow_path); +      down_cast<SuspendCheckSlowPathX86_64*>(instruction->GetSlowPath()); +  if (slow_path == nullptr) { +    slow_path = new (GetGraph()->GetArena()) SuspendCheckSlowPathX86_64(instruction, successor); +    instruction->SetSlowPath(slow_path); +    codegen_->AddSlowPath(slow_path); +    if (successor != nullptr) { +      DCHECK(successor->IsLoopHeader()); +      codegen_->ClearSpillSlotsFromLoopPhisInStackMap(instruction); +    } +  } else { +    DCHECK_EQ(slow_path->GetSuccessor(), successor); +  } +    __ gs()->cmpw(Address::Absolute(        Thread::ThreadFlagsOffset<kX86_64WordSize>().Int32Value(), true), Immediate(0));    if (successor == nullptr) { @@ -3938,45 +3952,42 @@ void ParallelMoveResolverX86_64::EmitMove(size_t index) {      } else if (constant->IsLongConstant()) {        int64_t value = constant->AsLongConstant()->GetValue();        if (destination.IsRegister()) { -        __ movq(destination.AsRegister<CpuRegister>(), Immediate(value)); +        codegen_->Load64BitValue(destination.AsRegister<CpuRegister>(), value);        } else {          DCHECK(destination.IsDoubleStackSlot()) << destination; -        __ movq(CpuRegister(TMP), Immediate(value)); +        codegen_->Load64BitValue(CpuRegister(TMP), value);          __ movq(Address(CpuRegister(RSP), destination.GetStackIndex()), CpuRegister(TMP));        }      } else if (constant->IsFloatConstant()) {        float fp_value = constant->AsFloatConstant()->GetValue();        int32_t value = bit_cast<int32_t, float>(fp_value); -      Immediate imm(value);        if (destination.IsFpuRegister()) {          XmmRegister dest = destination.AsFpuRegister<XmmRegister>();          if (value == 0) {            // easy FP 0.0.            __ xorps(dest, dest);          } else { -          __ movl(CpuRegister(TMP), imm); -          __ movd(dest, CpuRegister(TMP)); +          __ movss(dest, codegen_->LiteralFloatAddress(fp_value));          }        } else {          DCHECK(destination.IsStackSlot()) << destination; +        Immediate imm(value);          __ movl(Address(CpuRegister(RSP), destination.GetStackIndex()), imm);        }      } else {        DCHECK(constant->IsDoubleConstant()) << constant->DebugName();        double fp_value =  constant->AsDoubleConstant()->GetValue();        int64_t value = bit_cast<int64_t, double>(fp_value); -      Immediate imm(value);        if (destination.IsFpuRegister()) {          XmmRegister dest = destination.AsFpuRegister<XmmRegister>();          if (value == 0) {            __ xorpd(dest, dest);          } else { -          __ movq(CpuRegister(TMP), imm); -          __ movd(dest, CpuRegister(TMP)); +          __ movsd(dest, codegen_->LiteralDoubleAddress(fp_value));          }        } else {          DCHECK(destination.IsDoubleStackSlot()) << destination; -        __ movq(CpuRegister(TMP), imm); +        codegen_->Load64BitValue(CpuRegister(TMP), value);          __ movq(Address(CpuRegister(RSP), destination.GetStackIndex()), CpuRegister(TMP));        }      } @@ -4435,6 +4446,17 @@ void InstructionCodeGeneratorX86_64::VisitBoundType(HBoundType* instruction) {    LOG(FATAL) << "Unreachable";  } +void CodeGeneratorX86_64::Load64BitValue(CpuRegister dest, int64_t value) { +  if (value == 0) { +    __ xorl(dest, dest); +  } else if (value > 0 && IsInt<32>(value)) { +    // We can use a 32 bit move, as it will zero-extend and is one byte shorter. +    __ movl(dest, Immediate(static_cast<int32_t>(value))); +  } else { +    __ movq(dest, Immediate(value)); +  } +} +  void CodeGeneratorX86_64::Finalize(CodeAllocator* allocator) {    // Generate the constant area if needed.    X86_64Assembler* assembler = GetAssembler(); diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index 13f9c46b5e..480ea6b9c9 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -282,6 +282,9 @@ class CodeGeneratorX86_64 : public CodeGenerator {    Address LiteralInt32Address(int32_t v);    Address LiteralInt64Address(int64_t v); +  // Load a 64 bit value into a register in the most efficient manner. +  void Load64BitValue(CpuRegister dest, int64_t value); +   private:    // Labels for each block that will be compiled.    GrowableArray<Label> block_labels_; diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc index 94f56e5d3e..bfed1a89de 100644 --- a/compiler/optimizing/codegen_test.cc +++ b/compiler/optimizing/codegen_test.cc @@ -225,7 +225,7 @@ static void RunCodeOptimized(HGraph* graph,  static void TestCode(const uint16_t* data, bool has_result = false, int32_t expected = 0) {    ArenaPool pool;    ArenaAllocator arena(&pool); -  HGraph* graph = new (&arena) HGraph(&arena); +  HGraph* graph = CreateGraph(&arena);    HGraphBuilder builder(graph);    const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);    bool graph_built = builder.BuildGraph(*item); @@ -238,7 +238,7 @@ static void TestCode(const uint16_t* data, bool has_result = false, int32_t expe  static void TestCodeLong(const uint16_t* data, bool has_result, int64_t expected) {    ArenaPool pool;    ArenaAllocator arena(&pool); -  HGraph* graph = new (&arena) HGraph(&arena); +  HGraph* graph = CreateGraph(&arena);    HGraphBuilder builder(graph, Primitive::kPrimLong);    const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);    bool graph_built = builder.BuildGraph(*item); @@ -504,7 +504,7 @@ TEST(CodegenTest, NonMaterializedCondition) {    ArenaPool pool;    ArenaAllocator allocator(&pool); -  HGraph* graph = new (&allocator) HGraph(&allocator); +  HGraph* graph = CreateGraph(&allocator);    HBasicBlock* entry = new (&allocator) HBasicBlock(graph);    graph->AddBlock(entry);    graph->SetEntryBlock(entry); @@ -623,7 +623,7 @@ TEST(CodegenTest, MaterializedCondition1) {    for (size_t i = 0; i < arraysize(lhs); i++) {      ArenaPool pool;      ArenaAllocator allocator(&pool); -    HGraph* graph = new (&allocator) HGraph(&allocator); +    HGraph* graph = CreateGraph(&allocator);      HBasicBlock* entry_block = new (&allocator) HBasicBlock(graph);      graph->AddBlock(entry_block); @@ -669,7 +669,7 @@ TEST(CodegenTest, MaterializedCondition2) {    for (size_t i = 0; i < arraysize(lhs); i++) {      ArenaPool pool;      ArenaAllocator allocator(&pool); -    HGraph* graph = new (&allocator) HGraph(&allocator); +    HGraph* graph = CreateGraph(&allocator);      HBasicBlock* entry_block = new (&allocator) HBasicBlock(graph);      graph->AddBlock(entry_block); diff --git a/compiler/optimizing/dominator_test.cc b/compiler/optimizing/dominator_test.cc index 61a7697301..78ae1dd960 100644 --- a/compiler/optimizing/dominator_test.cc +++ b/compiler/optimizing/dominator_test.cc @@ -27,7 +27,7 @@ namespace art {  static void TestCode(const uint16_t* data, const int* blocks, size_t blocks_length) {    ArenaPool pool;    ArenaAllocator allocator(&pool); -  HGraph* graph = new (&allocator) HGraph(&allocator); +  HGraph* graph = CreateGraph(&allocator);    HGraphBuilder builder(graph);    const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);    bool graph_built = builder.BuildGraph(*item); diff --git a/compiler/optimizing/find_loops_test.cc b/compiler/optimizing/find_loops_test.cc index 2bfecc696a..29aa97a83a 100644 --- a/compiler/optimizing/find_loops_test.cc +++ b/compiler/optimizing/find_loops_test.cc @@ -28,7 +28,7 @@  namespace art {  static HGraph* TestCode(const uint16_t* data, ArenaAllocator* allocator) { -  HGraph* graph = new (allocator) HGraph(allocator); +  HGraph* graph = CreateGraph(allocator);    HGraphBuilder builder(graph);    const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);    builder.BuildGraph(*item); @@ -235,14 +235,13 @@ TEST(FindLoopsTest, Loop4) {    TestBlock(graph, 0, false, -1);            // entry block    TestBlock(graph, 1, false, -1);            // pre header -  const int blocks2[] = {2, 3, 4, 5, 8}; -  TestBlock(graph, 2, true, 2, blocks2, 5);  // loop header +  const int blocks2[] = {2, 3, 4, 5}; +  TestBlock(graph, 2, true, 2, blocks2, arraysize(blocks2));  // loop header    TestBlock(graph, 3, false, 2);             // block in loop -  TestBlock(graph, 4, false, 2);             // original back edge -  TestBlock(graph, 5, false, 2);             // original back edge +  TestBlock(graph, 4, false, 2);             // back edge +  TestBlock(graph, 5, false, 2);             // back edge    TestBlock(graph, 6, false, -1);            // return block    TestBlock(graph, 7, false, -1);            // exit block -  TestBlock(graph, 8, false, 2);             // synthesized back edge  } diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc index 8ea8f3cd79..fd28f0b83f 100644 --- a/compiler/optimizing/graph_checker.cc +++ b/compiler/optimizing/graph_checker.cc @@ -288,6 +288,7 @@ void SSAChecker::VisitBasicBlock(HBasicBlock* block) {  void SSAChecker::CheckLoop(HBasicBlock* loop_header) {    int id = loop_header->GetBlockId(); +  HLoopInformation* loop_information = loop_header->GetLoopInformation();    // Ensure the pre-header block is first in the list of    // predecessors of a loop header. @@ -297,57 +298,48 @@ void SSAChecker::CheckLoop(HBasicBlock* loop_header) {          id));    } -  // Ensure the loop header has only two predecessors and that only the -  // second one is a back edge. +  // Ensure the loop header has only one incoming branch and the remaining +  // predecessors are back edges.    size_t num_preds = loop_header->GetPredecessors().Size();    if (num_preds < 2) {      AddError(StringPrintf(          "Loop header %d has less than two predecessors: %zu.",          id,          num_preds)); -  } else if (num_preds > 2) { -    AddError(StringPrintf( -        "Loop header %d has more than two predecessors: %zu.", -        id, -        num_preds));    } else { -    HLoopInformation* loop_information = loop_header->GetLoopInformation();      HBasicBlock* first_predecessor = loop_header->GetPredecessors().Get(0);      if (loop_information->IsBackEdge(*first_predecessor)) {        AddError(StringPrintf(            "First predecessor of loop header %d is a back edge.",            id));      } -    HBasicBlock* second_predecessor = loop_header->GetPredecessors().Get(1); -    if (!loop_information->IsBackEdge(*second_predecessor)) { -      AddError(StringPrintf( -          "Second predecessor of loop header %d is not a back edge.", -          id)); +    for (size_t i = 1, e = loop_header->GetPredecessors().Size(); i < e; ++i) { +      HBasicBlock* predecessor = loop_header->GetPredecessors().Get(i); +      if (!loop_information->IsBackEdge(*predecessor)) { +        AddError(StringPrintf( +            "Loop header %d has multiple incoming (non back edge) blocks.", +            id)); +      }      }    } -  const ArenaBitVector& loop_blocks = loop_header->GetLoopInformation()->GetBlocks(); +  const ArenaBitVector& loop_blocks = loop_information->GetBlocks(); -  // Ensure there is only one back edge per loop. -  size_t num_back_edges = -    loop_header->GetLoopInformation()->GetBackEdges().Size(); +  // Ensure back edges belong to the loop. +  size_t num_back_edges = loop_information->GetBackEdges().Size();    if (num_back_edges == 0) {      AddError(StringPrintf(          "Loop defined by header %d has no back edge.",          id)); -  } else if (num_back_edges > 1) { -    AddError(StringPrintf( -        "Loop defined by header %d has several back edges: %zu.", -        id, -        num_back_edges));    } else { -    DCHECK_EQ(num_back_edges, 1u); -    int back_edge_id = loop_header->GetLoopInformation()->GetBackEdges().Get(0)->GetBlockId(); -    if (!loop_blocks.IsBitSet(back_edge_id)) { -      AddError(StringPrintf( -          "Loop defined by header %d has an invalid back edge %d.", -          id, -          back_edge_id)); +    for (size_t i = 0; i < num_back_edges; ++i) { +      int back_edge_id = loop_information->GetBackEdges().Get(i)->GetBlockId(); +      if (!loop_blocks.IsBitSet(back_edge_id)) { +        AddError(StringPrintf( +            "Loop defined by header %d has an invalid back edge %d.", +            id, +            back_edge_id)); +      }      }    } @@ -394,8 +386,9 @@ void SSAChecker::VisitInstruction(HInstruction* instruction) {    // Ensure an instruction having an environment is dominated by the    // instructions contained in the environment. -  HEnvironment* environment = instruction->GetEnvironment(); -  if (environment != nullptr) { +  for (HEnvironment* environment = instruction->GetEnvironment(); +       environment != nullptr; +       environment = environment->GetParent()) {      for (size_t i = 0, e = environment->Size(); i < e; ++i) {        HInstruction* env_instruction = environment->GetInstructionAt(i);        if (env_instruction != nullptr diff --git a/compiler/optimizing/graph_checker_test.cc b/compiler/optimizing/graph_checker_test.cc index 923468ff16..eca0d9344f 100644 --- a/compiler/optimizing/graph_checker_test.cc +++ b/compiler/optimizing/graph_checker_test.cc @@ -30,7 +30,7 @@ namespace art {   *     1: Exit   */  HGraph* CreateSimpleCFG(ArenaAllocator* allocator) { -  HGraph* graph = new (allocator) HGraph(allocator); +  HGraph* graph = CreateGraph(allocator);    HBasicBlock* entry_block = new (allocator) HBasicBlock(graph);    entry_block->AddInstruction(new (allocator) HGoto());    graph->AddBlock(entry_block); diff --git a/compiler/optimizing/graph_test.cc b/compiler/optimizing/graph_test.cc index 50398b4790..59d50926ad 100644 --- a/compiler/optimizing/graph_test.cc +++ b/compiler/optimizing/graph_test.cc @@ -73,7 +73,7 @@ TEST(GraphTest, IfSuccessorSimpleJoinBlock1) {    ArenaPool pool;    ArenaAllocator allocator(&pool); -  HGraph* graph = new (&allocator) HGraph(&allocator); +  HGraph* graph = CreateGraph(&allocator);    HBasicBlock* entry_block = createEntryBlock(graph, &allocator);    HBasicBlock* if_block = createIfBlock(graph, &allocator);    HBasicBlock* if_true = createGotoBlock(graph, &allocator); @@ -108,7 +108,7 @@ TEST(GraphTest, IfSuccessorSimpleJoinBlock2) {    ArenaPool pool;    ArenaAllocator allocator(&pool); -  HGraph* graph = new (&allocator) HGraph(&allocator); +  HGraph* graph = CreateGraph(&allocator);    HBasicBlock* entry_block = createEntryBlock(graph, &allocator);    HBasicBlock* if_block = createIfBlock(graph, &allocator);    HBasicBlock* if_false = createGotoBlock(graph, &allocator); @@ -143,7 +143,7 @@ TEST(GraphTest, IfSuccessorMultipleBackEdges1) {    ArenaPool pool;    ArenaAllocator allocator(&pool); -  HGraph* graph = new (&allocator) HGraph(&allocator); +  HGraph* graph = CreateGraph(&allocator);    HBasicBlock* entry_block = createEntryBlock(graph, &allocator);    HBasicBlock* if_block = createIfBlock(graph, &allocator);    HBasicBlock* return_block = createReturnBlock(graph, &allocator); @@ -178,7 +178,7 @@ TEST(GraphTest, IfSuccessorMultipleBackEdges2) {    ArenaPool pool;    ArenaAllocator allocator(&pool); -  HGraph* graph = new (&allocator) HGraph(&allocator); +  HGraph* graph = CreateGraph(&allocator);    HBasicBlock* entry_block = createEntryBlock(graph, &allocator);    HBasicBlock* if_block = createIfBlock(graph, &allocator);    HBasicBlock* return_block = createReturnBlock(graph, &allocator); @@ -213,7 +213,7 @@ TEST(GraphTest, IfSuccessorMultiplePreHeaders1) {    ArenaPool pool;    ArenaAllocator allocator(&pool); -  HGraph* graph = new (&allocator) HGraph(&allocator); +  HGraph* graph = CreateGraph(&allocator);    HBasicBlock* entry_block = createEntryBlock(graph, &allocator);    HBasicBlock* first_if_block = createIfBlock(graph, &allocator);    HBasicBlock* if_block = createIfBlock(graph, &allocator); @@ -252,7 +252,7 @@ TEST(GraphTest, IfSuccessorMultiplePreHeaders2) {    ArenaPool pool;    ArenaAllocator allocator(&pool); -  HGraph* graph = new (&allocator) HGraph(&allocator); +  HGraph* graph = CreateGraph(&allocator);    HBasicBlock* entry_block = createEntryBlock(graph, &allocator);    HBasicBlock* first_if_block = createIfBlock(graph, &allocator);    HBasicBlock* if_block = createIfBlock(graph, &allocator); @@ -288,7 +288,7 @@ TEST(GraphTest, InsertInstructionBefore) {    ArenaPool pool;    ArenaAllocator allocator(&pool); -  HGraph* graph = new (&allocator) HGraph(&allocator); +  HGraph* graph = CreateGraph(&allocator);    HBasicBlock* block = createGotoBlock(graph, &allocator);    HInstruction* got = block->GetLastInstruction();    ASSERT_TRUE(got->IsControlFlow()); diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index ca9cbc3d01..7130127136 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -211,17 +211,22 @@ class HGraphVisualizerPrinter : public HGraphVisitor {        output_ << "]";      }      if (instruction->HasEnvironment()) { -      HEnvironment* env = instruction->GetEnvironment(); -      output_ << " (env: [ "; -      for (size_t i = 0, e = env->Size(); i < e; ++i) { -        HInstruction* insn = env->GetInstructionAt(i); -        if (insn != nullptr) { -          output_ << GetTypeId(insn->GetType()) << insn->GetId() << " "; -        } else { -          output_ << " _ "; +      output_ << " (env:"; +      for (HEnvironment* environment = instruction->GetEnvironment(); +           environment != nullptr; +           environment = environment->GetParent()) { +        output_ << " [ "; +        for (size_t i = 0, e = environment->Size(); i < e; ++i) { +          HInstruction* insn = environment->GetInstructionAt(i); +          if (insn != nullptr) { +            output_ << GetTypeId(insn->GetType()) << insn->GetId() << " "; +          } else { +            output_ << " _ "; +          }          } +        output_ << "]";        } -      output_ << "])"; +      output_ << ")";      }      if (IsPass(SsaLivenessAnalysis::kLivenessPassName)          && is_after_pass_ diff --git a/compiler/optimizing/gvn_test.cc b/compiler/optimizing/gvn_test.cc index a81d49aa0c..c3ce7e142a 100644 --- a/compiler/optimizing/gvn_test.cc +++ b/compiler/optimizing/gvn_test.cc @@ -29,7 +29,7 @@ TEST(GVNTest, LocalFieldElimination) {    ArenaPool pool;    ArenaAllocator allocator(&pool); -  HGraph* graph = new (&allocator) HGraph(&allocator); +  HGraph* graph = CreateGraph(&allocator);    HBasicBlock* entry = new (&allocator) HBasicBlock(graph);    graph->AddBlock(entry);    graph->SetEntryBlock(entry); @@ -78,7 +78,7 @@ TEST(GVNTest, GlobalFieldElimination) {    ArenaPool pool;    ArenaAllocator allocator(&pool); -  HGraph* graph = new (&allocator) HGraph(&allocator); +  HGraph* graph = CreateGraph(&allocator);    HBasicBlock* entry = new (&allocator) HBasicBlock(graph);    graph->AddBlock(entry);    graph->SetEntryBlock(entry); @@ -133,7 +133,7 @@ TEST(GVNTest, LoopFieldElimination) {    ArenaPool pool;    ArenaAllocator allocator(&pool); -  HGraph* graph = new (&allocator) HGraph(&allocator); +  HGraph* graph = CreateGraph(&allocator);    HBasicBlock* entry = new (&allocator) HBasicBlock(graph);    graph->AddBlock(entry);    graph->SetEntryBlock(entry); @@ -220,7 +220,7 @@ TEST(GVNTest, LoopSideEffects) {    ArenaPool pool;    ArenaAllocator allocator(&pool); -  HGraph* graph = new (&allocator) HGraph(&allocator); +  HGraph* graph = CreateGraph(&allocator);    HBasicBlock* entry = new (&allocator) HBasicBlock(graph);    graph->AddBlock(entry);    graph->SetEntryBlock(entry); diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index ada32db047..afffc7ab4f 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -170,7 +170,11 @@ bool HInliner::TryBuildAndInline(Handle<mirror::ArtMethod> resolved_method,      nullptr);    HGraph* callee_graph = new (graph_->GetArena()) HGraph( -      graph_->GetArena(), graph_->IsDebuggable(), graph_->GetCurrentInstructionId()); +      graph_->GetArena(), +      caller_dex_file, +      method_index, +      graph_->IsDebuggable(), +      graph_->GetCurrentInstructionId());    OptimizingCompilerStats inline_stats;    HGraphBuilder builder(callee_graph, diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index e79d4f4bdc..46fad17b8f 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -137,13 +137,25 @@ void InstructionSimplifierVisitor::VisitShift(HBinaryOperation* instruction) {    HConstant* input_cst = instruction->GetConstantRight();    HInstruction* input_other = instruction->GetLeastConstantLeft(); -  if ((input_cst != nullptr) && input_cst->IsZero()) { -    // Replace code looking like -    //    SHL dst, src, 0 -    // with -    //    src -    instruction->ReplaceWith(input_other); -    instruction->GetBlock()->RemoveInstruction(instruction); +  if (input_cst != nullptr) { +    if (input_cst->IsZero()) { +      // Replace code looking like +      //    SHL dst, src, 0 +      // with +      //    src +      instruction->ReplaceWith(input_other); +      instruction->GetBlock()->RemoveInstruction(instruction); +    } else if (instruction->IsShl() && input_cst->IsOne()) { +      // Replace Shl looking like +      //    SHL dst, src, 1 +      // with +      //    ADD dst, src, src +      HAdd *add = new(GetGraph()->GetArena()) HAdd(instruction->GetType(), +                                                   input_other, +                                                   input_other); +      instruction->GetBlock()->ReplaceAndRemoveInstructionWith(instruction, add); +      RecordSimplification(); +    }    }  } diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index 1eef1eff0b..28b7a07cf9 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -16,6 +16,8 @@  #include "intrinsics_x86.h" +#include <limits> +  #include "arch/x86/instruction_set_features_x86.h"  #include "code_generator_x86.h"  #include "entrypoints/quick/quick_entrypoints.h" @@ -124,11 +126,8 @@ static void MoveArguments(HInvoke* invoke, CodeGeneratorX86* codegen) {  //       restored!  class IntrinsicSlowPathX86 : public SlowPathCodeX86 {   public: -  explicit IntrinsicSlowPathX86(HInvoke* invoke, Register temp) -    : invoke_(invoke) { -      // The temporary register has to be EAX for x86 invokes. -      DCHECK_EQ(temp, EAX); -    } +  explicit IntrinsicSlowPathX86(HInvoke* invoke) +    : invoke_(invoke) { }    void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE {      CodeGeneratorX86* codegen = down_cast<CodeGeneratorX86*>(codegen_in); @@ -880,8 +879,6 @@ void IntrinsicLocationsBuilderX86::VisitStringCharAt(HInvoke* invoke) {    locations->SetInAt(0, Location::RequiresRegister());    locations->SetInAt(1, Location::RequiresRegister());    locations->SetOut(Location::SameAsFirstInput()); -  // Needs to be EAX for the invoke. -  locations->AddTemp(Location::RegisterLocation(EAX));  }  void IntrinsicCodeGeneratorX86::VisitStringCharAt(HInvoke* invoke) { @@ -901,8 +898,7 @@ void IntrinsicCodeGeneratorX86::VisitStringCharAt(HInvoke* invoke) {    // TODO: For simplicity, the index parameter is requested in a register, so different from Quick    //       we will not optimize the code for constants (which would save a register). -  SlowPathCodeX86* slow_path = new (GetAllocator()) IntrinsicSlowPathX86( -      invoke, locations->GetTemp(0).AsRegister<Register>()); +  SlowPathCodeX86* slow_path = new (GetAllocator()) IntrinsicSlowPathX86(invoke);    codegen_->AddSlowPath(slow_path);    X86Assembler* assembler = GetAssembler(); @@ -926,8 +922,6 @@ void IntrinsicLocationsBuilderX86::VisitStringCompareTo(HInvoke* invoke) {    locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));    locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));    locations->SetOut(Location::RegisterLocation(EAX)); -  // Needs to be EAX for the invoke. -  locations->AddTemp(Location::RegisterLocation(EAX));  }  void IntrinsicCodeGeneratorX86::VisitStringCompareTo(HInvoke* invoke) { @@ -939,8 +933,7 @@ void IntrinsicCodeGeneratorX86::VisitStringCompareTo(HInvoke* invoke) {    Register argument = locations->InAt(1).AsRegister<Register>();    __ testl(argument, argument); -  SlowPathCodeX86* slow_path = new (GetAllocator()) IntrinsicSlowPathX86( -      invoke, locations->GetTemp(0).AsRegister<Register>()); +  SlowPathCodeX86* slow_path = new (GetAllocator()) IntrinsicSlowPathX86(invoke);    codegen_->AddSlowPath(slow_path);    __ j(kEqual, slow_path->GetEntryLabel()); @@ -948,6 +941,158 @@ void IntrinsicCodeGeneratorX86::VisitStringCompareTo(HInvoke* invoke) {    __ Bind(slow_path->GetExitLabel());  } +static void CreateStringIndexOfLocations(HInvoke* invoke, +                                         ArenaAllocator* allocator, +                                         bool start_at_zero) { +  LocationSummary* locations = new (allocator) LocationSummary(invoke, +                                                               LocationSummary::kCallOnSlowPath, +                                                               kIntrinsified); +  // The data needs to be in EDI for scasw. So request that the string is there, anyways. +  locations->SetInAt(0, Location::RegisterLocation(EDI)); +  // If we look for a constant char, we'll still have to copy it into EAX. So just request the +  // allocator to do that, anyways. We can still do the constant check by checking the parameter +  // of the instruction explicitly. +  // Note: This works as we don't clobber EAX anywhere. +  locations->SetInAt(1, Location::RegisterLocation(EAX)); +  if (!start_at_zero) { +    locations->SetInAt(2, Location::RequiresRegister());          // The starting index. +  } +  // As we clobber EDI during execution anyways, also use it as the output. +  locations->SetOut(Location::SameAsFirstInput()); + +  // repne scasw uses ECX as the counter. +  locations->AddTemp(Location::RegisterLocation(ECX)); +  // Need another temporary to be able to compute the result. +  locations->AddTemp(Location::RequiresRegister()); +} + +static void GenerateStringIndexOf(HInvoke* invoke, +                                  X86Assembler* assembler, +                                  CodeGeneratorX86* codegen, +                                  ArenaAllocator* allocator, +                                  bool start_at_zero) { +  LocationSummary* locations = invoke->GetLocations(); + +  // Note that the null check must have been done earlier. +  DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0))); + +  Register string_obj = locations->InAt(0).AsRegister<Register>(); +  Register search_value = locations->InAt(1).AsRegister<Register>(); +  Register counter = locations->GetTemp(0).AsRegister<Register>(); +  Register string_length = locations->GetTemp(1).AsRegister<Register>(); +  Register out = locations->Out().AsRegister<Register>(); + +  // Check our assumptions for registers. +  DCHECK_EQ(string_obj, EDI); +  DCHECK_EQ(search_value, EAX); +  DCHECK_EQ(counter, ECX); +  DCHECK_EQ(out, EDI); + +  // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically, +  // or directly dispatch if we have a constant. +  SlowPathCodeX86* slow_path = nullptr; +  if (invoke->InputAt(1)->IsIntConstant()) { +    if (static_cast<uint32_t>(invoke->InputAt(1)->AsIntConstant()->GetValue()) > +    std::numeric_limits<uint16_t>::max()) { +      // Always needs the slow-path. We could directly dispatch to it, but this case should be +      // rare, so for simplicity just put the full slow-path down and branch unconditionally. +      slow_path = new (allocator) IntrinsicSlowPathX86(invoke); +      codegen->AddSlowPath(slow_path); +      __ jmp(slow_path->GetEntryLabel()); +      __ Bind(slow_path->GetExitLabel()); +      return; +    } +  } else { +    __ cmpl(search_value, Immediate(std::numeric_limits<uint16_t>::max())); +    slow_path = new (allocator) IntrinsicSlowPathX86(invoke); +    codegen->AddSlowPath(slow_path); +    __ j(kAbove, slow_path->GetEntryLabel()); +  } + +  // From here down, we know that we are looking for a char that fits in 16 bits. +  // Location of reference to data array within the String object. +  int32_t value_offset = mirror::String::ValueOffset().Int32Value(); +  // Location of count within the String object. +  int32_t count_offset = mirror::String::CountOffset().Int32Value(); + +  // Load string length, i.e., the count field of the string. +  __ movl(string_length, Address(string_obj, count_offset)); + +  // Do a zero-length check. +  // TODO: Support jecxz. +  Label not_found_label; +  __ testl(string_length, string_length); +  __ j(kEqual, ¬_found_label); + +  if (start_at_zero) { +    // Number of chars to scan is the same as the string length. +    __ movl(counter, string_length); + +    // Move to the start of the string. +    __ addl(string_obj, Immediate(value_offset)); +  } else { +    Register start_index = locations->InAt(2).AsRegister<Register>(); + +    // Do a start_index check. +    __ cmpl(start_index, string_length); +    __ j(kGreaterEqual, ¬_found_label); + +    // Ensure we have a start index >= 0; +    __ xorl(counter, counter); +    __ cmpl(start_index, Immediate(0)); +    __ cmovl(kGreater, counter, start_index); + +    // Move to the start of the string: string_obj + value_offset + 2 * start_index. +    __ leal(string_obj, Address(string_obj, counter, ScaleFactor::TIMES_2, value_offset)); + +    // Now update ecx (the repne scasw work counter). We have string.length - start_index left to +    // compare. +    __ negl(counter); +    __ leal(counter, Address(string_length, counter, ScaleFactor::TIMES_1, 0)); +  } + +  // Everything is set up for repne scasw: +  //   * Comparison address in EDI. +  //   * Counter in ECX. +  __ repne_scasw(); + +  // Did we find a match? +  __ j(kNotEqual, ¬_found_label); + +  // Yes, we matched.  Compute the index of the result. +  __ subl(string_length, counter); +  __ leal(out, Address(string_length, -1)); + +  Label done; +  __ jmp(&done); + +  // Failed to match; return -1. +  __ Bind(¬_found_label); +  __ movl(out, Immediate(-1)); + +  // And join up at the end. +  __ Bind(&done); +  if (slow_path != nullptr) { +    __ Bind(slow_path->GetExitLabel()); +  } +} + +void IntrinsicLocationsBuilderX86::VisitStringIndexOf(HInvoke* invoke) { +  CreateStringIndexOfLocations(invoke, arena_, true); +} + +void IntrinsicCodeGeneratorX86::VisitStringIndexOf(HInvoke* invoke) { +  GenerateStringIndexOf(invoke, GetAssembler(), codegen_, GetAllocator(), true); +} + +void IntrinsicLocationsBuilderX86::VisitStringIndexOfAfter(HInvoke* invoke) { +  CreateStringIndexOfLocations(invoke, arena_, false); +} + +void IntrinsicCodeGeneratorX86::VisitStringIndexOfAfter(HInvoke* invoke) { +  GenerateStringIndexOf(invoke, GetAssembler(), codegen_, GetAllocator(), false); +} +  void IntrinsicLocationsBuilderX86::VisitStringNewStringFromBytes(HInvoke* invoke) {    LocationSummary* locations = new (arena_) LocationSummary(invoke,                                                              LocationSummary::kCall, @@ -958,8 +1103,6 @@ void IntrinsicLocationsBuilderX86::VisitStringNewStringFromBytes(HInvoke* invoke    locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));    locations->SetInAt(3, Location::RegisterLocation(calling_convention.GetRegisterAt(3)));    locations->SetOut(Location::RegisterLocation(EAX)); -  // Needs to be EAX for the invoke. -  locations->AddTemp(Location::RegisterLocation(EAX));  }  void IntrinsicCodeGeneratorX86::VisitStringNewStringFromBytes(HInvoke* invoke) { @@ -968,8 +1111,7 @@ void IntrinsicCodeGeneratorX86::VisitStringNewStringFromBytes(HInvoke* invoke) {    Register byte_array = locations->InAt(0).AsRegister<Register>();    __ testl(byte_array, byte_array); -  SlowPathCodeX86* slow_path = new (GetAllocator()) IntrinsicSlowPathX86( -      invoke, locations->GetTemp(0).AsRegister<Register>()); +  SlowPathCodeX86* slow_path = new (GetAllocator()) IntrinsicSlowPathX86(invoke);    codegen_->AddSlowPath(slow_path);    __ j(kEqual, slow_path->GetEntryLabel()); @@ -1003,8 +1145,6 @@ void IntrinsicLocationsBuilderX86::VisitStringNewStringFromString(HInvoke* invok    InvokeRuntimeCallingConvention calling_convention;    locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));    locations->SetOut(Location::RegisterLocation(EAX)); -  // Needs to be EAX for the invoke. -  locations->AddTemp(Location::RegisterLocation(EAX));  }  void IntrinsicCodeGeneratorX86::VisitStringNewStringFromString(HInvoke* invoke) { @@ -1013,8 +1153,7 @@ void IntrinsicCodeGeneratorX86::VisitStringNewStringFromString(HInvoke* invoke)    Register string_to_copy = locations->InAt(0).AsRegister<Register>();    __ testl(string_to_copy, string_to_copy); -  SlowPathCodeX86* slow_path = new (GetAllocator()) IntrinsicSlowPathX86( -      invoke, locations->GetTemp(0).AsRegister<Register>()); +  SlowPathCodeX86* slow_path = new (GetAllocator()) IntrinsicSlowPathX86(invoke);    codegen_->AddSlowPath(slow_path);    __ j(kEqual, slow_path->GetEntryLabel()); @@ -1584,8 +1723,6 @@ void IntrinsicCodeGeneratorX86::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED)  UNIMPLEMENTED_INTRINSIC(MathRoundDouble)  UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck) -UNIMPLEMENTED_INTRINSIC(StringIndexOf) -UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter)  UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)  UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 1fc5432a89..0efa714a23 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -16,6 +16,8 @@  #include "intrinsics_x86_64.h" +#include <limits> +  #include "arch/x86_64/instruction_set_features_x86_64.h"  #include "code_generator_x86_64.h"  #include "entrypoints/quick/quick_entrypoints.h" @@ -783,7 +785,7 @@ void IntrinsicCodeGeneratorX86_64::VisitMathRoundDouble(HInvoke* invoke) {    __ Bind(&nan);    //  output = 0 -  __ xorq(out, out); +  __ xorl(out, out);    __ Bind(&done);  } @@ -858,6 +860,157 @@ void IntrinsicCodeGeneratorX86_64::VisitStringCompareTo(HInvoke* invoke) {    __ Bind(slow_path->GetExitLabel());  } +static void CreateStringIndexOfLocations(HInvoke* invoke, +                                         ArenaAllocator* allocator, +                                         bool start_at_zero) { +  LocationSummary* locations = new (allocator) LocationSummary(invoke, +                                                               LocationSummary::kCallOnSlowPath, +                                                               kIntrinsified); +  // The data needs to be in RDI for scasw. So request that the string is there, anyways. +  locations->SetInAt(0, Location::RegisterLocation(RDI)); +  // If we look for a constant char, we'll still have to copy it into RAX. So just request the +  // allocator to do that, anyways. We can still do the constant check by checking the parameter +  // of the instruction explicitly. +  // Note: This works as we don't clobber RAX anywhere. +  locations->SetInAt(1, Location::RegisterLocation(RAX)); +  if (!start_at_zero) { +    locations->SetInAt(2, Location::RequiresRegister());          // The starting index. +  } +  // As we clobber RDI during execution anyways, also use it as the output. +  locations->SetOut(Location::SameAsFirstInput()); + +  // repne scasw uses RCX as the counter. +  locations->AddTemp(Location::RegisterLocation(RCX)); +  // Need another temporary to be able to compute the result. +  locations->AddTemp(Location::RequiresRegister()); +} + +static void GenerateStringIndexOf(HInvoke* invoke, +                                  X86_64Assembler* assembler, +                                  CodeGeneratorX86_64* codegen, +                                  ArenaAllocator* allocator, +                                  bool start_at_zero) { +  LocationSummary* locations = invoke->GetLocations(); + +  // Note that the null check must have been done earlier. +  DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0))); + +  CpuRegister string_obj = locations->InAt(0).AsRegister<CpuRegister>(); +  CpuRegister search_value = locations->InAt(1).AsRegister<CpuRegister>(); +  CpuRegister counter = locations->GetTemp(0).AsRegister<CpuRegister>(); +  CpuRegister string_length = locations->GetTemp(1).AsRegister<CpuRegister>(); +  CpuRegister out = locations->Out().AsRegister<CpuRegister>(); + +  // Check our assumptions for registers. +  DCHECK_EQ(string_obj.AsRegister(), RDI); +  DCHECK_EQ(search_value.AsRegister(), RAX); +  DCHECK_EQ(counter.AsRegister(), RCX); +  DCHECK_EQ(out.AsRegister(), RDI); + +  // Check for code points > 0xFFFF. Either a slow-path check when we don't know statically, +  // or directly dispatch if we have a constant. +  SlowPathCodeX86_64* slow_path = nullptr; +  if (invoke->InputAt(1)->IsIntConstant()) { +    if (static_cast<uint32_t>(invoke->InputAt(1)->AsIntConstant()->GetValue()) > +    std::numeric_limits<uint16_t>::max()) { +      // Always needs the slow-path. We could directly dispatch to it, but this case should be +      // rare, so for simplicity just put the full slow-path down and branch unconditionally. +      slow_path = new (allocator) IntrinsicSlowPathX86_64(invoke); +      codegen->AddSlowPath(slow_path); +      __ jmp(slow_path->GetEntryLabel()); +      __ Bind(slow_path->GetExitLabel()); +      return; +    } +  } else { +    __ cmpl(search_value, Immediate(std::numeric_limits<uint16_t>::max())); +    slow_path = new (allocator) IntrinsicSlowPathX86_64(invoke); +    codegen->AddSlowPath(slow_path); +    __ j(kAbove, slow_path->GetEntryLabel()); +  } + +  // From here down, we know that we are looking for a char that fits in 16 bits. +  // Location of reference to data array within the String object. +  int32_t value_offset = mirror::String::ValueOffset().Int32Value(); +  // Location of count within the String object. +  int32_t count_offset = mirror::String::CountOffset().Int32Value(); + +  // Load string length, i.e., the count field of the string. +  __ movl(string_length, Address(string_obj, count_offset)); + +  // Do a length check. +  // TODO: Support jecxz. +  Label not_found_label; +  __ testl(string_length, string_length); +  __ j(kEqual, ¬_found_label); + +  if (start_at_zero) { +    // Number of chars to scan is the same as the string length. +    __ movl(counter, string_length); + +    // Move to the start of the string. +    __ addq(string_obj, Immediate(value_offset)); +  } else { +    CpuRegister start_index = locations->InAt(2).AsRegister<CpuRegister>(); + +    // Do a start_index check. +    __ cmpl(start_index, string_length); +    __ j(kGreaterEqual, ¬_found_label); + +    // Ensure we have a start index >= 0; +    __ xorl(counter, counter); +    __ cmpl(start_index, Immediate(0)); +    __ cmov(kGreater, counter, start_index, false);  // 32-bit copy is enough. + +    // Move to the start of the string: string_obj + value_offset + 2 * start_index. +    __ leaq(string_obj, Address(string_obj, counter, ScaleFactor::TIMES_2, value_offset)); + +    // Now update ecx, the work counter: it's gonna be string.length - start_index. +    __ negq(counter);  // Needs to be 64-bit negation, as the address computation is 64-bit. +    __ leaq(counter, Address(string_length, counter, ScaleFactor::TIMES_1, 0)); +  } + +  // Everything is set up for repne scasw: +  //   * Comparison address in RDI. +  //   * Counter in ECX. +  __ repne_scasw(); + +  // Did we find a match? +  __ j(kNotEqual, ¬_found_label); + +  // Yes, we matched.  Compute the index of the result. +  __ subl(string_length, counter); +  __ leal(out, Address(string_length, -1)); + +  Label done; +  __ jmp(&done); + +  // Failed to match; return -1. +  __ Bind(¬_found_label); +  __ movl(out, Immediate(-1)); + +  // And join up at the end. +  __ Bind(&done); +  if (slow_path != nullptr) { +    __ Bind(slow_path->GetExitLabel()); +  } +} + +void IntrinsicLocationsBuilderX86_64::VisitStringIndexOf(HInvoke* invoke) { +  CreateStringIndexOfLocations(invoke, arena_, true); +} + +void IntrinsicCodeGeneratorX86_64::VisitStringIndexOf(HInvoke* invoke) { +  GenerateStringIndexOf(invoke, GetAssembler(), codegen_, GetAllocator(), true); +} + +void IntrinsicLocationsBuilderX86_64::VisitStringIndexOfAfter(HInvoke* invoke) { +  CreateStringIndexOfLocations(invoke, arena_, false); +} + +void IntrinsicCodeGeneratorX86_64::VisitStringIndexOfAfter(HInvoke* invoke) { +  GenerateStringIndexOf(invoke, GetAssembler(), codegen_, GetAllocator(), false); +} +  void IntrinsicLocationsBuilderX86_64::VisitStringNewStringFromBytes(HInvoke* invoke) {    LocationSummary* locations = new (arena_) LocationSummary(invoke,                                                              LocationSummary::kCall, @@ -1434,8 +1587,6 @@ void IntrinsicCodeGeneratorX86_64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSE  }  UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck) -UNIMPLEMENTED_INTRINSIC(StringIndexOf) -UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter)  UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)  UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) diff --git a/compiler/optimizing/licm.cc b/compiler/optimizing/licm.cc index bf9b8e59c5..2535ea274a 100644 --- a/compiler/optimizing/licm.cc +++ b/compiler/optimizing/licm.cc @@ -39,8 +39,9 @@ static bool InputsAreDefinedBeforeLoop(HInstruction* instruction) {      }    } -  if (instruction->HasEnvironment()) { -    HEnvironment* environment = instruction->GetEnvironment(); +  for (HEnvironment* environment = instruction->GetEnvironment(); +       environment != nullptr; +       environment = environment->GetParent()) {      for (size_t i = 0, e = environment->Size(); i < e; ++i) {        HInstruction* input = environment->GetInstructionAt(i);        if (input != nullptr) { @@ -63,13 +64,15 @@ static bool InputsAreDefinedBeforeLoop(HInstruction* instruction) {   * If `environment` has a loop header phi, we replace it with its first input.   */  static void UpdateLoopPhisIn(HEnvironment* environment, HLoopInformation* info) { -  for (size_t i = 0, e = environment->Size(); i < e; ++i) { -    HInstruction* input = environment->GetInstructionAt(i); -    if (input != nullptr && IsPhiOf(input, info->GetHeader())) { -      environment->RemoveAsUserOfInput(i); -      HInstruction* incoming = input->InputAt(0); -      environment->SetRawEnvAt(i, incoming); -      incoming->AddEnvUseAt(environment, i); +  for (; environment != nullptr; environment = environment->GetParent()) { +    for (size_t i = 0, e = environment->Size(); i < e; ++i) { +      HInstruction* input = environment->GetInstructionAt(i); +      if (input != nullptr && IsPhiOf(input, info->GetHeader())) { +        environment->RemoveAsUserOfInput(i); +        HInstruction* incoming = input->InputAt(0); +        environment->SetRawEnvAt(i, incoming); +        incoming->AddEnvUseAt(environment, i); +      }      }    }  } diff --git a/compiler/optimizing/linearize_test.cc b/compiler/optimizing/linearize_test.cc index 7818c606db..4f259b5095 100644 --- a/compiler/optimizing/linearize_test.cc +++ b/compiler/optimizing/linearize_test.cc @@ -39,7 +39,7 @@ namespace art {  static void TestCode(const uint16_t* data, const int* expected_order, size_t number_of_blocks) {    ArenaPool pool;    ArenaAllocator allocator(&pool); -  HGraph* graph = new (&allocator) HGraph(&allocator); +  HGraph* graph = CreateGraph(&allocator);    HGraphBuilder builder(graph);    const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);    bool graph_built = builder.BuildGraph(*item); diff --git a/compiler/optimizing/live_ranges_test.cc b/compiler/optimizing/live_ranges_test.cc index 52367730ed..7cb00a1923 100644 --- a/compiler/optimizing/live_ranges_test.cc +++ b/compiler/optimizing/live_ranges_test.cc @@ -32,7 +32,7 @@  namespace art {  static HGraph* BuildGraph(const uint16_t* data, ArenaAllocator* allocator) { -  HGraph* graph = new (allocator) HGraph(allocator); +  HGraph* graph = CreateGraph(allocator);    HGraphBuilder builder(graph);    const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);    builder.BuildGraph(*item); diff --git a/compiler/optimizing/liveness_test.cc b/compiler/optimizing/liveness_test.cc index 8a96ee9ace..9d7d0b6c67 100644 --- a/compiler/optimizing/liveness_test.cc +++ b/compiler/optimizing/liveness_test.cc @@ -46,7 +46,7 @@ static void DumpBitVector(BitVector* vector,  static void TestCode(const uint16_t* data, const char* expected) {    ArenaPool pool;    ArenaAllocator allocator(&pool); -  HGraph* graph = new (&allocator) HGraph(&allocator); +  HGraph* graph = CreateGraph(&allocator);    HGraphBuilder builder(graph);    const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);    bool graph_built = builder.BuildGraph(*item); @@ -445,44 +445,40 @@ TEST(LivenessTest, Loop5) {  TEST(LivenessTest, Loop6) {    // Bitsets are made of: -  // (constant0, constant4, constant5, phi in block 2, phi in block 8) +  // (constant0, constant4, constant5, phi in block 2)    const char* expected =      "Block 0\n" -    "  live in: (00000)\n" -    "  live out: (11100)\n" -    "  kill: (11100)\n" +    "  live in: (0000)\n" +    "  live out: (1110)\n" +    "  kill: (1110)\n"      "Block 1\n" -    "  live in: (11100)\n" -    "  live out: (01100)\n" -    "  kill: (00000)\n" +    "  live in: (1110)\n" +    "  live out: (0110)\n" +    "  kill: (0000)\n"      "Block 2\n"  // loop header -    "  live in: (01100)\n" -    "  live out: (01110)\n" -    "  kill: (00010)\n" +    "  live in: (0110)\n" +    "  live out: (0111)\n" +    "  kill: (0001)\n"      "Block 3\n" -    "  live in: (01100)\n" -    "  live out: (01100)\n" -    "  kill: (00000)\n" -    "Block 4\n"  // original back edge -    "  live in: (01100)\n" -    "  live out: (01100)\n" -    "  kill: (00000)\n" -    "Block 5\n"  // original back edge -    "  live in: (01100)\n" -    "  live out: (01100)\n" -    "  kill: (00000)\n" +    "  live in: (0110)\n" +    "  live out: (0110)\n" +    "  kill: (0000)\n" +    "Block 4\n"  // back edge +    "  live in: (0110)\n" +    "  live out: (0110)\n" +    "  kill: (0000)\n" +    "Block 5\n"  // back edge +    "  live in: (0110)\n" +    "  live out: (0110)\n" +    "  kill: (0000)\n"      "Block 6\n"  // return block -    "  live in: (00010)\n" -    "  live out: (00000)\n" -    "  kill: (00000)\n" +    "  live in: (0001)\n" +    "  live out: (0000)\n" +    "  kill: (0000)\n"      "Block 7\n"  // exit block -    "  live in: (00000)\n" -    "  live out: (00000)\n" -    "  kill: (00000)\n" -    "Block 8\n"  // synthesized back edge -    "  live in: (01100)\n" -    "  live out: (01100)\n" -    "  kill: (00001)\n"; +    "  live in: (0000)\n" +    "  live out: (0000)\n" +    "  kill: (0000)\n";    const uint16_t data[] = ONE_REGISTER_CODE_ITEM(      Instruction::CONST_4 | 0 | 0, diff --git a/compiler/optimizing/locations.cc b/compiler/optimizing/locations.cc index a1ae67009e..42aba04828 100644 --- a/compiler/optimizing/locations.cc +++ b/compiler/optimizing/locations.cc @@ -25,8 +25,6 @@ LocationSummary::LocationSummary(HInstruction* instruction,                                   bool intrinsified)      : inputs_(instruction->GetBlock()->GetGraph()->GetArena(), instruction->InputCount()),        temps_(instruction->GetBlock()->GetGraph()->GetArena(), 0), -      environment_(instruction->GetBlock()->GetGraph()->GetArena(), -                   instruction->EnvironmentSize()),        output_overlaps_(Location::kOutputOverlap),        call_kind_(call_kind),        stack_mask_(nullptr), @@ -37,10 +35,6 @@ LocationSummary::LocationSummary(HInstruction* instruction,    for (size_t i = 0; i < instruction->InputCount(); ++i) {      inputs_.Put(i, Location());    } -  environment_.SetSize(instruction->EnvironmentSize()); -  for (size_t i = 0; i < instruction->EnvironmentSize(); ++i) { -    environment_.Put(i, Location()); -  }    instruction->SetLocations(this);    if (NeedsSafepoint()) { diff --git a/compiler/optimizing/locations.h b/compiler/optimizing/locations.h index c3a99150c4..09bbb33042 100644 --- a/compiler/optimizing/locations.h +++ b/compiler/optimizing/locations.h @@ -525,14 +525,6 @@ class LocationSummary : public ArenaObject<kArenaAllocMisc> {      return temps_.Size();    } -  void SetEnvironmentAt(uint32_t at, Location location) { -    environment_.Put(at, location); -  } - -  Location GetEnvironmentAt(uint32_t at) const { -    return environment_.Get(at); -  } -    Location Out() const { return output_; }    bool CanCall() const { return call_kind_ != kNoCall; } @@ -602,7 +594,6 @@ class LocationSummary : public ArenaObject<kArenaAllocMisc> {   private:    GrowableArray<Location> inputs_;    GrowableArray<Location> temps_; -  GrowableArray<Location> environment_;    // Whether the output overlaps with any of the inputs. If it overlaps, then it cannot    // share the same register as the inputs.    Location::OutputOverlap output_overlaps_; diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index d3ee770941..b9e58c7032 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -37,8 +37,9 @@ static void RemoveAsUser(HInstruction* instruction) {      instruction->RemoveAsUserOfInput(i);    } -  HEnvironment* environment = instruction->GetEnvironment(); -  if (environment != nullptr) { +  for (HEnvironment* environment = instruction->GetEnvironment(); +       environment != nullptr; +       environment = environment->GetParent()) {      for (size_t i = 0, e = environment->Size(); i < e; ++i) {        if (environment->GetInstructionAt(i) != nullptr) {          environment->RemoveAsUserOfInput(i); @@ -191,24 +192,6 @@ void HGraph::SplitCriticalEdge(HBasicBlock* block, HBasicBlock* successor) {  void HGraph::SimplifyLoop(HBasicBlock* header) {    HLoopInformation* info = header->GetLoopInformation(); -  // If there are more than one back edge, make them branch to the same block that -  // will become the only back edge. This simplifies finding natural loops in the -  // graph. -  // Also, if the loop is a do/while (that is the back edge is an if), change the -  // back edge to be a goto. This simplifies code generation of suspend cheks. -  if (info->NumberOfBackEdges() > 1 || info->GetBackEdges().Get(0)->GetLastInstruction()->IsIf()) { -    HBasicBlock* new_back_edge = new (arena_) HBasicBlock(this, header->GetDexPc()); -    AddBlock(new_back_edge); -    new_back_edge->AddInstruction(new (arena_) HGoto()); -    for (size_t pred = 0, e = info->GetBackEdges().Size(); pred < e; ++pred) { -      HBasicBlock* back_edge = info->GetBackEdges().Get(pred); -      back_edge->ReplaceSuccessor(header, new_back_edge); -    } -    info->ClearBackEdges(); -    info->AddBackEdge(new_back_edge); -    new_back_edge->AddSuccessor(header); -  } -    // Make sure the loop has only one pre header. This simplifies SSA building by having    // to just look at the pre header to know which locals are initialized at entry of the    // loop. @@ -218,11 +201,9 @@ void HGraph::SimplifyLoop(HBasicBlock* header) {      AddBlock(pre_header);      pre_header->AddInstruction(new (arena_) HGoto()); -    ArenaBitVector back_edges(arena_, GetBlocks().Size(), false); -    HBasicBlock* back_edge = info->GetBackEdges().Get(0);      for (size_t pred = 0; pred < header->GetPredecessors().Size(); ++pred) {        HBasicBlock* predecessor = header->GetPredecessors().Get(pred); -      if (predecessor != back_edge) { +      if (!info->IsBackEdge(*predecessor)) {          predecessor->ReplaceSuccessor(header, pre_header);          pred--;        } @@ -230,9 +211,17 @@ void HGraph::SimplifyLoop(HBasicBlock* header) {      pre_header->AddSuccessor(header);    } -  // Make sure the second predecessor of a loop header is the back edge. -  if (header->GetPredecessors().Get(1) != info->GetBackEdges().Get(0)) { -    header->SwapPredecessors(); +  // Make sure the first predecessor of a loop header is the incoming block. +  if (info->IsBackEdge(*header->GetPredecessors().Get(0))) { +    HBasicBlock* to_swap = header->GetPredecessors().Get(0); +    for (size_t pred = 1, e = header->GetPredecessors().Size(); pred < e; ++pred) { +      HBasicBlock* predecessor = header->GetPredecessors().Get(pred); +      if (!info->IsBackEdge(*predecessor)) { +        header->predecessors_.Put(pred, to_swap); +        header->predecessors_.Put(0, predecessor); +        break; +      } +    }    }    // Place the suspend check at the beginning of the header, so that live registers @@ -357,21 +346,22 @@ void HLoopInformation::PopulateRecursive(HBasicBlock* block) {  }  bool HLoopInformation::Populate() { -  DCHECK_EQ(GetBackEdges().Size(), 1u); -  HBasicBlock* back_edge = GetBackEdges().Get(0); -  DCHECK(back_edge->GetDominator() != nullptr); -  if (!header_->Dominates(back_edge)) { -    // This loop is not natural. Do not bother going further. -    return false; -  } +  for (size_t i = 0, e = GetBackEdges().Size(); i < e; ++i) { +    HBasicBlock* back_edge = GetBackEdges().Get(i); +    DCHECK(back_edge->GetDominator() != nullptr); +    if (!header_->Dominates(back_edge)) { +      // This loop is not natural. Do not bother going further. +      return false; +    } -  // Populate this loop: starting with the back edge, recursively add predecessors -  // that are not already part of that loop. Set the header as part of the loop -  // to end the recursion. -  // This is a recursive implementation of the algorithm described in -  // "Advanced Compiler Design & Implementation" (Muchnick) p192. -  blocks_.SetBit(header_->GetBlockId()); -  PopulateRecursive(back_edge); +    // Populate this loop: starting with the back edge, recursively add predecessors +    // that are not already part of that loop. Set the header as part of the loop +    // to end the recursion. +    // This is a recursive implementation of the algorithm described in +    // "Advanced Compiler Design & Implementation" (Muchnick) p192. +    blocks_.SetBit(header_->GetBlockId()); +    PopulateRecursive(back_edge); +  }    return true;  } @@ -387,6 +377,14 @@ bool HLoopInformation::IsIn(const HLoopInformation& other) const {    return other.blocks_.IsBitSet(header_->GetBlockId());  } +size_t HLoopInformation::GetLifetimeEnd() const { +  size_t last_position = 0; +  for (size_t i = 0, e = back_edges_.Size(); i < e; ++i) { +    last_position = std::max(back_edges_.Get(i)->GetLifetimeEnd(), last_position); +  } +  return last_position; +} +  bool HBasicBlock::Dominates(HBasicBlock* other) const {    // Walk up the dominator tree from `other`, to find out if `this`    // is an ancestor. @@ -503,6 +501,16 @@ void HBasicBlock::RemoveInstructionOrPhi(HInstruction* instruction, bool ensure_    }  } +void HEnvironment::CopyFrom(const GrowableArray<HInstruction*>& locals) { +  for (size_t i = 0; i < locals.Size(); i++) { +    HInstruction* instruction = locals.Get(i); +    SetRawEnvAt(i, instruction); +    if (instruction != nullptr) { +      instruction->AddEnvUseAt(this, i); +    } +  } +} +  void HEnvironment::CopyFrom(HEnvironment* env) {    for (size_t i = 0; i < env->Size(); i++) {      HInstruction* instruction = env->GetInstructionAt(i); @@ -963,8 +971,9 @@ void HBasicBlock::DisconnectAndDelete() {      HLoopInformation* loop_info = it.Current();      loop_info->Remove(this);      if (loop_info->IsBackEdge(*this)) { -      // This deliberately leaves the loop in an inconsistent state and will -      // fail SSAChecker unless the entire loop is removed during the pass. +      // If this was the last back edge of the loop, we deliberately leave the +      // loop in an inconsistent state and will fail SSAChecker unless the +      // entire loop is removed during the pass.        loop_info->RemoveBackEdge(this);      }    } @@ -1075,8 +1084,7 @@ void HBasicBlock::MergeWith(HBasicBlock* other) {      HLoopInformation* loop_info = it.Current();      loop_info->Remove(other);      if (loop_info->IsBackEdge(*other)) { -      loop_info->ClearBackEdges(); -      loop_info->AddBackEdge(this); +      loop_info->ReplaceBackEdge(other, this);      }    } @@ -1307,11 +1315,9 @@ void HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) {          loop_it.Current()->Add(to);        }        if (info->IsBackEdge(*at)) { -        // Only `at` can become a back edge, as the inlined blocks -        // are predecessors of `at`. -        DCHECK_EQ(1u, info->NumberOfBackEdges()); -        info->ClearBackEdges(); -        info->AddBackEdge(to); +        // Only `to` can become a back edge, as the inlined blocks +        // are predecessors of `to`. +        info->ReplaceBackEdge(at, to);        }      }    } diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 63f3c95c7d..031761e7f7 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -48,6 +48,7 @@ class HPhi;  class HSuspendCheck;  class LiveInterval;  class LocationSummary; +class SlowPathCode;  class SsaBuilder;  static const int kDefaultNumberOfBlocks = 8; @@ -116,7 +117,11 @@ class HInstructionList {  // Control-flow graph of a method. Contains a list of basic blocks.  class HGraph : public ArenaObject<kArenaAllocMisc> {   public: -  HGraph(ArenaAllocator* arena, bool debuggable = false, int start_instruction_id = 0) +  HGraph(ArenaAllocator* arena, +         const DexFile& dex_file, +         uint32_t method_idx, +         bool debuggable = false, +         int start_instruction_id = 0)        : arena_(arena),          blocks_(arena, kDefaultNumberOfBlocks),          reverse_post_order_(arena, kDefaultNumberOfBlocks), @@ -130,6 +135,8 @@ class HGraph : public ArenaObject<kArenaAllocMisc> {          has_bounds_checks_(false),          debuggable_(debuggable),          current_instruction_id_(start_instruction_id), +        dex_file_(dex_file), +        method_idx_(method_idx),          cached_null_constant_(nullptr),          cached_int_constants_(std::less<int32_t>(), arena->Adapter()),          cached_float_constants_(std::less<int32_t>(), arena->Adapter()), @@ -262,6 +269,14 @@ class HGraph : public ArenaObject<kArenaAllocMisc> {    HBasicBlock* FindCommonDominator(HBasicBlock* first, HBasicBlock* second) const; +  const DexFile& GetDexFile() const { +    return dex_file_; +  } + +  uint32_t GetMethodIdx() const { +    return method_idx_; +  } +   private:    void VisitBlockForDominatorTree(HBasicBlock* block,                                    HBasicBlock* predecessor, @@ -338,6 +353,12 @@ class HGraph : public ArenaObject<kArenaAllocMisc> {    // The current id to assign to a newly added instruction. See HInstruction.id_.    int32_t current_instruction_id_; +  // The dex file from which the method is from. +  const DexFile& dex_file_; + +  // The method index in the dex file. +  const uint32_t method_idx_; +    // Cached constants.    HNullConstant* cached_null_constant_;    ArenaSafeMap<int32_t, HIntConstant*> cached_int_constants_; @@ -397,16 +418,21 @@ class HLoopInformation : public ArenaObject<kArenaAllocMisc> {      return back_edges_;    } -  HBasicBlock* GetSingleBackEdge() const { -    DCHECK_EQ(back_edges_.Size(), 1u); -    return back_edges_.Get(0); -  } +  // Returns the lifetime position of the back edge that has the +  // greatest lifetime position. +  size_t GetLifetimeEnd() const; -  void ClearBackEdges() { -    back_edges_.Reset(); +  void ReplaceBackEdge(HBasicBlock* existing, HBasicBlock* new_back_edge) { +    for (size_t i = 0, e = back_edges_.Size(); i < e; ++i) { +      if (back_edges_.Get(i) == existing) { +        back_edges_.Put(i, new_back_edge); +        return; +      } +    } +    UNREACHABLE();    } -  // Find blocks that are part of this loop. Returns whether the loop is a natural loop, +  // Finds blocks that are part of this loop. Returns whether the loop is a natural loop,    // that is the header dominates the back edge.    bool Populate(); @@ -928,6 +954,14 @@ class HUseList : public ValueObject {      return first_ != nullptr && first_->next_ == nullptr;    } +  size_t SizeSlow() const { +    size_t count = 0; +    for (HUseListNode<T>* current = first_; current != nullptr; current = current->GetNext()) { +      ++count; +    } +    return count; +  } +   private:    HUseListNode<T>* first_;  }; @@ -1054,15 +1088,43 @@ class SideEffects : public ValueObject {  // A HEnvironment object contains the values of virtual registers at a given location.  class HEnvironment : public ArenaObject<kArenaAllocMisc> {   public: -  HEnvironment(ArenaAllocator* arena, size_t number_of_vregs) -     : vregs_(arena, number_of_vregs) { +  HEnvironment(ArenaAllocator* arena, +               size_t number_of_vregs, +               const DexFile& dex_file, +               uint32_t method_idx, +               uint32_t dex_pc) +     : vregs_(arena, number_of_vregs), +       locations_(arena, number_of_vregs), +       parent_(nullptr), +       dex_file_(dex_file), +       method_idx_(method_idx), +       dex_pc_(dex_pc) {      vregs_.SetSize(number_of_vregs);      for (size_t i = 0; i < number_of_vregs; i++) {        vregs_.Put(i, HUserRecord<HEnvironment*>());      } + +    locations_.SetSize(number_of_vregs); +    for (size_t i = 0; i < number_of_vregs; ++i) { +      locations_.Put(i, Location()); +    }    } -  void CopyFrom(HEnvironment* env); +  void SetAndCopyParentChain(ArenaAllocator* allocator, HEnvironment* parent) { +    parent_ = new (allocator) HEnvironment(allocator, +                                           parent->Size(), +                                           parent->GetDexFile(), +                                           parent->GetMethodIdx(), +                                           parent->GetDexPc()); +    if (parent->GetParent() != nullptr) { +      parent_->SetAndCopyParentChain(allocator, parent->GetParent()); +    } +    parent_->CopyFrom(parent); +  } + +  void CopyFrom(const GrowableArray<HInstruction*>& locals); +  void CopyFrom(HEnvironment* environment); +    // Copy from `env`. If it's a loop phi for `loop_header`, copy the first    // input to the loop phi instead. This is for inserting instructions that    // require an environment (like HDeoptimization) in the loop pre-header. @@ -1080,6 +1142,28 @@ class HEnvironment : public ArenaObject<kArenaAllocMisc> {    size_t Size() const { return vregs_.Size(); } +  HEnvironment* GetParent() const { return parent_; } + +  void SetLocationAt(size_t index, Location location) { +    locations_.Put(index, location); +  } + +  Location GetLocationAt(size_t index) const { +    return locations_.Get(index); +  } + +  uint32_t GetDexPc() const { +    return dex_pc_; +  } + +  uint32_t GetMethodIdx() const { +    return method_idx_; +  } + +  const DexFile& GetDexFile() const { +    return dex_file_; +  } +   private:    // Record instructions' use entries of this environment for constant-time removal.    // It should only be called by HInstruction when a new environment use is added. @@ -1090,6 +1174,11 @@ class HEnvironment : public ArenaObject<kArenaAllocMisc> {    }    GrowableArray<HUserRecord<HEnvironment*> > vregs_; +  GrowableArray<Location> locations_; +  HEnvironment* parent_; +  const DexFile& dex_file_; +  const uint32_t method_idx_; +  const uint32_t dex_pc_;    friend class HInstruction; @@ -1221,6 +1310,11 @@ class HInstruction : public ArenaObject<kArenaAllocMisc> {    }    virtual bool NeedsEnvironment() const { return false; } +  virtual uint32_t GetDexPc() const { +    LOG(FATAL) << "GetDexPc() cannot be called on an instruction that" +                  " does not need an environment"; +    UNREACHABLE(); +  }    virtual bool IsControlFlow() const { return false; }    virtual bool CanThrow() const { return false; }    bool HasSideEffects() const { return side_effects_.HasSideEffects(); } @@ -1298,14 +1392,30 @@ class HInstruction : public ArenaObject<kArenaAllocMisc> {    // copying, the uses lists are being updated.    void CopyEnvironmentFrom(HEnvironment* environment) {      ArenaAllocator* allocator = GetBlock()->GetGraph()->GetArena(); -    environment_ = new (allocator) HEnvironment(allocator, environment->Size()); +    environment_ = new (allocator) HEnvironment( +        allocator, +        environment->Size(), +        environment->GetDexFile(), +        environment->GetMethodIdx(), +        environment->GetDexPc());      environment_->CopyFrom(environment); +    if (environment->GetParent() != nullptr) { +      environment_->SetAndCopyParentChain(allocator, environment->GetParent()); +    }    }    void CopyEnvironmentFromWithLoopPhiAdjustment(HEnvironment* environment,                                                  HBasicBlock* block) {      ArenaAllocator* allocator = GetBlock()->GetGraph()->GetArena(); -    environment_ = new (allocator) HEnvironment(allocator, environment->Size()); +    environment_ = new (allocator) HEnvironment( +        allocator, +        environment->Size(), +        environment->GetDexFile(), +        environment->GetMethodIdx(), +        environment->GetDexPc()); +    if (environment->GetParent() != nullptr) { +      environment_->SetAndCopyParentChain(allocator, environment->GetParent()); +    }      environment_->CopyFromWithLoopPhiAdjustment(environment, block);    } @@ -1682,7 +1792,7 @@ class HDeoptimize : public HTemplateInstruction<1> {    bool NeedsEnvironment() const OVERRIDE { return true; }    bool CanThrow() const OVERRIDE { return true; } -  uint32_t GetDexPc() const { return dex_pc_; } +  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }    DECLARE_INSTRUCTION(Deoptimize); @@ -2251,7 +2361,7 @@ class HInvoke : public HInstruction {    Primitive::Type GetType() const OVERRIDE { return return_type_; } -  uint32_t GetDexPc() const { return dex_pc_; } +  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }    uint32_t GetDexMethodIndex() const { return dex_method_index_; } @@ -2468,7 +2578,7 @@ class HNewInstance : public HExpression<0> {          type_index_(type_index),          entrypoint_(entrypoint) {} -  uint32_t GetDexPc() const { return dex_pc_; } +  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }    uint16_t GetTypeIndex() const { return type_index_; }    // Calls runtime so needs an environment. @@ -2520,7 +2630,7 @@ class HNewArray : public HExpression<1> {      SetRawInputAt(0, length);    } -  uint32_t GetDexPc() const { return dex_pc_; } +  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }    uint16_t GetTypeIndex() const { return type_index_; }    // Calls runtime so needs an environment. @@ -2615,7 +2725,7 @@ class HDiv : public HBinaryOperation {      return (y == -1) ? -x : x / y;    } -  uint32_t GetDexPc() const { return dex_pc_; } +  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }    DECLARE_INSTRUCTION(Div); @@ -2642,7 +2752,7 @@ class HRem : public HBinaryOperation {      return (y == -1) ? 0 : x % y;    } -  uint32_t GetDexPc() const { return dex_pc_; } +  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }    DECLARE_INSTRUCTION(Rem); @@ -2669,7 +2779,7 @@ class HDivZeroCheck : public HExpression<1> {    bool NeedsEnvironment() const OVERRIDE { return true; }    bool CanThrow() const OVERRIDE { return true; } -  uint32_t GetDexPc() const { return dex_pc_; } +  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }    DECLARE_INSTRUCTION(DivZeroCheck); @@ -2864,7 +2974,7 @@ class HTypeConversion : public HExpression<1> {    // Required by the x86 and ARM code generators when producing calls    // to the runtime. -  uint32_t GetDexPc() const { return dex_pc_; } +  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }    bool CanBeMoved() const OVERRIDE { return true; }    bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; } @@ -2974,7 +3084,7 @@ class HNullCheck : public HExpression<1> {    bool CanBeNull() const OVERRIDE { return false; } -  uint32_t GetDexPc() const { return dex_pc_; } +  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }    DECLARE_INSTRUCTION(NullCheck); @@ -3137,7 +3247,7 @@ class HArraySet : public HTemplateInstruction<3> {    bool NeedsTypeCheck() const { return needs_type_check_; } -  uint32_t GetDexPc() const { return dex_pc_; } +  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }    HInstruction* GetArray() const { return InputAt(0); }    HInstruction* GetIndex() const { return InputAt(1); } @@ -3207,7 +3317,7 @@ class HBoundsCheck : public HExpression<2> {    bool CanThrow() const OVERRIDE { return true; } -  uint32_t GetDexPc() const { return dex_pc_; } +  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }    DECLARE_INSTRUCTION(BoundsCheck); @@ -3247,19 +3357,25 @@ class HTemporary : public HTemplateInstruction<0> {  class HSuspendCheck : public HTemplateInstruction<0> {   public:    explicit HSuspendCheck(uint32_t dex_pc) -      : HTemplateInstruction(SideEffects::None()), dex_pc_(dex_pc) {} +      : HTemplateInstruction(SideEffects::None()), dex_pc_(dex_pc), slow_path_(nullptr) {}    bool NeedsEnvironment() const OVERRIDE {      return true;    } -  uint32_t GetDexPc() const { return dex_pc_; } +  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; } +  void SetSlowPath(SlowPathCode* slow_path) { slow_path_ = slow_path; } +  SlowPathCode* GetSlowPath() const { return slow_path_; }    DECLARE_INSTRUCTION(SuspendCheck);   private:    const uint32_t dex_pc_; +  // Only used for code generation, in order to share the same slow path between back edges +  // of a same loop. +  SlowPathCode* slow_path_; +    DISALLOW_COPY_AND_ASSIGN(HSuspendCheck);  }; @@ -3286,7 +3402,7 @@ class HLoadClass : public HExpression<0> {    size_t ComputeHashCode() const OVERRIDE { return type_index_; } -  uint32_t GetDexPc() const { return dex_pc_; } +  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }    uint16_t GetTypeIndex() const { return type_index_; }    bool IsReferrersClass() const { return is_referrers_class_; } @@ -3360,7 +3476,7 @@ class HLoadString : public HExpression<0> {    size_t ComputeHashCode() const OVERRIDE { return string_index_; } -  uint32_t GetDexPc() const { return dex_pc_; } +  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }    uint32_t GetStringIndex() const { return string_index_; }    // TODO: Can we deopt or debug when we resolve a string? @@ -3398,7 +3514,7 @@ class HClinitCheck : public HExpression<1> {      return true;    } -  uint32_t GetDexPc() const { return dex_pc_; } +  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }    HLoadClass* GetLoadClass() const { return InputAt(0)->AsLoadClass(); } @@ -3498,7 +3614,7 @@ class HThrow : public HTemplateInstruction<1> {    bool CanThrow() const OVERRIDE { return true; } -  uint32_t GetDexPc() const { return dex_pc_; } +  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }    DECLARE_INSTRUCTION(Throw); @@ -3532,7 +3648,7 @@ class HInstanceOf : public HExpression<2> {      return false;    } -  uint32_t GetDexPc() const { return dex_pc_; } +  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }    bool IsClassFinal() const { return class_is_final_; } @@ -3607,7 +3723,7 @@ class HCheckCast : public HTemplateInstruction<2> {    bool MustDoNullCheck() const { return must_do_null_check_; }    void ClearMustDoNullCheck() { must_do_null_check_ = false; } -  uint32_t GetDexPc() const { return dex_pc_; } +  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }    bool IsClassFinal() const { return class_is_final_; } @@ -3653,7 +3769,7 @@ class HMonitorOperation : public HTemplateInstruction<1> {    bool NeedsEnvironment() const OVERRIDE { return true; }    bool CanThrow() const OVERRIDE { return true; } -  uint32_t GetDexPc() const { return dex_pc_; } +  uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }    bool IsEnter() const { return kind_ == kEnter; } diff --git a/compiler/optimizing/nodes_test.cc b/compiler/optimizing/nodes_test.cc index 4e83ce576c..2736453ccc 100644 --- a/compiler/optimizing/nodes_test.cc +++ b/compiler/optimizing/nodes_test.cc @@ -16,6 +16,7 @@  #include "base/arena_allocator.h"  #include "nodes.h" +#include "optimizing_unit_test.h"  #include "gtest/gtest.h" @@ -29,7 +30,7 @@ TEST(Node, RemoveInstruction) {    ArenaPool pool;    ArenaAllocator allocator(&pool); -  HGraph* graph = new (&allocator) HGraph(&allocator); +  HGraph* graph = CreateGraph(&allocator);    HBasicBlock* entry = new (&allocator) HBasicBlock(graph);    graph->AddBlock(entry);    graph->SetEntryBlock(entry); @@ -49,7 +50,8 @@ TEST(Node, RemoveInstruction) {    first_block->AddSuccessor(exit_block);    exit_block->AddInstruction(new (&allocator) HExit()); -  HEnvironment* environment = new (&allocator) HEnvironment(&allocator, 1); +  HEnvironment* environment = new (&allocator) HEnvironment( +      &allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0);    null_check->SetRawEnvironment(environment);    environment->SetRawEnvAt(0, parameter);    parameter->AddEnvUseAt(null_check->GetEnvironment(), 0); @@ -70,7 +72,7 @@ TEST(Node, InsertInstruction) {    ArenaPool pool;    ArenaAllocator allocator(&pool); -  HGraph* graph = new (&allocator) HGraph(&allocator); +  HGraph* graph = CreateGraph(&allocator);    HBasicBlock* entry = new (&allocator) HBasicBlock(graph);    graph->AddBlock(entry);    graph->SetEntryBlock(entry); @@ -96,7 +98,7 @@ TEST(Node, AddInstruction) {    ArenaPool pool;    ArenaAllocator allocator(&pool); -  HGraph* graph = new (&allocator) HGraph(&allocator); +  HGraph* graph = CreateGraph(&allocator);    HBasicBlock* entry = new (&allocator) HBasicBlock(graph);    graph->AddBlock(entry);    graph->SetEntryBlock(entry); @@ -112,4 +114,51 @@ TEST(Node, AddInstruction) {    ASSERT_TRUE(parameter->GetUses().HasOnlyOneUse());  } +TEST(Node, ParentEnvironment) { +  ArenaPool pool; +  ArenaAllocator allocator(&pool); + +  HGraph* graph = CreateGraph(&allocator); +  HBasicBlock* entry = new (&allocator) HBasicBlock(graph); +  graph->AddBlock(entry); +  graph->SetEntryBlock(entry); +  HInstruction* parameter1 = new (&allocator) HParameterValue(0, Primitive::kPrimNot); +  HInstruction* with_environment = new (&allocator) HNullCheck(parameter1, 0); +  entry->AddInstruction(parameter1); +  entry->AddInstruction(with_environment); +  entry->AddInstruction(new (&allocator) HExit()); + +  ASSERT_TRUE(parameter1->HasUses()); +  ASSERT_TRUE(parameter1->GetUses().HasOnlyOneUse()); + +  HEnvironment* environment = new (&allocator) HEnvironment( +      &allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0); +  GrowableArray<HInstruction*> array(&allocator, 1); +  array.Add(parameter1); + +  environment->CopyFrom(array); +  with_environment->SetRawEnvironment(environment); + +  ASSERT_TRUE(parameter1->HasEnvironmentUses()); +  ASSERT_TRUE(parameter1->GetEnvUses().HasOnlyOneUse()); + +  HEnvironment* parent1 = new (&allocator) HEnvironment( +      &allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0); +  parent1->CopyFrom(array); + +  ASSERT_EQ(parameter1->GetEnvUses().SizeSlow(), 2u); + +  HEnvironment* parent2 = new (&allocator) HEnvironment( +      &allocator, 1, graph->GetDexFile(), graph->GetMethodIdx(), 0); +  parent2->CopyFrom(array); +  parent1->SetAndCopyParentChain(&allocator, parent2); + +  // One use for parent2, and one other use for the new parent of parent1. +  ASSERT_EQ(parameter1->GetEnvUses().SizeSlow(), 4u); + +  // We have copied the parent chain. So we now have two more uses. +  environment->SetAndCopyParentChain(&allocator, parent1); +  ASSERT_EQ(parameter1->GetEnvUses().SizeSlow(), 6u); +} +  }  // namespace art diff --git a/compiler/optimizing/optimizing_cfi_test.cc b/compiler/optimizing/optimizing_cfi_test.cc index b2c13adf35..7aea249c42 100644 --- a/compiler/optimizing/optimizing_cfi_test.cc +++ b/compiler/optimizing/optimizing_cfi_test.cc @@ -21,6 +21,7 @@  #include "cfi_test.h"  #include "gtest/gtest.h"  #include "optimizing/code_generator.h" +#include "optimizing/optimizing_unit_test.h"  #include "utils/assembler.h"  #include "optimizing/optimizing_cfi_test_expected.inc" @@ -45,10 +46,10 @@ class OptimizingCFITest  : public CFITest {      std::unique_ptr<const InstructionSetFeatures> isa_features;      std::string error;      isa_features.reset(InstructionSetFeatures::FromVariant(isa, "default", &error)); -    HGraph graph(&allocator); +    HGraph* graph = CreateGraph(&allocator);      // Generate simple frame with some spills.      std::unique_ptr<CodeGenerator> code_gen( -        CodeGenerator::Create(&graph, isa, *isa_features.get(), opts)); +        CodeGenerator::Create(graph, isa, *isa_features.get(), opts));      const int frame_size = 64;      int core_reg = 0;      int fp_reg = 0; diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 05451bcaa6..e993d778d4 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -512,7 +512,7 @@ CompiledMethod* OptimizingCompiler::TryCompile(const DexFile::CodeItem* code_ite    ArenaAllocator arena(Runtime::Current()->GetArenaPool());    HGraph* graph = new (&arena) HGraph( -      &arena, compiler_driver->GetCompilerOptions().GetDebuggable()); +      &arena, dex_file, method_idx, compiler_driver->GetCompilerOptions().GetDebuggable());    // For testing purposes, we put a special marker on method names that should be compiled    // with this compiler. This makes sure we're not regressing. diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h index 65c84e6942..b6b1bb1cad 100644 --- a/compiler/optimizing/optimizing_compiler_stats.h +++ b/compiler/optimizing/optimizing_compiler_stats.h @@ -29,25 +29,26 @@ enum MethodCompilationStat {    kCompiledBaseline,    kCompiledOptimized,    kCompiledQuick, -  kInstructionSimplifications,    kInlinedInvoke, -  kNotCompiledUnsupportedIsa, -  kNotCompiledPathological, +  kInstructionSimplifications, +  kNotCompiledBranchOutsideMethodCode, +  kNotCompiledCannotBuildSSA, +  kNotCompiledCantAccesType, +  kNotCompiledClassNotVerified,    kNotCompiledHugeMethod,    kNotCompiledLargeMethodNoBranches, -  kNotCompiledCannotBuildSSA,    kNotCompiledNoCodegen, -  kNotCompiledUnresolvedMethod, -  kNotCompiledUnresolvedField,    kNotCompiledNonSequentialRegPair, +  kNotCompiledPathological,    kNotCompiledSpaceFilter, -  kNotOptimizedTryCatch, -  kNotOptimizedDisabled, -  kNotCompiledCantAccesType, -  kNotOptimizedRegisterAllocator,    kNotCompiledUnhandledInstruction, +  kNotCompiledUnresolvedField, +  kNotCompiledUnresolvedMethod, +  kNotCompiledUnsupportedIsa,    kNotCompiledVerifyAtRuntime, -  kNotCompiledClassNotVerified, +  kNotOptimizedDisabled, +  kNotOptimizedRegisterAllocator, +  kNotOptimizedTryCatch,    kRemovedCheckedCast,    kRemovedDeadInstruction,    kRemovedNullCheck, @@ -98,23 +99,24 @@ class OptimizingCompilerStats {        case kCompiledQuick : return "kCompiledQuick";        case kInlinedInvoke : return "kInlinedInvoke";        case kInstructionSimplifications: return "kInstructionSimplifications"; -      case kNotCompiledUnsupportedIsa : return "kNotCompiledUnsupportedIsa"; -      case kNotCompiledPathological : return "kNotCompiledPathological"; +      case kNotCompiledBranchOutsideMethodCode: return "kNotCompiledBranchOutsideMethodCode"; +      case kNotCompiledCannotBuildSSA : return "kNotCompiledCannotBuildSSA"; +      case kNotCompiledCantAccesType : return "kNotCompiledCantAccesType"; +      case kNotCompiledClassNotVerified : return "kNotCompiledClassNotVerified";        case kNotCompiledHugeMethod : return "kNotCompiledHugeMethod";        case kNotCompiledLargeMethodNoBranches : return "kNotCompiledLargeMethodNoBranches"; -      case kNotCompiledCannotBuildSSA : return "kNotCompiledCannotBuildSSA";        case kNotCompiledNoCodegen : return "kNotCompiledNoCodegen"; -      case kNotCompiledUnresolvedMethod : return "kNotCompiledUnresolvedMethod"; -      case kNotCompiledUnresolvedField : return "kNotCompiledUnresolvedField";        case kNotCompiledNonSequentialRegPair : return "kNotCompiledNonSequentialRegPair"; -      case kNotOptimizedDisabled : return "kNotOptimizedDisabled"; -      case kNotOptimizedTryCatch : return "kNotOptimizedTryCatch"; -      case kNotCompiledCantAccesType : return "kNotCompiledCantAccesType"; +      case kNotCompiledPathological : return "kNotCompiledPathological";        case kNotCompiledSpaceFilter : return "kNotCompiledSpaceFilter"; -      case kNotOptimizedRegisterAllocator : return "kNotOptimizedRegisterAllocator";        case kNotCompiledUnhandledInstruction : return "kNotCompiledUnhandledInstruction"; +      case kNotCompiledUnresolvedField : return "kNotCompiledUnresolvedField"; +      case kNotCompiledUnresolvedMethod : return "kNotCompiledUnresolvedMethod"; +      case kNotCompiledUnsupportedIsa : return "kNotCompiledUnsupportedIsa";        case kNotCompiledVerifyAtRuntime : return "kNotCompiledVerifyAtRuntime"; -      case kNotCompiledClassNotVerified : return "kNotCompiledClassNotVerified"; +      case kNotOptimizedDisabled : return "kNotOptimizedDisabled"; +      case kNotOptimizedRegisterAllocator : return "kNotOptimizedRegisterAllocator"; +      case kNotOptimizedTryCatch : return "kNotOptimizedTryCatch";        case kRemovedCheckedCast: return "kRemovedCheckedCast";        case kRemovedDeadInstruction: return "kRemovedDeadInstruction";        case kRemovedNullCheck: return "kRemovedNullCheck"; diff --git a/compiler/optimizing/optimizing_unit_test.h b/compiler/optimizing/optimizing_unit_test.h index 6b236927da..4f8ec65e43 100644 --- a/compiler/optimizing/optimizing_unit_test.h +++ b/compiler/optimizing/optimizing_unit_test.h @@ -72,11 +72,16 @@ void RemoveSuspendChecks(HGraph* graph) {    }  } +inline HGraph* CreateGraph(ArenaAllocator* allocator) { +  return new (allocator) HGraph( +      allocator, *reinterpret_cast<DexFile*>(allocator->Alloc(sizeof(DexFile))), -1); +} +  // Create a control-flow graph from Dex instructions.  inline HGraph* CreateCFG(ArenaAllocator* allocator,                           const uint16_t* data,                           Primitive::Type return_type = Primitive::kPrimInt) { -  HGraph* graph = new (allocator) HGraph(allocator); +  HGraph* graph = CreateGraph(allocator);    HGraphBuilder builder(graph, return_type);    const DexFile::CodeItem* item =      reinterpret_cast<const DexFile::CodeItem*>(data); diff --git a/compiler/optimizing/pretty_printer_test.cc b/compiler/optimizing/pretty_printer_test.cc index 293fde978e..c56100dfa1 100644 --- a/compiler/optimizing/pretty_printer_test.cc +++ b/compiler/optimizing/pretty_printer_test.cc @@ -30,7 +30,7 @@ namespace art {  static void TestCode(const uint16_t* data, const char* expected) {    ArenaPool pool;    ArenaAllocator allocator(&pool); -  HGraph* graph = new (&allocator) HGraph(&allocator); +  HGraph* graph = CreateGraph(&allocator);    HGraphBuilder builder(graph);    const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);    bool graph_built = builder.BuildGraph(*item); diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc index 2375595978..f53f846326 100644 --- a/compiler/optimizing/register_allocator.cc +++ b/compiler/optimizing/register_allocator.cc @@ -1534,9 +1534,10 @@ void RegisterAllocator::ConnectSiblings(LiveInterval* interval) {        }        while (env_use != nullptr && env_use->GetPosition() <= range->GetEnd()) { -        DCHECK(current->CoversSlow(env_use->GetPosition()) || (env_use->GetPosition() == range->GetEnd())); -        LocationSummary* locations = env_use->GetUser()->GetLocations(); -        locations->SetEnvironmentAt(env_use->GetInputIndex(), source); +        DCHECK(current->CoversSlow(env_use->GetPosition()) +               || (env_use->GetPosition() == range->GetEnd())); +        HEnvironment* environment = env_use->GetUser()->GetEnvironment(); +        environment->SetLocationAt(env_use->GetInputIndex(), source);          env_use = env_use->GetNext();        } diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc index 8c6d904a4c..b72ffb8bf7 100644 --- a/compiler/optimizing/register_allocator_test.cc +++ b/compiler/optimizing/register_allocator_test.cc @@ -38,7 +38,7 @@ namespace art {  static bool Check(const uint16_t* data) {    ArenaPool pool;    ArenaAllocator allocator(&pool); -  HGraph* graph = new (&allocator) HGraph(&allocator); +  HGraph* graph = CreateGraph(&allocator);    HGraphBuilder builder(graph);    const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);    builder.BuildGraph(*item); @@ -60,7 +60,7 @@ static bool Check(const uint16_t* data) {  TEST(RegisterAllocatorTest, ValidateIntervals) {    ArenaPool pool;    ArenaAllocator allocator(&pool); -  HGraph* graph = new (&allocator) HGraph(&allocator); +  HGraph* graph = CreateGraph(&allocator);    std::unique_ptr<const X86InstructionSetFeatures> features_x86(        X86InstructionSetFeatures::FromCppDefines());    x86::CodeGeneratorX86 codegen(graph, *features_x86.get(), CompilerOptions()); @@ -255,7 +255,7 @@ TEST(RegisterAllocatorTest, Loop2) {  }  static HGraph* BuildSSAGraph(const uint16_t* data, ArenaAllocator* allocator) { -  HGraph* graph = new (allocator) HGraph(allocator); +  HGraph* graph = CreateGraph(allocator);    HGraphBuilder builder(graph);    const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);    builder.BuildGraph(*item); @@ -463,7 +463,7 @@ static HGraph* BuildIfElseWithPhi(ArenaAllocator* allocator,                                    HPhi** phi,                                    HInstruction** input1,                                    HInstruction** input2) { -  HGraph* graph = new (allocator) HGraph(allocator); +  HGraph* graph = CreateGraph(allocator);    HBasicBlock* entry = new (allocator) HBasicBlock(graph);    graph->AddBlock(entry);    graph->SetEntryBlock(entry); @@ -593,7 +593,7 @@ TEST(RegisterAllocatorTest, PhiHint) {  static HGraph* BuildFieldReturn(ArenaAllocator* allocator,                                  HInstruction** field,                                  HInstruction** ret) { -  HGraph* graph = new (allocator) HGraph(allocator); +  HGraph* graph = CreateGraph(allocator);    HBasicBlock* entry = new (allocator) HBasicBlock(graph);    graph->AddBlock(entry);    graph->SetEntryBlock(entry); @@ -661,7 +661,7 @@ TEST(RegisterAllocatorTest, ExpectedInRegisterHint) {  static HGraph* BuildTwoSubs(ArenaAllocator* allocator,                              HInstruction** first_sub,                              HInstruction** second_sub) { -  HGraph* graph = new (allocator) HGraph(allocator); +  HGraph* graph = CreateGraph(allocator);    HBasicBlock* entry = new (allocator) HBasicBlock(graph);    graph->AddBlock(entry);    graph->SetEntryBlock(entry); @@ -731,7 +731,7 @@ TEST(RegisterAllocatorTest, SameAsFirstInputHint) {  static HGraph* BuildDiv(ArenaAllocator* allocator,                          HInstruction** div) { -  HGraph* graph = new (allocator) HGraph(allocator); +  HGraph* graph = CreateGraph(allocator);    HBasicBlock* entry = new (allocator) HBasicBlock(graph);    graph->AddBlock(entry);    graph->SetEntryBlock(entry); @@ -783,7 +783,7 @@ TEST(RegisterAllocatorTest, SpillInactive) {    // Create a synthesized graph to please the register_allocator and    // ssa_liveness_analysis code.    ArenaAllocator allocator(&pool); -  HGraph* graph = new (&allocator) HGraph(&allocator); +  HGraph* graph = CreateGraph(&allocator);    HBasicBlock* entry = new (&allocator) HBasicBlock(graph);    graph->AddBlock(entry);    graph->SetEntryBlock(entry); diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc index b66e655d2b..59a2852735 100644 --- a/compiler/optimizing/ssa_builder.cc +++ b/compiler/optimizing/ssa_builder.cc @@ -332,7 +332,7 @@ void SsaBuilder::BuildSsa() {  }  HInstruction* SsaBuilder::ValueOfLocal(HBasicBlock* block, size_t local) { -  return GetLocalsFor(block)->GetInstructionAt(local); +  return GetLocalsFor(block)->Get(local);  }  void SsaBuilder::VisitBasicBlock(HBasicBlock* block) { @@ -349,7 +349,7 @@ void SsaBuilder::VisitBasicBlock(HBasicBlock* block) {          HPhi* phi = new (GetGraph()->GetArena()) HPhi(              GetGraph()->GetArena(), local, 0, Primitive::kPrimVoid);          block->AddPhi(phi); -        current_locals_->SetRawEnvAt(local, phi); +        current_locals_->Put(local, phi);        }      }      // Save the loop header so that the last phase of the analysis knows which @@ -389,7 +389,7 @@ void SsaBuilder::VisitBasicBlock(HBasicBlock* block) {          block->AddPhi(phi);          value = phi;        } -      current_locals_->SetRawEnvAt(local, value); +      current_locals_->Put(local, value);      }    } @@ -520,7 +520,7 @@ HInstruction* SsaBuilder::GetReferenceTypeEquivalent(HInstruction* value) {  }  void SsaBuilder::VisitLoadLocal(HLoadLocal* load) { -  HInstruction* value = current_locals_->GetInstructionAt(load->GetLocal()->GetRegNumber()); +  HInstruction* value = current_locals_->Get(load->GetLocal()->GetRegNumber());    // If the operation requests a specific type, we make sure its input is of that type.    if (load->GetType() != value->GetType()) {      if (load->GetType() == Primitive::kPrimFloat || load->GetType() == Primitive::kPrimDouble) { @@ -534,7 +534,7 @@ void SsaBuilder::VisitLoadLocal(HLoadLocal* load) {  }  void SsaBuilder::VisitStoreLocal(HStoreLocal* store) { -  current_locals_->SetRawEnvAt(store->GetLocal()->GetRegNumber(), store->InputAt(1)); +  current_locals_->Put(store->GetLocal()->GetRegNumber(), store->InputAt(1));    store->GetBlock()->RemoveInstruction(store);  } @@ -543,8 +543,12 @@ void SsaBuilder::VisitInstruction(HInstruction* instruction) {      return;    }    HEnvironment* environment = new (GetGraph()->GetArena()) HEnvironment( -      GetGraph()->GetArena(), current_locals_->Size()); -  environment->CopyFrom(current_locals_); +      GetGraph()->GetArena(), +      current_locals_->Size(), +      GetGraph()->GetDexFile(), +      GetGraph()->GetMethodIdx(), +      instruction->GetDexPc()); +  environment->CopyFrom(*current_locals_);    instruction->SetRawEnvironment(environment);  } diff --git a/compiler/optimizing/ssa_builder.h b/compiler/optimizing/ssa_builder.h index 265e95b4ac..1c83c4ba48 100644 --- a/compiler/optimizing/ssa_builder.h +++ b/compiler/optimizing/ssa_builder.h @@ -58,14 +58,15 @@ class SsaBuilder : public HGraphVisitor {    void BuildSsa(); -  HEnvironment* GetLocalsFor(HBasicBlock* block) { -    HEnvironment* env = locals_for_.Get(block->GetBlockId()); -    if (env == nullptr) { -      env = new (GetGraph()->GetArena()) HEnvironment( +  GrowableArray<HInstruction*>* GetLocalsFor(HBasicBlock* block) { +    GrowableArray<HInstruction*>* locals = locals_for_.Get(block->GetBlockId()); +    if (locals == nullptr) { +      locals = new (GetGraph()->GetArena()) GrowableArray<HInstruction*>(            GetGraph()->GetArena(), GetGraph()->GetNumberOfVRegs()); -      locals_for_.Put(block->GetBlockId(), env); +      locals->SetSize(GetGraph()->GetNumberOfVRegs()); +      locals_for_.Put(block->GetBlockId(), locals);      } -    return env; +    return locals;    }    HInstruction* ValueOfLocal(HBasicBlock* block, size_t local); @@ -93,14 +94,14 @@ class SsaBuilder : public HGraphVisitor {    static HPhi* GetFloatDoubleOrReferenceEquivalentOfPhi(HPhi* phi, Primitive::Type type);    // Locals for the current block being visited. -  HEnvironment* current_locals_; +  GrowableArray<HInstruction*>* current_locals_;    // Keep track of loop headers found. The last phase of the analysis iterates    // over these blocks to set the inputs of their phis.    GrowableArray<HBasicBlock*> loop_headers_;    // HEnvironment for each block. -  GrowableArray<HEnvironment*> locals_for_; +  GrowableArray<GrowableArray<HInstruction*>*> locals_for_;    DISALLOW_COPY_AND_ASSIGN(SsaBuilder);  }; diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc index 17841685b1..250eb04a1c 100644 --- a/compiler/optimizing/ssa_liveness_analysis.cc +++ b/compiler/optimizing/ssa_liveness_analysis.cc @@ -75,9 +75,7 @@ void SsaLivenessAnalysis::LinearizeGraph() {      HBasicBlock* block = it.Current();      size_t number_of_forward_predecessors = block->GetPredecessors().Size();      if (block->IsLoopHeader()) { -      // We rely on having simplified the CFG. -      DCHECK_EQ(1u, block->GetLoopInformation()->NumberOfBackEdges()); -      number_of_forward_predecessors--; +      number_of_forward_predecessors -= block->GetLoopInformation()->NumberOfBackEdges();      }      forward_predecessors.Put(block->GetBlockId(), number_of_forward_predecessors);    } @@ -220,10 +218,11 @@ void SsaLivenessAnalysis::ComputeLiveRanges() {        // Process the environment first, because we know their uses come after        // or at the same liveness position of inputs. -      if (current->HasEnvironment()) { +      for (HEnvironment* environment = current->GetEnvironment(); +           environment != nullptr; +           environment = environment->GetParent()) {          // Handle environment uses. See statements (b) and (c) of the          // SsaLivenessAnalysis. -        HEnvironment* environment = current->GetEnvironment();          for (size_t i = 0, e = environment->Size(); i < e; ++i) {            HInstruction* instruction = environment->GetInstructionAt(i);            bool should_be_live = ShouldBeLiveForEnvironment(instruction); @@ -233,7 +232,7 @@ void SsaLivenessAnalysis::ComputeLiveRanges() {            }            if (instruction != nullptr) {              instruction->GetLiveInterval()->AddUse( -                current, i, /* is_environment */ true, should_be_live); +                current, environment, i, should_be_live);            }          }        } @@ -245,7 +244,7 @@ void SsaLivenessAnalysis::ComputeLiveRanges() {          // to be materialized.          if (input->HasSsaIndex()) {            live_in->SetBit(input->GetSsaIndex()); -          input->GetLiveInterval()->AddUse(current, i, /* is_environment */ false); +          input->GetLiveInterval()->AddUse(current, /* environment */ nullptr, i);          }        }      } @@ -264,13 +263,12 @@ void SsaLivenessAnalysis::ComputeLiveRanges() {      }      if (block->IsLoopHeader()) { -      HBasicBlock* back_edge = block->GetLoopInformation()->GetBackEdges().Get(0); +      size_t last_position = block->GetLoopInformation()->GetLifetimeEnd();        // For all live_in instructions at the loop header, we need to create a range        // that covers the full loop.        for (uint32_t idx : live_in->Indexes()) {          HInstruction* current = instructions_from_ssa_index_.Get(idx); -        current->GetLiveInterval()->AddLoopRange(block->GetLifetimeStart(), -                                                 back_edge->GetLifetimeEnd()); +        current->GetLiveInterval()->AddLoopRange(block->GetLifetimeStart(), last_position);        }      }    } diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h index 7b98c4eab5..82c5454bb0 100644 --- a/compiler/optimizing/ssa_liveness_analysis.h +++ b/compiler/optimizing/ssa_liveness_analysis.h @@ -104,13 +104,13 @@ class LiveRange FINAL : public ArenaObject<kArenaAllocMisc> {  class UsePosition : public ArenaObject<kArenaAllocMisc> {   public:    UsePosition(HInstruction* user, +              HEnvironment* environment,                size_t input_index, -              bool is_environment,                size_t position,                UsePosition* next)        : user_(user), +        environment_(environment),          input_index_(input_index), -        is_environment_(is_environment),          position_(position),          next_(next) {      DCHECK((user == nullptr) @@ -129,7 +129,7 @@ class UsePosition : public ArenaObject<kArenaAllocMisc> {    HInstruction* GetUser() const { return user_; } -  bool GetIsEnvironment() const { return is_environment_; } +  bool GetIsEnvironment() const { return environment_ != nullptr; }    bool IsSynthesized() const { return user_ == nullptr; }    size_t GetInputIndex() const { return input_index_; } @@ -144,7 +144,7 @@ class UsePosition : public ArenaObject<kArenaAllocMisc> {    UsePosition* Dup(ArenaAllocator* allocator) const {      return new (allocator) UsePosition( -        user_, input_index_, is_environment_, position_, +        user_, environment_, input_index_, position_,          next_ == nullptr ? nullptr : next_->Dup(allocator));    } @@ -159,8 +159,8 @@ class UsePosition : public ArenaObject<kArenaAllocMisc> {   private:    HInstruction* const user_; +  HEnvironment* const environment_;    const size_t input_index_; -  const bool is_environment_;    const size_t position_;    UsePosition* next_; @@ -237,15 +237,16 @@ class LiveInterval : public ArenaObject<kArenaAllocMisc> {      DCHECK(first_env_use_ == nullptr) << "A temporary cannot have environment user";      size_t position = instruction->GetLifetimePosition();      first_use_ = new (allocator_) UsePosition( -        instruction, temp_index, /* is_environment */ false, position, first_use_); +        instruction, /* environment */ nullptr, temp_index, position, first_use_);      AddRange(position, position + 1);    }    void AddUse(HInstruction* instruction, +              HEnvironment* environment,                size_t input_index, -              bool is_environment,                bool keep_alive = false) {      // Set the use within the instruction. +    bool is_environment = (environment != nullptr);      size_t position = instruction->GetLifetimePosition() + 1;      LocationSummary* locations = instruction->GetLocations();      if (!is_environment) { @@ -279,7 +280,7 @@ class LiveInterval : public ArenaObject<kArenaAllocMisc> {        }        DCHECK(first_use_->GetPosition() + 1 == position);        UsePosition* new_use = new (allocator_) UsePosition( -          instruction, input_index, is_environment, position, cursor->GetNext()); +          instruction, environment, input_index, position, cursor->GetNext());        cursor->SetNext(new_use);        if (first_range_->GetEnd() == first_use_->GetPosition()) {          first_range_->end_ = position; @@ -289,10 +290,10 @@ class LiveInterval : public ArenaObject<kArenaAllocMisc> {      if (is_environment) {        first_env_use_ = new (allocator_) UsePosition( -          instruction, input_index, is_environment, position, first_env_use_); +          instruction, environment, input_index, position, first_env_use_);      } else {        first_use_ = new (allocator_) UsePosition( -          instruction, input_index, is_environment, position, first_use_); +          instruction, environment, input_index, position, first_use_);      }      if (is_environment && !keep_alive) { @@ -331,7 +332,7 @@ class LiveInterval : public ArenaObject<kArenaAllocMisc> {        AddBackEdgeUses(*block);      }      first_use_ = new (allocator_) UsePosition( -        instruction, input_index, false, block->GetLifetimeEnd(), first_use_); +        instruction, /* environment */ nullptr, input_index, block->GetLifetimeEnd(), first_use_);    }    void AddRange(size_t start, size_t end) { @@ -973,7 +974,11 @@ class LiveInterval : public ArenaObject<kArenaAllocMisc> {          break;        } -      size_t back_edge_use_position = current->GetSingleBackEdge()->GetLifetimeEnd(); +      // We're only adding a synthesized use at the last back edge. Adding syntehsized uses on +      // all back edges is not necessary: anything used in the loop will have its use at the +      // last back edge. If we want branches in a loop to have better register allocation than +      // another branch, then it is the linear order we should change. +      size_t back_edge_use_position = current->GetLifetimeEnd();        if ((first_use_ != nullptr) && (first_use_->GetPosition() <= back_edge_use_position)) {          // There was a use already seen in this loop. Therefore the previous call to `AddUse`          // already inserted the backedge use. We can stop going outward. @@ -985,8 +990,11 @@ class LiveInterval : public ArenaObject<kArenaAllocMisc> {               || back_edge_use_position > last_in_new_list->GetPosition());        UsePosition* new_use = new (allocator_) UsePosition( -          nullptr, UsePosition::kNoInput, /* is_environment */ false, -          back_edge_use_position, nullptr); +          /* user */ nullptr, +          /* environment */ nullptr, +          UsePosition::kNoInput, +          back_edge_use_position, +          /* next */ nullptr);        if (last_in_new_list != nullptr) {          // Going outward. The latest created use needs to point to the new use. diff --git a/compiler/optimizing/ssa_test.cc b/compiler/optimizing/ssa_test.cc index 00c241b85a..fb3e7d798c 100644 --- a/compiler/optimizing/ssa_test.cc +++ b/compiler/optimizing/ssa_test.cc @@ -78,7 +78,7 @@ static void ReNumberInstructions(HGraph* graph) {  static void TestCode(const uint16_t* data, const char* expected) {    ArenaPool pool;    ArenaAllocator allocator(&pool); -  HGraph* graph = new (&allocator) HGraph(&allocator); +  HGraph* graph = CreateGraph(&allocator);    HGraphBuilder builder(graph);    const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);    bool graph_built = builder.BuildGraph(*item); @@ -373,30 +373,26 @@ TEST(SsaTest, Loop6) {    const char* expected =      "BasicBlock 0, succ: 1\n"      "  0: IntConstant 0 [5]\n" -    "  1: IntConstant 4 [14, 8, 8]\n" -    "  2: IntConstant 5 [14]\n" +    "  1: IntConstant 4 [5, 8, 8]\n" +    "  2: IntConstant 5 [5]\n"      "  3: Goto\n"      "BasicBlock 1, pred: 0, succ: 2\n"      "  4: Goto\n" -    "BasicBlock 2, pred: 1, 8, succ: 6, 3\n" -    "  5: Phi(0, 14) [12, 6, 6]\n" +    "BasicBlock 2, pred: 1, 4, 5, succ: 6, 3\n" +    "  5: Phi(0, 2, 1) [12, 6, 6]\n"      "  6: Equal(5, 5) [7]\n"      "  7: If(6)\n"      "BasicBlock 3, pred: 2, succ: 5, 4\n"      "  8: Equal(1, 1) [9]\n"      "  9: If(8)\n" -    "BasicBlock 4, pred: 3, succ: 8\n" +    "BasicBlock 4, pred: 3, succ: 2\n"      "  10: Goto\n" -    "BasicBlock 5, pred: 3, succ: 8\n" +    "BasicBlock 5, pred: 3, succ: 2\n"      "  11: Goto\n"      "BasicBlock 6, pred: 2, succ: 7\n"      "  12: Return(5)\n"      "BasicBlock 7, pred: 6\n" -    "  13: Exit\n" -    // Synthesized single back edge of loop. -    "BasicBlock 8, pred: 5, 4, succ: 2\n" -    "  14: Phi(1, 2) [5]\n" -    "  15: Goto\n"; +    "  13: Exit\n";    const uint16_t data[] = ONE_REGISTER_CODE_ITEM(      Instruction::CONST_4 | 0 | 0, diff --git a/compiler/optimizing/suspend_check_test.cc b/compiler/optimizing/suspend_check_test.cc index a5a0eb2114..5ca66a1de6 100644 --- a/compiler/optimizing/suspend_check_test.cc +++ b/compiler/optimizing/suspend_check_test.cc @@ -30,7 +30,7 @@ namespace art {  static void TestCode(const uint16_t* data) {    ArenaPool pool;    ArenaAllocator allocator(&pool); -  HGraph* graph = new (&allocator) HGraph(&allocator); +  HGraph* graph = CreateGraph(&allocator);    HGraphBuilder builder(graph);    const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);    bool graph_built = builder.BuildGraph(*item); diff --git a/compiler/utils/mips64/assembler_mips64.cc b/compiler/utils/mips64/assembler_mips64.cc index 282ab96ce4..5e9653df33 100644 --- a/compiler/utils/mips64/assembler_mips64.cc +++ b/compiler/utils/mips64/assembler_mips64.cc @@ -272,6 +272,10 @@ void Mips64Assembler::Lhu(GpuRegister rt, GpuRegister rs, uint16_t imm16) {    EmitI(0x25, rs, rt, imm16);  } +void Mips64Assembler::Lwu(GpuRegister rt, GpuRegister rs, uint16_t imm16) { +  EmitI(0x27, rs, rt, imm16); +} +  void Mips64Assembler::Lui(GpuRegister rt, uint16_t imm16) {    EmitI(0xf, static_cast<GpuRegister>(0), rt, imm16);  } @@ -480,6 +484,9 @@ void Mips64Assembler::LoadFromOffset(LoadOperandType type, GpuRegister reg, GpuR      case kLoadWord:        Lw(reg, base, offset);        break; +    case kLoadUnsignedWord: +      Lwu(reg, base, offset); +      break;      case kLoadDoubleword:        // TODO: alignment issues ???        Ld(reg, base, offset); @@ -512,7 +519,6 @@ void Mips64Assembler::EmitLoad(ManagedRegister m_dst, GpuRegister src_register,      CHECK_EQ(0u, size) << dst;    } else if (dst.IsGpuRegister()) {      if (size == 4) { -      CHECK_EQ(4u, size) << dst;        LoadFromOffset(kLoadWord, dst.AsGpuRegister(), src_register, src_offset);      } else if (size == 8) {        CHECK_EQ(8u, size) << dst; @@ -740,14 +746,13 @@ void Mips64Assembler::LoadFromThread64(ManagedRegister mdest, ThreadOffset<8> sr  void Mips64Assembler::LoadRef(ManagedRegister mdest, FrameOffset src) {    Mips64ManagedRegister dest = mdest.AsMips64();    CHECK(dest.IsGpuRegister()); -  LoadFromOffset(kLoadWord, dest.AsGpuRegister(), SP, src.Int32Value()); +  LoadFromOffset(kLoadUnsignedWord, dest.AsGpuRegister(), SP, src.Int32Value());  } -void Mips64Assembler::LoadRef(ManagedRegister mdest, ManagedRegister base, -                            MemberOffset offs) { +void Mips64Assembler::LoadRef(ManagedRegister mdest, ManagedRegister base, MemberOffset offs) {    Mips64ManagedRegister dest = mdest.AsMips64(); -  CHECK(dest.IsGpuRegister() && dest.IsGpuRegister()); -  LoadFromOffset(kLoadWord, dest.AsGpuRegister(), +  CHECK(dest.IsGpuRegister() && base.AsMips64().IsGpuRegister()); +  LoadFromOffset(kLoadUnsignedWord, dest.AsGpuRegister(),                   base.AsMips64().AsGpuRegister(), offs.Int32Value());    if (kPoisonHeapReferences) {      Subu(dest.AsGpuRegister(), ZERO, dest.AsGpuRegister()); @@ -921,7 +926,7 @@ void Mips64Assembler::CreateHandleScopeEntry(ManagedRegister mout_reg,      // the address in the handle scope holding the reference.      // e.g. out_reg = (handle == 0) ? 0 : (SP+handle_offset)      if (in_reg.IsNoRegister()) { -      LoadFromOffset(kLoadWord, out_reg.AsGpuRegister(), +      LoadFromOffset(kLoadUnsignedWord, out_reg.AsGpuRegister(),                       SP, handle_scope_offset.Int32Value());        in_reg = out_reg;      } @@ -944,7 +949,7 @@ void Mips64Assembler::CreateHandleScopeEntry(FrameOffset out_off,    CHECK(scratch.IsGpuRegister()) << scratch;    if (null_allowed) {      Label null_arg; -    LoadFromOffset(kLoadWord, scratch.AsGpuRegister(), SP, +    LoadFromOffset(kLoadUnsignedWord, scratch.AsGpuRegister(), SP,                     handle_scope_offset.Int32Value());      // Null values get a handle scope entry value of 0.  Otherwise, the handle scope entry is      // the address in the handle scope holding the reference. @@ -998,7 +1003,7 @@ void Mips64Assembler::Call(FrameOffset base, Offset offset, ManagedRegister mscr    Mips64ManagedRegister scratch = mscratch.AsMips64();    CHECK(scratch.IsGpuRegister()) << scratch;    // Call *(*(SP + base) + offset) -  LoadFromOffset(kLoadWord, scratch.AsGpuRegister(), +  LoadFromOffset(kLoadUnsignedWord, scratch.AsGpuRegister(),                   SP, base.Int32Value());    LoadFromOffset(kLoadDoubleword, scratch.AsGpuRegister(),                   scratch.AsGpuRegister(), offset.Int32Value()); diff --git a/compiler/utils/mips64/assembler_mips64.h b/compiler/utils/mips64/assembler_mips64.h index b7f6a9e83a..2d7c661eac 100644 --- a/compiler/utils/mips64/assembler_mips64.h +++ b/compiler/utils/mips64/assembler_mips64.h @@ -36,6 +36,7 @@ enum LoadOperandType {    kLoadSignedHalfword,    kLoadUnsignedHalfword,    kLoadWord, +  kLoadUnsignedWord,    kLoadDoubleword  }; @@ -85,6 +86,7 @@ class Mips64Assembler FINAL : public Assembler {    void Ld(GpuRegister rt, GpuRegister rs, uint16_t imm16);    void Lbu(GpuRegister rt, GpuRegister rs, uint16_t imm16);    void Lhu(GpuRegister rt, GpuRegister rs, uint16_t imm16); +  void Lwu(GpuRegister rt, GpuRegister rs, uint16_t imm16);    void Lui(GpuRegister rt, uint16_t imm16);    void Mfhi(GpuRegister rd);    void Mflo(GpuRegister rd); diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc index f2541a2113..7e7520066d 100644 --- a/compiler/utils/x86/assembler_x86.cc +++ b/compiler/utils/x86/assembler_x86.cc @@ -1507,6 +1507,14 @@ void X86Assembler::jmp(Label* label) {  } +void X86Assembler::repne_scasw() { +  AssemblerBuffer::EnsureCapacity ensured(&buffer_); +  EmitUint8(0x66); +  EmitUint8(0xF2); +  EmitUint8(0xAF); +} + +  X86Assembler* X86Assembler::lock() {    AssemblerBuffer::EnsureCapacity ensured(&buffer_);    EmitUint8(0xF0); diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h index 946c96de71..136b0cbfdb 100644 --- a/compiler/utils/x86/assembler_x86.h +++ b/compiler/utils/x86/assembler_x86.h @@ -464,6 +464,8 @@ class X86Assembler FINAL : public Assembler {    void jmp(const Address& address);    void jmp(Label* label); +  void repne_scasw(); +    X86Assembler* lock();    void cmpxchgl(const Address& address, Register reg);    void cmpxchg8b(const Address& address); diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc index f326e496d4..aacc57bb0c 100644 --- a/compiler/utils/x86/assembler_x86_test.cc +++ b/compiler/utils/x86/assembler_x86_test.cc @@ -190,4 +190,10 @@ TEST_F(AssemblerX86Test, FPUIntegerStore) {    DriverStr(expected, "FPUIntegerStore");  } +TEST_F(AssemblerX86Test, Repnescasw) { +  GetAssembler()->repne_scasw(); +  const char* expected = "repne scasw\n"; +  DriverStr(expected, "Repnescasw"); +} +  }  // namespace art diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc index c0ca7ef437..feceecac68 100644 --- a/compiler/utils/x86_64/assembler_x86_64.cc +++ b/compiler/utils/x86_64/assembler_x86_64.cc @@ -2065,6 +2065,14 @@ void X86_64Assembler::bswapq(CpuRegister dst) {  } +void X86_64Assembler::repne_scasw() { +  AssemblerBuffer::EnsureCapacity ensured(&buffer_); +  EmitUint8(0x66); +  EmitUint8(0xF2); +  EmitUint8(0xAF); +} + +  void X86_64Assembler::LoadDoubleConstant(XmmRegister dst, double value) {    // TODO: Need to have a code constants table.    int64_t constant = bit_cast<int64_t, double>(value); diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h index f5327a8d02..162714af68 100644 --- a/compiler/utils/x86_64/assembler_x86_64.h +++ b/compiler/utils/x86_64/assembler_x86_64.h @@ -601,6 +601,8 @@ class X86_64Assembler FINAL : public Assembler {    void bswapl(CpuRegister dst);    void bswapq(CpuRegister dst); +  void repne_scasw(); +    //    // Macros for High-level operations.    // diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc index 9e4144ac26..0be4d632fb 100644 --- a/compiler/utils/x86_64/assembler_x86_64_test.cc +++ b/compiler/utils/x86_64/assembler_x86_64_test.cc @@ -1215,4 +1215,10 @@ TEST_F(AssemblerX86_64Test, MovsxbRegs) {    DriverStr(Repeatrb(&x86_64::X86_64Assembler::movsxb, "movsbl %{reg2}, %{reg1}"), "movsxb");  } +TEST_F(AssemblerX86_64Test, Repnescasw) { +  GetAssembler()->repne_scasw(); +  const char* expected = "repne scasw\n"; +  DriverStr(expected, "Repnescasw"); +} +  }  // namespace art diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index 1d316fcea5..f8b0734428 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -182,7 +182,7 @@      // Restore xSELF as it might be scratched.      mov xSELF, xETR      // ETR -    ldr xETR, [sp, #16] +    ldr xETR, [sp, #32]      .cfi_restore x21      add sp, sp, #112 diff --git a/runtime/arch/mips64/asm_support_mips64.S b/runtime/arch/mips64/asm_support_mips64.S index 10976bb70d..26137777e3 100644 --- a/runtime/arch/mips64/asm_support_mips64.S +++ b/runtime/arch/mips64/asm_support_mips64.S @@ -27,7 +27,8 @@  #define rSELF $s1 -    //  Declare a function called name, sets up $gp. +    // Declare a function called name, sets up $gp. +    // This macro modifies t8.  .macro ENTRY name      .type \name, %function      .global \name @@ -35,10 +36,11 @@      .balign 16  \name:      .cfi_startproc +    // Set up $gp and store the previous $gp value to $t8. It will be pushed to the +    // stack after the frame has been constructed. +    .cpsetup $t9, $t8, \name      // Ensure we get a sane starting CFA.      .cfi_def_cfa $sp,0 -    // Load $gp. We expect that ".set noreorder" is in effect. -    .cpload $t9      // Declare a local convenience label to be branched to when $gp is already set up.  .L\name\()_gp_set:  .endm diff --git a/runtime/arch/mips64/context_mips64.cc b/runtime/arch/mips64/context_mips64.cc index ce99b40b92..6b3f4c9d43 100644 --- a/runtime/arch/mips64/context_mips64.cc +++ b/runtime/arch/mips64/context_mips64.cc @@ -18,7 +18,7 @@  #include "mirror/art_method-inl.h"  #include "quick/quick_method_frame_info.h" -#include "util.h" +#include "utils.h"  namespace art {  namespace mips64 { diff --git a/runtime/arch/mips64/jni_entrypoints_mips64.S b/runtime/arch/mips64/jni_entrypoints_mips64.S index 10856668a2..70d7d9742c 100644 --- a/runtime/arch/mips64/jni_entrypoints_mips64.S +++ b/runtime/arch/mips64/jni_entrypoints_mips64.S @@ -44,8 +44,11 @@ ENTRY art_jni_dlsym_lookup_stub      .cfi_rel_offset 5, 8      sd     $a0, 0($sp)      .cfi_rel_offset 4, 0 -    jal    artFindNativeMethod  # (Thread*)      move   $a0, $s1             # pass Thread::Current() +    jal    artFindNativeMethod  # (Thread*) +    .cpreturn                   # Restore gp from t8 in branch delay slot. gp is not used +                                # anymore, and t8 may be clobbered in artFindNativeMethod. +      ld     $a0, 0($sp)          # restore registers from stack      .cfi_restore 4      ld     $a1, 8($sp) diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S index d781e7662c..ff79b5d77c 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -27,6 +27,19 @@      .extern artDeliverPendingExceptionFromCode      /* +     * Macro that sets up $gp and stores the previous $gp value to $t8. +     * This macro modifies v1 and t8. +     */ +.macro SETUP_GP +    move $v1, $ra +    bal 1f +    nop +1: +    .cpsetup $ra, $t8, 1b +    move $ra, $v1 +.endm + +    /*       * Macro that sets up the callee save frame to conform with       * Runtime::CreateCalleeSaveMethod(kSaveAll)       * callee-save: padding + $f24-$f31 + $s0-$s7 + $gp + $ra + $s8 = 19 total + 1x8 bytes padding @@ -44,8 +57,8 @@      .cfi_rel_offset 31, 152      sd     $s8, 144($sp)      .cfi_rel_offset 30, 144 -    sd     $gp, 136($sp) -    .cfi_rel_offset 28, 136 +    sd     $t8, 136($sp)           # t8 holds caller's gp, now save it to the stack. +    .cfi_rel_offset 28, 136        # Value from gp is pushed, so set the cfi offset accordingly.      sd     $s7, 128($sp)      .cfi_rel_offset 23, 128      sd     $s6, 120($sp) @@ -102,8 +115,8 @@      .cfi_rel_offset 31, 72      sd     $s8, 64($sp)      .cfi_rel_offset 30, 64 -    sd     $gp, 56($sp) -    .cfi_rel_offset 28, 56 +    sd     $t8, 56($sp)            # t8 holds caller's gp, now save it to the stack. +    .cfi_rel_offset 28, 56         # Value from gp is pushed, so set the cfi offset accordingly.      sd     $s7, 48($sp)      .cfi_rel_offset 23, 48      sd     $s6, 40($sp) @@ -130,7 +143,7 @@      .cfi_restore 31      ld     $s8, 64($sp)      .cfi_restore 30 -    ld     $gp, 56($sp) +    ld     $t8, 56($sp)            # Restore gp back to it's temp storage.      .cfi_restore 28      ld     $s7, 48($sp)      .cfi_restore 23 @@ -146,6 +159,7 @@      .cfi_restore 18      daddiu $sp, $sp, 80      .cfi_adjust_cfa_offset -80 +    .cpreturn  .endm  .macro RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME_AND_RETURN @@ -153,7 +167,7 @@      .cfi_restore 31      ld     $s8, 64($sp)      .cfi_restore 30 -    ld     $gp, 56($sp) +    ld     $t8, 56($sp)            # Restore gp back to it's temp storage.      .cfi_restore 28      ld     $s7, 48($sp)      .cfi_restore 23 @@ -167,6 +181,7 @@      .cfi_restore 19      ld     $s2, 8($sp)      .cfi_restore 18 +    .cpreturn      jalr   $zero, $ra      daddiu $sp, $sp, 80      .cfi_adjust_cfa_offset -80 @@ -175,12 +190,6 @@  // This assumes the top part of these stack frame types are identical.  #define REFS_AND_ARGS_MINUS_REFS_SIZE (FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE - FRAME_SIZE_REFS_ONLY_CALLEE_SAVE) -    /* -     * Macro that sets up the callee save frame to conform with -     * Runtime::CreateCalleeSaveMethod(kRefsAndArgs). Restoration assumes -     * non-moving GC. -     * callee-save: padding + $f12-$f19 + $a1-$a7 + $s2-$s7 + $gp + $ra + $s8 = 24 total + 1 words padding + Method* -     */  .macro SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_INTERNAL      daddiu  $sp, $sp, -208      .cfi_adjust_cfa_offset 208 @@ -194,8 +203,8 @@      .cfi_rel_offset 31, 200      sd     $s8, 192($sp)      .cfi_rel_offset 30, 192 -    sd     $gp, 184($sp) -    .cfi_rel_offset 28, 184 +    sd     $t8, 184($sp)           # t8 holds caller's gp, now save it to the stack. +    .cfi_rel_offset 28, 184        # Value from gp is pushed, so set the cfi offset accordingly.      sd     $s7, 176($sp)      .cfi_rel_offset 23, 176      sd     $s6, 168($sp) @@ -232,16 +241,15 @@      s.d    $f14, 32($sp)      s.d    $f13, 24($sp)           # = kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset      s.d    $f12, 16($sp)           # This isn't necessary to store. - -    # 1x8 bytes paddig + Method* -    ld      $v0, %got(_ZN3art7Runtime9instance_E)($gp) -    ld      $v0, 0($v0) -    THIS_LOAD_REQUIRES_READ_BARRIER -    lwu     $v0, RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET($v0) -    sw      $v0, 0($sp)                                # Place Method* at bottom of stack. -    sd      $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF)  # Place sp in Thread::Current()->top_quick_frame. +    # 1x8 bytes padding + Method*  .endm +    /* +     * Macro that sets up the callee save frame to conform with +     * Runtime::CreateCalleeSaveMethod(kRefsAndArgs). Restoration assumes +     * non-moving GC. +     * callee-save: padding + $f12-$f19 + $a1-$a7 + $s2-$s7 + $gp + $ra + $s8 = 24 total + 1 words padding + Method* +     */  .macro SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME      SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_INTERNAL      # load appropriate callee-save-method @@ -253,12 +261,18 @@      sd      $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF)  # Place sp in Thread::Current()->top_quick_frame.  .endm +.macro SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_A0 +    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_INTERNAL +    sw      $a0, 0($sp)                                # Place Method* at bottom of stack. +    sd      $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF)  # Place sp in Thread::Current()->top_quick_frame. +.endm +  .macro RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME      ld     $ra, 200($sp)      .cfi_restore 31      ld     $s8, 192($sp)      .cfi_restore 30 -    ld     $gp, 184($sp) +    ld     $t8, 184($sp)           # Restore gp back to it's temp storage.      .cfi_restore 28      ld     $s7, 176($sp)      .cfi_restore 23 @@ -297,6 +311,7 @@      l.d    $f13, 24($sp)      l.d    $f12, 16($sp) +    .cpreturn      daddiu $sp, $sp, 208      .cfi_adjust_cfa_offset -208  .endm @@ -307,6 +322,7 @@       * exception is Thread::Current()->exception_       */  .macro DELIVER_PENDING_EXCEPTION +    SETUP_GP      SETUP_SAVE_ALL_CALLEE_SAVE_FRAME     # save callee saves for throw      dla     $t9, artDeliverPendingExceptionFromCode      jalr    $zero, $t9                   # artDeliverPendingExceptionFromCode(Thread*) @@ -348,7 +364,7 @@       * On entry $a0 is uint32_t* gprs_ and $a1 is uint32_t* fprs_       * FIXME: just guessing about the shape of the jmpbuf.  Where will pc be?       */ -ENTRY art_quick_do_long_jump +ENTRY_NO_GP art_quick_do_long_jump      l.d     $f0, 0($a1)      l.d     $f1, 8($a1)      l.d     $f2, 16($a1) @@ -605,7 +621,7 @@ INVOKE_TRAMPOLINE art_quick_invoke_virtual_trampoline_with_access_check, artInvo       *   a4 = JValue* result       *   a5 = shorty       */ -ENTRY art_quick_invoke_stub +ENTRY_NO_GP art_quick_invoke_stub      # push a4, a5, s0(rSUSPEND), s1(rSELF), s8, ra onto the stack      daddiu $sp, $sp, -48      .cfi_adjust_cfa_offset 48 @@ -707,7 +723,7 @@ END art_quick_invoke_stub       *   a4 = JValue* result       *   a5 = shorty       */ -ENTRY art_quick_invoke_static_stub +ENTRY_NO_GP art_quick_invoke_static_stub      # push a4, a5, s0(rSUSPEND), s1(rSELF), s8, ra, onto the stack      daddiu $sp, $sp, -48 @@ -851,7 +867,8 @@ ENTRY art_quick_check_cast      sd     $a1, 8($sp)      sd     $a0, 0($sp)      jal    artIsAssignableFromCode -    nop +    .cpreturn                       # Restore gp from t8 in branch delay slot. +                                    # t8 may be clobbered in artIsAssignableFromCode.      beq    $v0, $zero, .Lthrow_class_cast_exception      ld     $ra, 24($sp)      jalr   $zero, $ra @@ -863,6 +880,7 @@ ENTRY art_quick_check_cast      ld     $a0, 0($sp)      daddiu $sp, $sp, 32      .cfi_adjust_cfa_offset -32 +    SETUP_GP      SETUP_SAVE_ALL_CALLEE_SAVE_FRAME      dla  $t9, artThrowClassCastException      jalr $zero, $t9                 # artThrowClassCastException (Class*, Class*, Thread*) @@ -908,13 +926,13 @@ ENTRY art_quick_aput_obj      daddu $t1, $t1, $t0      sb   $t0, ($t1)      jalr $zero, $ra -    nop +    .cpreturn                       # Restore gp from t8 in branch delay slot.  .Ldo_aput_null:      dsll  $a1, $a1, 2      daddu $t0, $a0, $a1      sw   $a2, MIRROR_OBJECT_ARRAY_DATA_OFFSET($t0)      jalr $zero, $ra -    nop +    .cpreturn                       # Restore gp from t8 in branch delay slot.  .Lcheck_assignability:      daddiu $sp, $sp, -64      .cfi_adjust_cfa_offset 64 @@ -927,7 +945,8 @@ ENTRY art_quick_aput_obj      move   $a1, $t1      move   $a0, $t0      jal    artIsAssignableFromCode  # (Class*, Class*) -    nop +    .cpreturn                       # Restore gp from t8 in branch delay slot. +                                    # t8 may be clobbered in artIsAssignableFromCode.      ld     $ra, 56($sp)      ld     $t9, 24($sp)      ld     $a2, 16($sp) @@ -935,6 +954,7 @@ ENTRY art_quick_aput_obj      ld     $a0, 0($sp)      daddiu $sp, $sp, 64      .cfi_adjust_cfa_offset -64 +    SETUP_GP      bne    $v0, $zero, .Ldo_aput      nop      SETUP_SAVE_ALL_CALLEE_SAVE_FRAME @@ -1312,7 +1332,7 @@ ENTRY art_quick_test_suspend      bne    $a0, $zero, 1f      daddiu rSUSPEND, $zero, SUSPEND_CHECK_INTERVAL   # reset rSUSPEND to SUSPEND_CHECK_INTERVAL      jalr   $zero, $ra -    nop +    .cpreturn                                 # Restore gp from t8 in branch delay slot.  1:      SETUP_REFS_ONLY_CALLEE_SAVE_FRAME         # save callee saves for stack crawl      jal    artTestSuspendFromCode             # (Thread*) @@ -1326,8 +1346,7 @@ END art_quick_test_suspend       */      .extern artQuickProxyInvokeHandler  ENTRY art_quick_proxy_invoke_handler -    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME -    sd      $a0, 0($sp)            # place proxy method at bottom of frame +    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_A0      move    $a2, rSELF             # pass Thread::Current      jal     artQuickProxyInvokeHandler  # (Method* proxy method, receiver, Thread*, SP)      move    $a3, $sp               # pass $sp @@ -1352,6 +1371,7 @@ ENTRY art_quick_imt_conflict_trampoline      dsll    $t0, 2                 # convert target method offset to bytes      daddu   $a0, $t0               # get address of target method      dla     $t9, art_quick_invoke_interface_trampoline +    .cpreturn      jalr    $zero, $t9      lwu     $a0, MIRROR_OBJECT_ARRAY_DATA_OFFSET($a0)  # load the target method  END art_quick_imt_conflict_trampoline @@ -1377,8 +1397,7 @@ END art_quick_resolution_trampoline      .extern artQuickGenericJniTrampoline      .extern artQuickGenericJniEndTrampoline  ENTRY art_quick_generic_jni_trampoline -    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_INTERNAL -    sd      $a0, 0($sp)            # store native ArtMethod* to bottom of stack +    SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_A0      move    $s8, $sp               # save $sp      # prepare for call to artQuickGenericJniTrampoline(Thread*, SP) @@ -1481,8 +1500,7 @@ END art_quick_instrumentation_entry      .global art_quick_instrumentation_exit  art_quick_instrumentation_exit:      .cfi_startproc -    daddiu   $t9, $ra, 4       # put current address into $t9 to rebuild $gp -    .cpload  $t9 +    SETUP_GP      move     $ra, $zero        # link register is to here, so clobber with 0 for later checks      SETUP_REFS_ONLY_CALLEE_SAVE_FRAME      move     $t0, $sp          # remember bottom of caller's frame @@ -1494,8 +1512,11 @@ art_quick_instrumentation_exit:      mov.d    $f15, $f0         # pass fpr result      move     $a2, $v0          # pass gpr result      move     $a1, $t0          # pass $sp -    jal      artInstrumentationMethodExitFromCode  # (Thread*, SP, gpr_res, fpr_res)      move     $a0, rSELF        # pass Thread::Current +    jal      artInstrumentationMethodExitFromCode  # (Thread*, SP, gpr_res, fpr_res) +    .cpreturn                  # Restore gp from t8 in branch delay slot. gp is not used anymore, +                               # and t8 may be clobbered in artInstrumentationMethodExitFromCode. +      move     $t9, $v0          # set aside returned link register      move     $ra, $v1          # set link register for deoptimization      ld       $v0, 0($sp)       # restore return values diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc index de7804f759..a7d24b8e93 100644 --- a/runtime/arch/stub_test.cc +++ b/runtime/arch/stub_test.cc @@ -261,6 +261,132 @@ class StubTest : public CommonRuntimeTest {            "d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23",            "d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31",            "memory");  // clobber. +#elif defined(__mips__) && !defined(__LP64__) +    __asm__ __volatile__ ( +        // Spill a0-a3 and t0-t7 which we say we don't clobber. May contain args. +        "addiu $sp, $sp, -64\n\t" +        "sw $a0, 0($sp)\n\t" +        "sw $a1, 4($sp)\n\t" +        "sw $a2, 8($sp)\n\t" +        "sw $a3, 12($sp)\n\t" +        "sw $t0, 16($sp)\n\t" +        "sw $t1, 20($sp)\n\t" +        "sw $t2, 24($sp)\n\t" +        "sw $t3, 28($sp)\n\t" +        "sw $t4, 32($sp)\n\t" +        "sw $t5, 36($sp)\n\t" +        "sw $t6, 40($sp)\n\t" +        "sw $t7, 44($sp)\n\t" +        // Spill gp register since it is caller save. +        "sw $gp, 52($sp)\n\t" + +        "addiu $sp, $sp, -16\n\t"  // Reserve stack space, 16B aligned. +        "sw %[referrer], 0($sp)\n\t" + +        // Push everything on the stack, so we don't rely on the order. +        "addiu $sp, $sp, -20\n\t" +        "sw %[arg0], 0($sp)\n\t" +        "sw %[arg1], 4($sp)\n\t" +        "sw %[arg2], 8($sp)\n\t" +        "sw %[code], 12($sp)\n\t" +        "sw %[self], 16($sp)\n\t" + +        // Load call params into the right registers. +        "lw $a0, 0($sp)\n\t" +        "lw $a1, 4($sp)\n\t" +        "lw $a2, 8($sp)\n\t" +        "lw $t9, 12($sp)\n\t" +        "lw $s1, 16($sp)\n\t" +        "addiu $sp, $sp, 20\n\t" + +        "jalr $t9\n\t"             // Call the stub. +        "nop\n\t" +        "addiu $sp, $sp, 16\n\t"   // Drop the quick "frame". + +        // Restore stuff not named clobbered. +        "lw $a0, 0($sp)\n\t" +        "lw $a1, 4($sp)\n\t" +        "lw $a2, 8($sp)\n\t" +        "lw $a3, 12($sp)\n\t" +        "lw $t0, 16($sp)\n\t" +        "lw $t1, 20($sp)\n\t" +        "lw $t2, 24($sp)\n\t" +        "lw $t3, 28($sp)\n\t" +        "lw $t4, 32($sp)\n\t" +        "lw $t5, 36($sp)\n\t" +        "lw $t6, 40($sp)\n\t" +        "lw $t7, 44($sp)\n\t" +        // Restore gp. +        "lw $gp, 52($sp)\n\t" +        "addiu $sp, $sp, 64\n\t"   // Free stack space, now sp as on entry. + +        "move %[result], $v0\n\t"  // Store the call result. +        : [result] "=r" (result) +        : [arg0] "r"(arg0), [arg1] "r"(arg1), [arg2] "r"(arg2), [code] "r"(code), [self] "r"(self), +          [referrer] "r"(referrer) +        : "at", "v0", "v1", "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "t8", "t9", "k0", "k1", +          "fp", "ra", +          "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12", "f13", +          "f14", "f15", "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", "f24", "f25", "f26", +          "f27", "f28", "f29", "f30", "f31", +          "memory");  // clobber. +#elif defined(__mips__) && defined(__LP64__) +    __asm__ __volatile__ ( +        // Spill a0-a7 which we say we don't clobber. May contain args. +        "daddiu $sp, $sp, -64\n\t" +        "sd $a0, 0($sp)\n\t" +        "sd $a1, 8($sp)\n\t" +        "sd $a2, 16($sp)\n\t" +        "sd $a3, 24($sp)\n\t" +        "sd $a4, 32($sp)\n\t" +        "sd $a5, 40($sp)\n\t" +        "sd $a6, 48($sp)\n\t" +        "sd $a7, 56($sp)\n\t" + +        "daddiu $sp, $sp, -16\n\t"  // Reserve stack space, 16B aligned. +        "sd %[referrer], 0($sp)\n\t" + +        // Push everything on the stack, so we don't rely on the order. +        "daddiu $sp, $sp, -40\n\t" +        "sd %[arg0], 0($sp)\n\t" +        "sd %[arg1], 8($sp)\n\t" +        "sd %[arg2], 16($sp)\n\t" +        "sd %[code], 24($sp)\n\t" +        "sd %[self], 32($sp)\n\t" + +        // Load call params into the right registers. +        "ld $a0, 0($sp)\n\t" +        "ld $a1, 8($sp)\n\t" +        "ld $a2, 16($sp)\n\t" +        "ld $t9, 24($sp)\n\t" +        "ld $s1, 32($sp)\n\t" +        "daddiu $sp, $sp, 40\n\t" + +        "jalr $t9\n\t"              // Call the stub. +        "nop\n\t" +        "daddiu $sp, $sp, 16\n\t"   // Drop the quick "frame". + +        // Restore stuff not named clobbered. +        "ld $a0, 0($sp)\n\t" +        "ld $a1, 8($sp)\n\t" +        "ld $a2, 16($sp)\n\t" +        "ld $a3, 24($sp)\n\t" +        "ld $a4, 32($sp)\n\t" +        "ld $a5, 40($sp)\n\t" +        "ld $a6, 48($sp)\n\t" +        "ld $a7, 56($sp)\n\t" +        "daddiu $sp, $sp, 64\n\t" + +        "move %[result], $v0\n\t"   // Store the call result. +        : [result] "=r" (result) +        : [arg0] "r"(arg0), [arg1] "r"(arg1), [arg2] "r"(arg2), [code] "r"(code), [self] "r"(self), +          [referrer] "r"(referrer) +        : "at", "v0", "v1", "t0", "t1", "t2", "t3", "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", +          "t8", "t9", "k0", "k1", "fp", "ra", +          "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12", "f13", +          "f14", "f15", "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", "f24", "f25", "f26", +          "f27", "f28", "f29", "f30", "f31", +          "memory");  // clobber.  #elif defined(__x86_64__) && !defined(__APPLE__) && defined(__clang__)      // Note: Uses the native convention      // TODO: Set the thread? @@ -487,6 +613,136 @@ class StubTest : public CommonRuntimeTest {            "d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23",            "d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31",            "memory");  // clobber. +#elif defined(__mips__) && !defined(__LP64__) +    __asm__ __volatile__ ( +        // Spill a0-a3 and t0-t7 which we say we don't clobber. May contain args. +        "addiu $sp, $sp, -64\n\t" +        "sw $a0, 0($sp)\n\t" +        "sw $a1, 4($sp)\n\t" +        "sw $a2, 8($sp)\n\t" +        "sw $a3, 12($sp)\n\t" +        "sw $t0, 16($sp)\n\t" +        "sw $t1, 20($sp)\n\t" +        "sw $t2, 24($sp)\n\t" +        "sw $t3, 28($sp)\n\t" +        "sw $t4, 32($sp)\n\t" +        "sw $t5, 36($sp)\n\t" +        "sw $t6, 40($sp)\n\t" +        "sw $t7, 44($sp)\n\t" +        // Spill gp register since it is caller save. +        "sw $gp, 52($sp)\n\t" + +        "addiu $sp, $sp, -16\n\t"  // Reserve stack space, 16B aligned. +        "sw %[referrer], 0($sp)\n\t" + +        // Push everything on the stack, so we don't rely on the order. +        "addiu $sp, $sp, -24\n\t" +        "sw %[arg0], 0($sp)\n\t" +        "sw %[arg1], 4($sp)\n\t" +        "sw %[arg2], 8($sp)\n\t" +        "sw %[code], 12($sp)\n\t" +        "sw %[self], 16($sp)\n\t" +        "sw %[hidden], 20($sp)\n\t" + +        // Load call params into the right registers. +        "lw $a0, 0($sp)\n\t" +        "lw $a1, 4($sp)\n\t" +        "lw $a2, 8($sp)\n\t" +        "lw $t9, 12($sp)\n\t" +        "lw $s1, 16($sp)\n\t" +        "lw $t0, 20($sp)\n\t" +        "addiu $sp, $sp, 24\n\t" + +        "jalr $t9\n\t"             // Call the stub. +        "nop\n\t" +        "addiu $sp, $sp, 16\n\t"   // Drop the quick "frame". + +        // Restore stuff not named clobbered. +        "lw $a0, 0($sp)\n\t" +        "lw $a1, 4($sp)\n\t" +        "lw $a2, 8($sp)\n\t" +        "lw $a3, 12($sp)\n\t" +        "lw $t0, 16($sp)\n\t" +        "lw $t1, 20($sp)\n\t" +        "lw $t2, 24($sp)\n\t" +        "lw $t3, 28($sp)\n\t" +        "lw $t4, 32($sp)\n\t" +        "lw $t5, 36($sp)\n\t" +        "lw $t6, 40($sp)\n\t" +        "lw $t7, 44($sp)\n\t" +        // Restore gp. +        "lw $gp, 52($sp)\n\t" +        "addiu $sp, $sp, 64\n\t"   // Free stack space, now sp as on entry. + +        "move %[result], $v0\n\t"  // Store the call result. +        : [result] "=r" (result) +        : [arg0] "r"(arg0), [arg1] "r"(arg1), [arg2] "r"(arg2), [code] "r"(code), [self] "r"(self), +          [referrer] "r"(referrer), [hidden] "r"(hidden) +        : "at", "v0", "v1", "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "t8", "t9", "k0", "k1", +          "fp", "ra", +          "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12", "f13", +          "f14", "f15", "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", "f24", "f25", "f26", +          "f27", "f28", "f29", "f30", "f31", +          "memory");  // clobber. +#elif defined(__mips__) && defined(__LP64__) +    __asm__ __volatile__ ( +        // Spill a0-a7 which we say we don't clobber. May contain args. +        "daddiu $sp, $sp, -64\n\t" +        "sd $a0, 0($sp)\n\t" +        "sd $a1, 8($sp)\n\t" +        "sd $a2, 16($sp)\n\t" +        "sd $a3, 24($sp)\n\t" +        "sd $a4, 32($sp)\n\t" +        "sd $a5, 40($sp)\n\t" +        "sd $a6, 48($sp)\n\t" +        "sd $a7, 56($sp)\n\t" + +        "daddiu $sp, $sp, -16\n\t"  // Reserve stack space, 16B aligned. +        "sd %[referrer], 0($sp)\n\t" + +        // Push everything on the stack, so we don't rely on the order. +        "daddiu $sp, $sp, -48\n\t" +        "sd %[arg0], 0($sp)\n\t" +        "sd %[arg1], 8($sp)\n\t" +        "sd %[arg2], 16($sp)\n\t" +        "sd %[code], 24($sp)\n\t" +        "sd %[self], 32($sp)\n\t" +        "sd %[hidden], 40($sp)\n\t" + +        // Load call params into the right registers. +        "ld $a0, 0($sp)\n\t" +        "ld $a1, 8($sp)\n\t" +        "ld $a2, 16($sp)\n\t" +        "ld $t9, 24($sp)\n\t" +        "ld $s1, 32($sp)\n\t" +        "ld $t0, 40($sp)\n\t" +        "daddiu $sp, $sp, 48\n\t" + +        "jalr $t9\n\t"              // Call the stub. +        "nop\n\t" +        "daddiu $sp, $sp, 16\n\t"   // Drop the quick "frame". + +        // Restore stuff not named clobbered. +        "ld $a0, 0($sp)\n\t" +        "ld $a1, 8($sp)\n\t" +        "ld $a2, 16($sp)\n\t" +        "ld $a3, 24($sp)\n\t" +        "ld $a4, 32($sp)\n\t" +        "ld $a5, 40($sp)\n\t" +        "ld $a6, 48($sp)\n\t" +        "ld $a7, 56($sp)\n\t" +        "daddiu $sp, $sp, 64\n\t" + +        "move %[result], $v0\n\t"   // Store the call result. +        : [result] "=r" (result) +        : [arg0] "r"(arg0), [arg1] "r"(arg1), [arg2] "r"(arg2), [code] "r"(code), [self] "r"(self), +          [referrer] "r"(referrer), [hidden] "r"(hidden) +        : "at", "v0", "v1", "t0", "t1", "t2", "t3", "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", +          "t8", "t9", "k0", "k1", "fp", "ra", +          "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12", "f13", +          "f14", "f15", "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", "f24", "f25", "f26", +          "f27", "f28", "f29", "f30", "f31", +          "memory");  // clobber.  #elif defined(__x86_64__) && !defined(__APPLE__) && defined(__clang__)      // Note: Uses the native convention      // TODO: Set the thread? @@ -521,7 +777,8 @@ class StubTest : public CommonRuntimeTest {    // Method with 32b arg0, 64b arg1    size_t Invoke3UWithReferrer(size_t arg0, uint64_t arg1, uintptr_t code, Thread* self,                                mirror::ArtMethod* referrer) { -#if (defined(__x86_64__) && !defined(__APPLE__)) || defined(__aarch64__) +#if (defined(__x86_64__) && !defined(__APPLE__)) || (defined(__mips__) && defined(__LP64__)) || \ +    defined(__aarch64__)      // Just pass through.      return Invoke3WithReferrer(arg0, arg1, 0U, code, self, referrer);  #else @@ -549,7 +806,7 @@ class StubTest : public CommonRuntimeTest {  TEST_F(StubTest, Memcpy) { -#if defined(__i386__) || (defined(__x86_64__) && !defined(__APPLE__)) +#if defined(__i386__) || (defined(__x86_64__) && !defined(__APPLE__)) || defined(__mips__)    Thread* self = Thread::Current();    uint32_t orig[20]; @@ -586,7 +843,8 @@ TEST_F(StubTest, Memcpy) {  }  TEST_F(StubTest, LockObject) { -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \ +    (defined(__x86_64__) && !defined(__APPLE__))    static constexpr size_t kThinLockLoops = 100;    Thread* self = Thread::Current(); @@ -659,7 +917,8 @@ class RandGen {  // NO_THREAD_SAFETY_ANALYSIS as we do not want to grab exclusive mutator lock for MonitorInfo.  static void TestUnlockObject(StubTest* test) NO_THREAD_SAFETY_ANALYSIS { -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \ +    (defined(__x86_64__) && !defined(__APPLE__))    static constexpr size_t kThinLockLoops = 100;    Thread* self = Thread::Current(); @@ -809,12 +1068,14 @@ TEST_F(StubTest, UnlockObject) {    TestUnlockObject(this);  } -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \ +    (defined(__x86_64__) && !defined(__APPLE__))  extern "C" void art_quick_check_cast(void);  #endif  TEST_F(StubTest, CheckCast) { -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \ +    (defined(__x86_64__) && !defined(__APPLE__))    Thread* self = Thread::Current();    const uintptr_t art_quick_check_cast = StubTest::GetEntrypoint(self, kQuickCheckCast); @@ -865,7 +1126,8 @@ TEST_F(StubTest, CheckCast) {  TEST_F(StubTest, APutObj) {    TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING(); -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \ +    (defined(__x86_64__) && !defined(__APPLE__))    Thread* self = Thread::Current();    // Do not check non-checked ones, we'd need handlers and stuff... @@ -998,7 +1260,8 @@ TEST_F(StubTest, APutObj) {  TEST_F(StubTest, AllocObject) {    TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING(); -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \ +    (defined(__x86_64__) && !defined(__APPLE__))    // This will lead to OOM  error messages in the log.    ScopedLogSeverity sls(LogSeverity::FATAL); @@ -1123,7 +1386,8 @@ TEST_F(StubTest, AllocObject) {  TEST_F(StubTest, AllocObjectArray) {    TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING(); -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \ +    (defined(__x86_64__) && !defined(__APPLE__))    // TODO: Check the "Unresolved" allocation stubs    // This will lead to OOM  error messages in the log. @@ -1292,7 +1556,8 @@ TEST_F(StubTest, StringCompareTo) {  static void GetSetBooleanStatic(ArtField* f, Thread* self,                                  mirror::ArtMethod* referrer, StubTest* test)      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \ +    (defined(__x86_64__) && !defined(__APPLE__))    constexpr size_t num_values = 5;    uint8_t values[num_values] = { 0, 1, 2, 128, 0xFF }; @@ -1322,7 +1587,8 @@ static void GetSetBooleanStatic(ArtField* f, Thread* self,  static void GetSetByteStatic(ArtField* f, Thread* self, mirror::ArtMethod* referrer,                               StubTest* test)      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \ +    (defined(__x86_64__) && !defined(__APPLE__))    int8_t values[] = { -128, -64, 0, 64, 127 };    for (size_t i = 0; i < arraysize(values); ++i) { @@ -1352,7 +1618,8 @@ static void GetSetByteStatic(ArtField* f, Thread* self, mirror::ArtMethod* refer  static void GetSetBooleanInstance(Handle<mirror::Object>* obj, ArtField* f, Thread* self,                                    mirror::ArtMethod* referrer, StubTest* test)      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \ +    (defined(__x86_64__) && !defined(__APPLE__))    uint8_t values[] = { 0, true, 2, 128, 0xFF };    for (size_t i = 0; i < arraysize(values); ++i) { @@ -1386,7 +1653,8 @@ static void GetSetBooleanInstance(Handle<mirror::Object>* obj, ArtField* f, Thre  static void GetSetByteInstance(Handle<mirror::Object>* obj, ArtField* f,                               Thread* self, mirror::ArtMethod* referrer, StubTest* test)      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \ +    (defined(__x86_64__) && !defined(__APPLE__))    int8_t values[] = { -128, -64, 0, 64, 127 };    for (size_t i = 0; i < arraysize(values); ++i) { @@ -1420,7 +1688,8 @@ static void GetSetByteInstance(Handle<mirror::Object>* obj, ArtField* f,  static void GetSetCharStatic(ArtField* f, Thread* self, mirror::ArtMethod* referrer,                               StubTest* test)      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \ +    (defined(__x86_64__) && !defined(__APPLE__))    uint16_t values[] = { 0, 1, 2, 255, 32768, 0xFFFF };    for (size_t i = 0; i < arraysize(values); ++i) { @@ -1449,7 +1718,8 @@ static void GetSetCharStatic(ArtField* f, Thread* self, mirror::ArtMethod* refer  static void GetSetShortStatic(ArtField* f, Thread* self,                                mirror::ArtMethod* referrer, StubTest* test)      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \ +    (defined(__x86_64__) && !defined(__APPLE__))    int16_t values[] = { -0x7FFF, -32768, 0, 255, 32767, 0x7FFE };    for (size_t i = 0; i < arraysize(values); ++i) { @@ -1479,7 +1749,8 @@ static void GetSetShortStatic(ArtField* f, Thread* self,  static void GetSetCharInstance(Handle<mirror::Object>* obj, ArtField* f,                                 Thread* self, mirror::ArtMethod* referrer, StubTest* test)      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \ +    (defined(__x86_64__) && !defined(__APPLE__))    uint16_t values[] = { 0, 1, 2, 255, 32768, 0xFFFF };    for (size_t i = 0; i < arraysize(values); ++i) { @@ -1512,7 +1783,8 @@ static void GetSetCharInstance(Handle<mirror::Object>* obj, ArtField* f,  static void GetSetShortInstance(Handle<mirror::Object>* obj, ArtField* f,                               Thread* self, mirror::ArtMethod* referrer, StubTest* test)      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \ +    (defined(__x86_64__) && !defined(__APPLE__))    int16_t values[] = { -0x7FFF, -32768, 0, 255, 32767, 0x7FFE };    for (size_t i = 0; i < arraysize(values); ++i) { @@ -1546,7 +1818,8 @@ static void GetSetShortInstance(Handle<mirror::Object>* obj, ArtField* f,  static void GetSet32Static(ArtField* f, Thread* self, mirror::ArtMethod* referrer,                             StubTest* test)      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \ +    (defined(__x86_64__) && !defined(__APPLE__))    uint32_t values[] = { 0, 1, 2, 255, 32768, 1000000, 0xFFFFFFFF };    for (size_t i = 0; i < arraysize(values); ++i) { @@ -1563,7 +1836,11 @@ static void GetSet32Static(ArtField* f, Thread* self, mirror::ArtMethod* referre                                             self,                                             referrer); +#if defined(__mips__) && defined(__LP64__) +    EXPECT_EQ(static_cast<uint32_t>(res), values[i]) << "Iteration " << i; +#else      EXPECT_EQ(res, values[i]) << "Iteration " << i; +#endif    }  #else    UNUSED(f, self, referrer, test); @@ -1577,7 +1854,8 @@ static void GetSet32Static(ArtField* f, Thread* self, mirror::ArtMethod* referre  static void GetSet32Instance(Handle<mirror::Object>* obj, ArtField* f,                               Thread* self, mirror::ArtMethod* referrer, StubTest* test)      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \ +    (defined(__x86_64__) && !defined(__APPLE__))    uint32_t values[] = { 0, 1, 2, 255, 32768, 1000000, 0xFFFFFFFF };    for (size_t i = 0; i < arraysize(values); ++i) { @@ -1611,7 +1889,8 @@ static void GetSet32Instance(Handle<mirror::Object>* obj, ArtField* f,  } -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \ +    (defined(__x86_64__) && !defined(__APPLE__))  static void set_and_check_static(uint32_t f_idx, mirror::Object* val, Thread* self,                                   mirror::ArtMethod* referrer, StubTest* test) @@ -1636,7 +1915,8 @@ static void set_and_check_static(uint32_t f_idx, mirror::Object* val, Thread* se  static void GetSetObjStatic(ArtField* f, Thread* self, mirror::ArtMethod* referrer,                              StubTest* test)      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \ +    (defined(__x86_64__) && !defined(__APPLE__))    set_and_check_static(f->GetDexFieldIndex(), nullptr, self, referrer, test);    // Allocate a string object for simplicity. @@ -1653,7 +1933,8 @@ static void GetSetObjStatic(ArtField* f, Thread* self, mirror::ArtMethod* referr  } -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \ +    (defined(__x86_64__) && !defined(__APPLE__))  static void set_and_check_instance(ArtField* f, mirror::Object* trg,                                     mirror::Object* val, Thread* self, mirror::ArtMethod* referrer,                                     StubTest* test) @@ -1681,7 +1962,8 @@ static void set_and_check_instance(ArtField* f, mirror::Object* trg,  static void GetSetObjInstance(Handle<mirror::Object>* obj, ArtField* f,                                Thread* self, mirror::ArtMethod* referrer, StubTest* test)      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \ +    (defined(__x86_64__) && !defined(__APPLE__))    set_and_check_instance(f, obj->Get(), nullptr, self, referrer, test);    // Allocate a string object for simplicity. @@ -1703,7 +1985,8 @@ static void GetSetObjInstance(Handle<mirror::Object>* obj, ArtField* f,  static void GetSet64Static(ArtField* f, Thread* self, mirror::ArtMethod* referrer,                             StubTest* test)      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { -#if (defined(__x86_64__) && !defined(__APPLE__)) || defined(__aarch64__) +#if (defined(__x86_64__) && !defined(__APPLE__)) || (defined(__mips__) && defined(__LP64__)) || \ +    defined(__aarch64__)    uint64_t values[] = { 0, 1, 2, 255, 32768, 1000000, 0xFFFFFFFF, 0xFFFFFFFFFFFF };    for (size_t i = 0; i < arraysize(values); ++i) { @@ -1733,7 +2016,8 @@ static void GetSet64Static(ArtField* f, Thread* self, mirror::ArtMethod* referre  static void GetSet64Instance(Handle<mirror::Object>* obj, ArtField* f,                               Thread* self, mirror::ArtMethod* referrer, StubTest* test)      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { -#if (defined(__x86_64__) && !defined(__APPLE__)) || defined(__aarch64__) +#if (defined(__x86_64__) && !defined(__APPLE__)) || (defined(__mips__) && defined(__LP64__)) || \ +    defined(__aarch64__)    uint64_t values[] = { 0, 1, 2, 255, 32768, 1000000, 0xFFFFFFFF, 0xFFFFFFFFFFFF };    for (size_t i = 0; i < arraysize(values); ++i) { @@ -1933,7 +2217,8 @@ TEST_F(StubTest, Fields64) {  }  TEST_F(StubTest, IMT) { -#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)) +#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \ +    (defined(__x86_64__) && !defined(__APPLE__))    TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING();    Thread* self = Thread::Current(); diff --git a/runtime/asm_support.h b/runtime/asm_support.h index a115fbe0f4..de4783a5f8 100644 --- a/runtime/asm_support.h +++ b/runtime/asm_support.h @@ -89,7 +89,7 @@ ADD_TEST_EQ(THREAD_ID_OFFSET,              art::Thread::ThinLockIdOffset<__SIZEOF_POINTER__>().Int32Value())  // Offset of field Thread::tlsPtr_.card_table. -#define THREAD_CARD_TABLE_OFFSET 120 +#define THREAD_CARD_TABLE_OFFSET 128  ADD_TEST_EQ(THREAD_CARD_TABLE_OFFSET,              art::Thread::CardTableOffset<__SIZEOF_POINTER__>().Int32Value()) diff --git a/runtime/check_reference_map_visitor.h b/runtime/check_reference_map_visitor.h index 5d9cd35c83..d87a563d73 100644 --- a/runtime/check_reference_map_visitor.h +++ b/runtime/check_reference_map_visitor.h @@ -29,7 +29,7 @@ namespace art {  class CheckReferenceMapVisitor : public StackVisitor {   public:    explicit CheckReferenceMapVisitor(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) -      : StackVisitor(thread, nullptr) {} +      : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames) {}    bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {      mirror::ArtMethod* m = GetMethod(); diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h index 991737893a..34fdd8d76a 100644 --- a/runtime/common_runtime_test.h +++ b/runtime/common_runtime_test.h @@ -182,7 +182,7 @@ class CheckJniAbortCatcher {    }  #define TEST_DISABLED_FOR_MIPS() \ -  if (kRuntimeISA == kMips || kRuntimeISA == kMips64) { \ +  if (kRuntimeISA == kMips) { \      printf("WARNING: TEST DISABLED FOR MIPS\n"); \      return; \    } diff --git a/runtime/debugger.cc b/runtime/debugger.cc index dc1b4f1dd5..811d15ad97 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -57,6 +57,9 @@  namespace art { +// The key identifying the debugger to update instrumentation. +static constexpr const char* kDbgInstrumentationKey = "Debugger"; +  static const size_t kMaxAllocRecordStackDepth = 16;  // Max 255.  static const size_t kDefaultNumAllocRecords = 64*1024;  // Must be a power of 2. 2BE can hold 64k-1. @@ -232,13 +235,29 @@ class DebugInstrumentationListener FINAL : public instrumentation::Instrumentati    virtual ~DebugInstrumentationListener() {}    void MethodEntered(Thread* thread, mirror::Object* this_object, mirror::ArtMethod* method, -                     uint32_t dex_pc ATTRIBUTE_UNUSED) +                     uint32_t dex_pc)        OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {      if (method->IsNative()) {        // TODO: post location events is a suspension point and native method entry stubs aren't.        return;      } -    Dbg::UpdateDebugger(thread, this_object, method, 0, Dbg::kMethodEntry, nullptr); +    if (IsListeningToDexPcMoved()) { +      // We also listen to kDexPcMoved instrumentation event so we know the DexPcMoved method is +      // going to be called right after us. To avoid sending JDWP events twice for this location, +      // we report the event in DexPcMoved. However, we must remind this is method entry so we +      // send the METHOD_ENTRY event. And we can also group it with other events for this location +      // like BREAKPOINT or SINGLE_STEP (or even METHOD_EXIT if this is a RETURN instruction). +      thread->SetDebugMethodEntry(); +    } else if (IsListeningToMethodExit() && IsReturn(method, dex_pc)) { +      // We also listen to kMethodExited instrumentation event and the current instruction is a +      // RETURN so we know the MethodExited method is going to be called right after us. To avoid +      // sending JDWP events twice for this location, we report the event(s) in MethodExited. +      // However, we must remind this is method entry so we send the METHOD_ENTRY event. And we can +      // also group it with other events for this location like BREAKPOINT or SINGLE_STEP. +      thread->SetDebugMethodEntry(); +    } else { +      Dbg::UpdateDebugger(thread, this_object, method, 0, Dbg::kMethodEntry, nullptr); +    }    }    void MethodExited(Thread* thread, mirror::Object* this_object, mirror::ArtMethod* method, @@ -248,14 +267,20 @@ class DebugInstrumentationListener FINAL : public instrumentation::Instrumentati        // TODO: post location events is a suspension point and native method entry stubs aren't.        return;      } -    Dbg::UpdateDebugger(thread, this_object, method, dex_pc, Dbg::kMethodExit, &return_value); +    uint32_t events = Dbg::kMethodExit; +    if (thread->IsDebugMethodEntry()) { +      // It is also the method entry. +      DCHECK(IsReturn(method, dex_pc)); +      events |= Dbg::kMethodEntry; +      thread->ClearDebugMethodEntry(); +    } +    Dbg::UpdateDebugger(thread, this_object, method, dex_pc, events, &return_value);    } -  void MethodUnwind(Thread* thread, mirror::Object* this_object, mirror::ArtMethod* method, -                    uint32_t dex_pc) +  void MethodUnwind(Thread* thread ATTRIBUTE_UNUSED, mirror::Object* this_object ATTRIBUTE_UNUSED, +                    mirror::ArtMethod* method, uint32_t dex_pc)        OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {      // We're not recorded to listen to this kind of event, so complain. -    UNUSED(thread, this_object, method, dex_pc);      LOG(ERROR) << "Unexpected method unwind event in debugger " << PrettyMethod(method)                 << " " << dex_pc;    } @@ -263,13 +288,27 @@ class DebugInstrumentationListener FINAL : public instrumentation::Instrumentati    void DexPcMoved(Thread* thread, mirror::Object* this_object, mirror::ArtMethod* method,                    uint32_t new_dex_pc)        OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { -    Dbg::UpdateDebugger(thread, this_object, method, new_dex_pc, 0, nullptr); +    if (IsListeningToMethodExit() && IsReturn(method, new_dex_pc)) { +      // We also listen to kMethodExited instrumentation event and the current instruction is a +      // RETURN so we know the MethodExited method is going to be called right after us. Like in +      // MethodEntered, we delegate event reporting to MethodExited. +      // Besides, if this RETURN instruction is the only one in the method, we can send multiple +      // JDWP events in the same packet: METHOD_ENTRY, METHOD_EXIT, BREAKPOINT and/or SINGLE_STEP. +      // Therefore, we must not clear the debug method entry flag here. +    } else { +      uint32_t events = 0; +      if (thread->IsDebugMethodEntry()) { +        // It is also the method entry. +        events = Dbg::kMethodEntry; +        thread->ClearDebugMethodEntry(); +      } +      Dbg::UpdateDebugger(thread, this_object, method, new_dex_pc, events, nullptr); +    }    } -  void FieldRead(Thread* thread, mirror::Object* this_object, mirror::ArtMethod* method, -                 uint32_t dex_pc, ArtField* field) +  void FieldRead(Thread* thread ATTRIBUTE_UNUSED, mirror::Object* this_object, +                 mirror::ArtMethod* method, uint32_t dex_pc, ArtField* field)        OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { -    UNUSED(thread);      Dbg::PostFieldAccessEvent(method, dex_pc, this_object, field);    } @@ -293,6 +332,26 @@ class DebugInstrumentationListener FINAL : public instrumentation::Instrumentati    }   private: +  static bool IsReturn(mirror::ArtMethod* method, uint32_t dex_pc) +      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +    const DexFile::CodeItem* code_item = method->GetCodeItem(); +    const Instruction* instruction = Instruction::At(&code_item->insns_[dex_pc]); +    return instruction->IsReturn(); +  } + +  static bool IsListeningToDexPcMoved() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +    return IsListeningTo(instrumentation::Instrumentation::kDexPcMoved); +  } + +  static bool IsListeningToMethodExit() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +    return IsListeningTo(instrumentation::Instrumentation::kMethodExited); +  } + +  static bool IsListeningTo(instrumentation::Instrumentation::InstrumentationEvent event) +      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +    return (Dbg::GetInstrumentationEvents() & event) != 0; +  } +    DISALLOW_COPY_AND_ASSIGN(DebugInstrumentationListener);  } gDebugInstrumentationListener; @@ -677,7 +736,7 @@ void Dbg::Disconnected() {        instrumentation_events_ = 0;      }      if (RequiresDeoptimization()) { -      runtime->GetInstrumentation()->DisableDeoptimization(); +      runtime->GetInstrumentation()->DisableDeoptimization(kDbgInstrumentationKey);      }      gDebuggerActive = false;    } @@ -831,8 +890,10 @@ JDWP::JdwpError Dbg::GetOwnedMonitors(JDWP::ObjectId thread_id,                          std::vector<JDWP::ObjectId>* monitor_vector,                          std::vector<uint32_t>* stack_depth_vector)          SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) -      : StackVisitor(thread, context), current_stack_depth(0), -        monitors(monitor_vector), stack_depths(stack_depth_vector) {} +      : StackVisitor(thread, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames), +        current_stack_depth(0), +        monitors(monitor_vector), +        stack_depths(stack_depth_vector) {}      // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses      // annotalysis. @@ -2193,7 +2254,8 @@ void Dbg::GetThreads(mirror::Object* thread_group, std::vector<JDWP::ObjectId>*  static int GetStackDepth(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {    struct CountStackDepthVisitor : public StackVisitor {      explicit CountStackDepthVisitor(Thread* thread_in) -        : StackVisitor(thread_in, nullptr), depth(0) {} +        : StackVisitor(thread_in, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), +          depth(0) {}      // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses      // annotalysis. @@ -2233,8 +2295,11 @@ JDWP::JdwpError Dbg::GetThreadFrames(JDWP::ObjectId thread_id, size_t start_fram      GetFrameVisitor(Thread* thread, size_t start_frame_in, size_t frame_count_in,                      JDWP::ExpandBuf* buf_in)          SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) -        : StackVisitor(thread, nullptr), depth_(0), -          start_frame_(start_frame_in), frame_count_(frame_count_in), buf_(buf_in) { +        : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), +          depth_(0), +          start_frame_(start_frame_in), +          frame_count_(frame_count_in), +          buf_(buf_in) {        expandBufAdd4BE(buf_, frame_count_);      } @@ -2351,7 +2416,9 @@ void Dbg::SuspendSelf() {  struct GetThisVisitor : public StackVisitor {    GetThisVisitor(Thread* thread, Context* context, JDWP::FrameId frame_id_in)        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) -      : StackVisitor(thread, context), this_object(nullptr), frame_id(frame_id_in) {} +      : StackVisitor(thread, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames), +        this_object(nullptr), +        frame_id(frame_id_in) {}    // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses    // annotalysis. @@ -2391,7 +2458,9 @@ class FindFrameVisitor FINAL : public StackVisitor {   public:    FindFrameVisitor(Thread* thread, Context* context, JDWP::FrameId frame_id)        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) -      : StackVisitor(thread, context), frame_id_(frame_id), error_(JDWP::ERR_INVALID_FRAMEID) {} +      : StackVisitor(thread, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames), +        frame_id_(frame_id), +        error_(JDWP::ERR_INVALID_FRAMEID) {}    // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses    // annotalysis. @@ -2775,7 +2844,7 @@ class CatchLocationFinder : public StackVisitor {   public:    CatchLocationFinder(Thread* self, const Handle<mirror::Throwable>& exception, Context* context)        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) -    : StackVisitor(self, context), +    : StackVisitor(self, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames),        self_(self),        exception_(exception),        handle_scope_(self), @@ -2998,12 +3067,12 @@ void Dbg::ProcessDeoptimizationRequest(const DeoptimizationRequest& request) {        break;      case DeoptimizationRequest::kFullDeoptimization:        VLOG(jdwp) << "Deoptimize the world ..."; -      instrumentation->DeoptimizeEverything(); +      instrumentation->DeoptimizeEverything(kDbgInstrumentationKey);        VLOG(jdwp) << "Deoptimize the world DONE";        break;      case DeoptimizationRequest::kFullUndeoptimization:        VLOG(jdwp) << "Undeoptimize the world ..."; -      instrumentation->UndeoptimizeEverything(); +      instrumentation->UndeoptimizeEverything(kDbgInstrumentationKey);        VLOG(jdwp) << "Undeoptimize the world DONE";        break;      case DeoptimizationRequest::kSelectiveDeoptimization: @@ -3523,8 +3592,10 @@ JDWP::JdwpError Dbg::ConfigureStep(JDWP::ObjectId thread_id, JDWP::JdwpStepSize    // is for step-out.    struct SingleStepStackVisitor : public StackVisitor {      explicit SingleStepStackVisitor(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) -        : StackVisitor(thread, nullptr), stack_depth(0), method(nullptr), line_number(-1) { -    } +        : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), +          stack_depth(0), +          method(nullptr), +          line_number(-1) {}      // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses      // annotalysis. @@ -4637,7 +4708,9 @@ void Dbg::SetAllocTrackingEnabled(bool enable) {  struct AllocRecordStackVisitor : public StackVisitor {    AllocRecordStackVisitor(Thread* thread, AllocRecord* record_in)        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) -      : StackVisitor(thread, nullptr), record(record_in), depth(0) {} +      : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), +        record(record_in), +        depth(0) {}    // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses    // annotalysis. diff --git a/runtime/debugger.h b/runtime/debugger.h index fe90eb613e..789a0a4dca 100644 --- a/runtime/debugger.h +++ b/runtime/debugger.h @@ -714,6 +714,10 @@ class Dbg {    static JDWP::JdwpState* GetJdwpState(); +  static uint32_t GetInstrumentationEvents() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +    return instrumentation_events_; +  } +   private:    static JDWP::JdwpError GetLocalValue(const StackVisitor& visitor,                                         ScopedObjectAccessUnchecked& soa, int slot, diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc index 1068e90a09..55a8411863 100644 --- a/runtime/gc/collector/mark_sweep.cc +++ b/runtime/gc/collector/mark_sweep.cc @@ -385,7 +385,7 @@ class MarkSweepMarkObjectSlowPath {        LOG(INTERNAL_FATAL) << "Attempting see if it's a bad root";        mark_sweep_->VerifyRoots();        PrintFileToLog("/proc/self/maps", LogSeverity::INTERNAL_FATAL); -      MemMap::DumpMaps(LOG(INTERNAL_FATAL)); +      MemMap::DumpMaps(LOG(INTERNAL_FATAL), true);        LOG(FATAL) << "Can't mark invalid object";      }    } diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index cbbc76ccd1..4129d75520 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -491,7 +491,7 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max      bool no_gap = MemMap::CheckNoGaps(GetImageSpace()->GetMemMap(),                                        non_moving_space_->GetMemMap());      if (!no_gap) { -      MemMap::DumpMaps(LOG(ERROR)); +      MemMap::DumpMaps(LOG(ERROR), true);        LOG(FATAL) << "There's a gap between the image space and the non-moving space";      }    } diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index e6c333d5cd..98e6200bcb 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -16,13 +16,10 @@  #include "instrumentation.h" -#include <sys/uio.h> -  #include <sstream>  #include "arch/context.h"  #include "atomic.h" -#include "base/unix_file/fd_file.h"  #include "class_linker.h"  #include "debugger.h"  #include "dex_file-inl.h" @@ -39,16 +36,18 @@  #include "mirror/object_array-inl.h"  #include "mirror/object-inl.h"  #include "nth_caller_visitor.h" -#include "os.h" -#include "scoped_thread_state_change.h"  #include "thread.h"  #include "thread_list.h"  namespace art { -  namespace instrumentation { -const bool kVerboseInstrumentation = false; +constexpr bool kVerboseInstrumentation = false; + +// Instrumentation works on non-inlined frames by updating returned PCs +// of compiled frames. +static constexpr StackVisitor::StackWalkKind kInstrumentationStackWalk = +    StackVisitor::StackWalkKind::kSkipInlinedFrames;  static bool InstallStubsClassVisitor(mirror::Class* klass, void* arg)      EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -64,7 +63,7 @@ Instrumentation::Instrumentation()        have_method_entry_listeners_(false), have_method_exit_listeners_(false),        have_method_unwind_listeners_(false), have_dex_pc_listeners_(false),        have_field_read_listeners_(false), have_field_write_listeners_(false), -      have_exception_caught_listeners_(false), +      have_exception_caught_listeners_(false), have_backward_branch_listeners_(false),        deoptimized_methods_lock_("deoptimized methods lock"),        deoptimization_enabled_(false),        interpreter_handler_table_(kMainHandlerTable), @@ -166,16 +165,16 @@ void Instrumentation::InstallStubsForMethod(mirror::ArtMethod* method) {  // existing instrumentation frames.  static void InstrumentationInstallStack(Thread* thread, void* arg)      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { -  struct InstallStackVisitor : public StackVisitor { +  struct InstallStackVisitor FINAL : public StackVisitor {      InstallStackVisitor(Thread* thread_in, Context* context, uintptr_t instrumentation_exit_pc) -        : StackVisitor(thread_in, context), +        : StackVisitor(thread_in, context, kInstrumentationStackWalk),            instrumentation_stack_(thread_in->GetInstrumentationStack()),            instrumentation_exit_pc_(instrumentation_exit_pc),            reached_existing_instrumentation_frames_(false), instrumentation_stack_depth_(0),            last_return_pc_(0) {      } -    virtual bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +    bool VisitFrame() OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {        mirror::ArtMethod* m = GetMethod();        if (m == nullptr) {          if (kVerboseInstrumentation) { @@ -306,16 +305,17 @@ static void InstrumentationInstallStack(Thread* thread, void* arg)  // Removes the instrumentation exit pc as the return PC for every quick frame.  static void InstrumentationRestoreStack(Thread* thread, void* arg)      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { -  struct RestoreStackVisitor : public StackVisitor { +  struct RestoreStackVisitor FINAL : public StackVisitor {      RestoreStackVisitor(Thread* thread_in, uintptr_t instrumentation_exit_pc,                          Instrumentation* instrumentation) -        : StackVisitor(thread_in, nullptr), thread_(thread_in), +        : StackVisitor(thread_in, nullptr, kInstrumentationStackWalk), +          thread_(thread_in),            instrumentation_exit_pc_(instrumentation_exit_pc),            instrumentation_(instrumentation),            instrumentation_stack_(thread_in->GetInstrumentationStack()),            frames_removed_(0) {} -    virtual bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +    bool VisitFrame() OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {        if (instrumentation_stack_->size() == 0) {          return false;  // Stop.        } @@ -390,25 +390,29 @@ static void InstrumentationRestoreStack(Thread* thread, void* arg)    }  } +static bool HasEvent(Instrumentation::InstrumentationEvent expected, uint32_t events) { +  return (events & expected) != 0; +} +  void Instrumentation::AddListener(InstrumentationListener* listener, uint32_t events) {    Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current()); -  if ((events & kMethodEntered) != 0) { +  if (HasEvent(kMethodEntered, events)) {      method_entry_listeners_.push_back(listener);      have_method_entry_listeners_ = true;    } -  if ((events & kMethodExited) != 0) { +  if (HasEvent(kMethodExited, events)) {      method_exit_listeners_.push_back(listener);      have_method_exit_listeners_ = true;    } -  if ((events & kMethodUnwind) != 0) { +  if (HasEvent(kMethodUnwind, events)) {      method_unwind_listeners_.push_back(listener);      have_method_unwind_listeners_ = true;    } -  if ((events & kBackwardBranch) != 0) { +  if (HasEvent(kBackwardBranch, events)) {      backward_branch_listeners_.push_back(listener);      have_backward_branch_listeners_ = true;    } -  if ((events & kDexPcMoved) != 0) { +  if (HasEvent(kDexPcMoved, events)) {      std::list<InstrumentationListener*>* modified;      if (have_dex_pc_listeners_) {        modified = new std::list<InstrumentationListener*>(*dex_pc_listeners_.get()); @@ -419,7 +423,7 @@ void Instrumentation::AddListener(InstrumentationListener* listener, uint32_t ev      dex_pc_listeners_.reset(modified);      have_dex_pc_listeners_ = true;    } -  if ((events & kFieldRead) != 0) { +  if (HasEvent(kFieldRead, events)) {      std::list<InstrumentationListener*>* modified;      if (have_field_read_listeners_) {        modified = new std::list<InstrumentationListener*>(*field_read_listeners_.get()); @@ -430,7 +434,7 @@ void Instrumentation::AddListener(InstrumentationListener* listener, uint32_t ev      field_read_listeners_.reset(modified);      have_field_read_listeners_ = true;    } -  if ((events & kFieldWritten) != 0) { +  if (HasEvent(kFieldWritten, events)) {      std::list<InstrumentationListener*>* modified;      if (have_field_write_listeners_) {        modified = new std::list<InstrumentationListener*>(*field_write_listeners_.get()); @@ -441,7 +445,7 @@ void Instrumentation::AddListener(InstrumentationListener* listener, uint32_t ev      field_write_listeners_.reset(modified);      have_field_write_listeners_ = true;    } -  if ((events & kExceptionCaught) != 0) { +  if (HasEvent(kExceptionCaught, events)) {      std::list<InstrumentationListener*>* modified;      if (have_exception_caught_listeners_) {        modified = new std::list<InstrumentationListener*>(*exception_caught_listeners_.get()); @@ -458,102 +462,104 @@ void Instrumentation::AddListener(InstrumentationListener* listener, uint32_t ev  void Instrumentation::RemoveListener(InstrumentationListener* listener, uint32_t events) {    Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current()); -  if ((events & kMethodEntered) != 0) { -    if (have_method_entry_listeners_) { -      method_entry_listeners_.remove(listener); -      have_method_entry_listeners_ = !method_entry_listeners_.empty(); -    } +  if (HasEvent(kMethodEntered, events) && have_method_entry_listeners_) { +    method_entry_listeners_.remove(listener); +    have_method_entry_listeners_ = !method_entry_listeners_.empty();    } -  if ((events & kMethodExited) != 0) { -    if (have_method_exit_listeners_) { -      method_exit_listeners_.remove(listener); -      have_method_exit_listeners_ = !method_exit_listeners_.empty(); -    } +  if (HasEvent(kMethodExited, events) && have_method_exit_listeners_) { +    method_exit_listeners_.remove(listener); +    have_method_exit_listeners_ = !method_exit_listeners_.empty();    } -  if ((events & kMethodUnwind) != 0) { -    if (have_method_unwind_listeners_) { +  if (HasEvent(kMethodUnwind, events) && have_method_unwind_listeners_) {        method_unwind_listeners_.remove(listener);        have_method_unwind_listeners_ = !method_unwind_listeners_.empty(); -    }    } -  if ((events & kDexPcMoved) != 0) { +  if (HasEvent(kBackwardBranch, events) && have_backward_branch_listeners_) { +      backward_branch_listeners_.remove(listener); +      have_backward_branch_listeners_ = !backward_branch_listeners_.empty(); +    } +  if (HasEvent(kDexPcMoved, events) && have_dex_pc_listeners_) { +    std::list<InstrumentationListener*>* modified = +        new std::list<InstrumentationListener*>(*dex_pc_listeners_.get()); +    modified->remove(listener); +    have_dex_pc_listeners_ = !modified->empty();      if (have_dex_pc_listeners_) { -      std::list<InstrumentationListener*>* modified = -          new std::list<InstrumentationListener*>(*dex_pc_listeners_.get()); -      modified->remove(listener); -      have_dex_pc_listeners_ = !modified->empty(); -      if (have_dex_pc_listeners_) { -        dex_pc_listeners_.reset(modified); -      } else { -        dex_pc_listeners_.reset(); -        delete modified; -      } +      dex_pc_listeners_.reset(modified); +    } else { +      dex_pc_listeners_.reset(); +      delete modified;      }    } -  if ((events & kFieldRead) != 0) { +  if (HasEvent(kFieldRead, events) && have_field_read_listeners_) { +    std::list<InstrumentationListener*>* modified = +        new std::list<InstrumentationListener*>(*field_read_listeners_.get()); +    modified->remove(listener); +    have_field_read_listeners_ = !modified->empty();      if (have_field_read_listeners_) { -      std::list<InstrumentationListener*>* modified = -          new std::list<InstrumentationListener*>(*field_read_listeners_.get()); -      modified->remove(listener); -      have_field_read_listeners_ = !modified->empty(); -      if (have_field_read_listeners_) { -        field_read_listeners_.reset(modified); -      } else { -        field_read_listeners_.reset(); -        delete modified; -      } +      field_read_listeners_.reset(modified); +    } else { +      field_read_listeners_.reset(); +      delete modified;      }    } -  if ((events & kFieldWritten) != 0) { +  if (HasEvent(kFieldWritten, events) && have_field_write_listeners_) { +    std::list<InstrumentationListener*>* modified = +        new std::list<InstrumentationListener*>(*field_write_listeners_.get()); +    modified->remove(listener); +    have_field_write_listeners_ = !modified->empty();      if (have_field_write_listeners_) { -      std::list<InstrumentationListener*>* modified = -          new std::list<InstrumentationListener*>(*field_write_listeners_.get()); -      modified->remove(listener); -      have_field_write_listeners_ = !modified->empty(); -      if (have_field_write_listeners_) { -        field_write_listeners_.reset(modified); -      } else { -        field_write_listeners_.reset(); -        delete modified; -      } +      field_write_listeners_.reset(modified); +    } else { +      field_write_listeners_.reset(); +      delete modified;      }    } -  if ((events & kExceptionCaught) != 0) { +  if (HasEvent(kExceptionCaught, events) && have_exception_caught_listeners_) { +    std::list<InstrumentationListener*>* modified = +        new std::list<InstrumentationListener*>(*exception_caught_listeners_.get()); +    modified->remove(listener); +    have_exception_caught_listeners_ = !modified->empty();      if (have_exception_caught_listeners_) { -      std::list<InstrumentationListener*>* modified = -          new std::list<InstrumentationListener*>(*exception_caught_listeners_.get()); -      modified->remove(listener); -      have_exception_caught_listeners_ = !modified->empty(); -      if (have_exception_caught_listeners_) { -        exception_caught_listeners_.reset(modified); -      } else { -        exception_caught_listeners_.reset(); -        delete modified; -      } +      exception_caught_listeners_.reset(modified); +    } else { +      exception_caught_listeners_.reset(); +      delete modified;      }    }    UpdateInterpreterHandlerTable();  } -void Instrumentation::ConfigureStubs(bool require_entry_exit_stubs, bool require_interpreter) { -  interpret_only_ = require_interpreter || forced_interpret_only_; -  // Compute what level of instrumentation is required and compare to current. -  int desired_level, current_level; -  if (require_interpreter) { -    desired_level = 2; -  } else if (require_entry_exit_stubs) { -    desired_level = 1; -  } else { -    desired_level = 0; -  } +Instrumentation::InstrumentationLevel Instrumentation::GetCurrentInstrumentationLevel() const {    if (interpreter_stubs_installed_) { -    current_level = 2; +    return InstrumentationLevel::kInstrumentWithInterpreter;    } else if (entry_exit_stubs_installed_) { -    current_level = 1; +    return InstrumentationLevel::kInstrumentWithInstrumentationStubs; +  } else { +    return InstrumentationLevel::kInstrumentNothing; +  } +} + +void Instrumentation::ConfigureStubs(const char* key, InstrumentationLevel desired_level) { +  // Store the instrumentation level for this key or remove it. +  if (desired_level == InstrumentationLevel::kInstrumentNothing) { +    // The client no longer needs instrumentation. +    requested_instrumentation_levels_.erase(key);    } else { -    current_level = 0; +    // The client needs instrumentation. +    requested_instrumentation_levels_.Overwrite(key, desired_level);    } -  if (desired_level == current_level) { + +  // Look for the highest required instrumentation level. +  InstrumentationLevel requested_level = InstrumentationLevel::kInstrumentNothing; +  for (const auto& v : requested_instrumentation_levels_) { +    requested_level = std::max(requested_level, v.second); +  } + +  interpret_only_ = (requested_level == InstrumentationLevel::kInstrumentWithInterpreter) || +                    forced_interpret_only_; + +  InstrumentationLevel current_level = GetCurrentInstrumentationLevel(); +  if (requested_level == current_level) {      // We're already set.      return;    } @@ -561,12 +567,14 @@ void Instrumentation::ConfigureStubs(bool require_entry_exit_stubs, bool require    Runtime* runtime = Runtime::Current();    Locks::mutator_lock_->AssertExclusiveHeld(self);    Locks::thread_list_lock_->AssertNotHeld(self); -  if (desired_level > 0) { -    if (require_interpreter) { +  if (requested_level > InstrumentationLevel::kInstrumentNothing) { +    if (requested_level == InstrumentationLevel::kInstrumentWithInterpreter) {        interpreter_stubs_installed_ = true; +      entry_exit_stubs_installed_ = true;      } else { -      CHECK(require_entry_exit_stubs); +      CHECK_EQ(requested_level, InstrumentationLevel::kInstrumentWithInstrumentationStubs);        entry_exit_stubs_installed_ = true; +      interpreter_stubs_installed_ = false;      }      runtime->GetClassLinker()->VisitClasses(InstallStubsClassVisitor, this);      instrumentation_stubs_installed_ = true; @@ -590,8 +598,7 @@ void Instrumentation::ConfigureStubs(bool require_entry_exit_stubs, bool require    }  } -static void ResetQuickAllocEntryPointsForThread(Thread* thread, void* arg) { -  UNUSED(arg); +static void ResetQuickAllocEntryPointsForThread(Thread* thread, void* arg ATTRIBUTE_UNUSED) {    thread->ResetQuickAllocEntryPointsForThread();  } @@ -804,11 +811,11 @@ void Instrumentation::EnableDeoptimization() {    deoptimization_enabled_ = true;  } -void Instrumentation::DisableDeoptimization() { +void Instrumentation::DisableDeoptimization(const char* key) {    CHECK_EQ(deoptimization_enabled_, true);    // If we deoptimized everything, undo it.    if (interpreter_stubs_installed_) { -    UndeoptimizeEverything(); +    UndeoptimizeEverything(key);    }    // Undeoptimized selected methods.    while (true) { @@ -828,25 +835,35 @@ void Instrumentation::DisableDeoptimization() {  // Indicates if instrumentation should notify method enter/exit events to the listeners.  bool Instrumentation::ShouldNotifyMethodEnterExitEvents() const { +  if (!HasMethodEntryListeners() && !HasMethodExitListeners()) { +    return false; +  }    return !deoptimization_enabled_ && !interpreter_stubs_installed_;  } -void Instrumentation::DeoptimizeEverything() { -  CHECK(!interpreter_stubs_installed_); -  ConfigureStubs(false, true); +void Instrumentation::DeoptimizeEverything(const char* key) { +  CHECK(deoptimization_enabled_); +  ConfigureStubs(key, InstrumentationLevel::kInstrumentWithInterpreter);  } -void Instrumentation::UndeoptimizeEverything() { +void Instrumentation::UndeoptimizeEverything(const char* key) {    CHECK(interpreter_stubs_installed_); -  ConfigureStubs(false, false); +  CHECK(deoptimization_enabled_); +  ConfigureStubs(key, InstrumentationLevel::kInstrumentNothing);  } -void Instrumentation::EnableMethodTracing(bool require_interpreter) { -  ConfigureStubs(!require_interpreter, require_interpreter); +void Instrumentation::EnableMethodTracing(const char* key, bool needs_interpreter) { +  InstrumentationLevel level; +  if (needs_interpreter) { +    level = InstrumentationLevel::kInstrumentWithInterpreter; +  } else { +    level = InstrumentationLevel::kInstrumentWithInstrumentationStubs; +  } +  ConfigureStubs(key, level);  } -void Instrumentation::DisableMethodTracing() { -  ConfigureStubs(false, false); +void Instrumentation::DisableMethodTracing(const char* key) { +  ConfigureStubs(key, InstrumentationLevel::kInstrumentNothing);  }  const void* Instrumentation::GetQuickCodeFor(mirror::ArtMethod* method, size_t pointer_size) const { @@ -896,7 +913,7 @@ void Instrumentation::MethodExitEventImpl(Thread* thread, mirror::Object* this_o  void Instrumentation::MethodUnwindEvent(Thread* thread, mirror::Object* this_object,                                          mirror::ArtMethod* method,                                          uint32_t dex_pc) const { -  if (have_method_unwind_listeners_) { +  if (HasMethodUnwindListeners()) {      for (InstrumentationListener* listener : method_unwind_listeners_) {        listener->MethodUnwind(thread, this_object, method, dex_pc);      } @@ -906,11 +923,9 @@ void Instrumentation::MethodUnwindEvent(Thread* thread, mirror::Object* this_obj  void Instrumentation::DexPcMovedEventImpl(Thread* thread, mirror::Object* this_object,                                            mirror::ArtMethod* method,                                            uint32_t dex_pc) const { -  if (HasDexPcListeners()) { -    std::shared_ptr<std::list<InstrumentationListener*>> original(dex_pc_listeners_); -    for (InstrumentationListener* listener : *original.get()) { -      listener->DexPcMoved(thread, this_object, method, dex_pc); -    } +  std::shared_ptr<std::list<InstrumentationListener*>> original(dex_pc_listeners_); +  for (InstrumentationListener* listener : *original.get()) { +    listener->DexPcMoved(thread, this_object, method, dex_pc);    }  } @@ -924,22 +939,18 @@ void Instrumentation::BackwardBranchImpl(Thread* thread, mirror::ArtMethod* meth  void Instrumentation::FieldReadEventImpl(Thread* thread, mirror::Object* this_object,                                           mirror::ArtMethod* method, uint32_t dex_pc,                                           ArtField* field) const { -  if (HasFieldReadListeners()) { -    std::shared_ptr<std::list<InstrumentationListener*>> original(field_read_listeners_); -    for (InstrumentationListener* listener : *original.get()) { -      listener->FieldRead(thread, this_object, method, dex_pc, field); -    } +  std::shared_ptr<std::list<InstrumentationListener*>> original(field_read_listeners_); +  for (InstrumentationListener* listener : *original.get()) { +    listener->FieldRead(thread, this_object, method, dex_pc, field);    }  }  void Instrumentation::FieldWriteEventImpl(Thread* thread, mirror::Object* this_object,                                           mirror::ArtMethod* method, uint32_t dex_pc,                                           ArtField* field, const JValue& field_value) const { -  if (HasFieldWriteListeners()) { -    std::shared_ptr<std::list<InstrumentationListener*>> original(field_write_listeners_); -    for (InstrumentationListener* listener : *original.get()) { -      listener->FieldWritten(thread, this_object, method, dex_pc, field, field_value); -    } +  std::shared_ptr<std::list<InstrumentationListener*>> original(field_write_listeners_); +  for (InstrumentationListener* listener : *original.get()) { +    listener->FieldWritten(thread, this_object, method, dex_pc, field, field_value);    }  } @@ -959,7 +970,7 @@ void Instrumentation::ExceptionCaughtEvent(Thread* thread,  static void CheckStackDepth(Thread* self, const InstrumentationStackFrame& instrumentation_frame,                              int delta)      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { -  size_t frame_id = StackVisitor::ComputeNumFrames(self) + delta; +  size_t frame_id = StackVisitor::ComputeNumFrames(self, kInstrumentationStackWalk) + delta;    if (frame_id != instrumentation_frame.frame_id_) {      LOG(ERROR) << "Expected frame_id=" << frame_id << " but found "          << instrumentation_frame.frame_id_; @@ -972,7 +983,7 @@ void Instrumentation::PushInstrumentationStackFrame(Thread* self, mirror::Object                                                      mirror::ArtMethod* method,                                                      uintptr_t lr, bool interpreter_entry) {    // We have a callee-save frame meaning this value is guaranteed to never be 0. -  size_t frame_id = StackVisitor::ComputeNumFrames(self); +  size_t frame_id = StackVisitor::ComputeNumFrames(self, kInstrumentationStackWalk);    std::deque<instrumentation::InstrumentationStackFrame>* stack = self->GetInstrumentationStack();    if (kVerboseInstrumentation) {      LOG(INFO) << "Entering " << PrettyMethod(method) << " from PC " << reinterpret_cast<void*>(lr); diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h index 8b7fcca48e..7d70d211bb 100644 --- a/runtime/instrumentation.h +++ b/runtime/instrumentation.h @@ -22,11 +22,10 @@  #include <map>  #include "arch/instruction_set.h" -#include "atomic.h"  #include "base/macros.h"  #include "base/mutex.h"  #include "gc_root.h" -#include "object_callbacks.h" +#include "safe_map.h"  namespace art {  namespace mirror { @@ -67,8 +66,6 @@ struct InstrumentationListener {                               uint32_t dex_pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0;    // Call-back for when a method is exited. -  // TODO: its likely passing the return value would be useful, however, we may need to get and -  //       parse the shorty to determine what kind of register holds the result.    virtual void MethodExited(Thread* thread, mirror::Object* this_object,                              mirror::ArtMethod* method, uint32_t dex_pc,                              const JValue& return_value) @@ -119,6 +116,12 @@ class Instrumentation {      kBackwardBranch = 0x80,    }; +  enum class InstrumentationLevel { +    kInstrumentNothing,                   // execute without instrumentation +    kInstrumentWithInstrumentationStubs,  // execute with instrumentation entry/exit stubs +    kInstrumentWithInterpreter            // execute with interpreter +  }; +    Instrumentation();    // Add a listener to be notified of the masked together sent of instrumentation events. This @@ -138,7 +141,7 @@ class Instrumentation {    void EnableDeoptimization()        EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)        LOCKS_EXCLUDED(deoptimized_methods_lock_); -  void DisableDeoptimization() +  void DisableDeoptimization(const char* key)        EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)        LOCKS_EXCLUDED(deoptimized_methods_lock_);    bool AreAllMethodsDeoptimized() const { @@ -147,12 +150,12 @@ class Instrumentation {    bool ShouldNotifyMethodEnterExitEvents() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);    // Executes everything with interpreter. -  void DeoptimizeEverything() +  void DeoptimizeEverything(const char* key)        EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)        LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::classlinker_classes_lock_);    // Executes everything with compiled code (or interpreter if there is no code). -  void UndeoptimizeEverything() +  void UndeoptimizeEverything(const char* key)        EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)        LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::classlinker_classes_lock_); @@ -170,18 +173,19 @@ class Instrumentation {        LOCKS_EXCLUDED(Locks::thread_list_lock_, deoptimized_methods_lock_)        EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_); +  // Indicates whether the method has been deoptimized so it is executed with the interpreter.    bool IsDeoptimized(mirror::ArtMethod* method)        LOCKS_EXCLUDED(deoptimized_methods_lock_)        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); -  // Enable method tracing by installing instrumentation entry/exit stubs. -  void EnableMethodTracing( -      bool require_interpreter = kDeoptimizeForAccurateMethodEntryExitListeners) +  // Enable method tracing by installing instrumentation entry/exit stubs or interpreter. +  void EnableMethodTracing(const char* key, +                           bool needs_interpreter = kDeoptimizeForAccurateMethodEntryExitListeners)        EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)        LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::classlinker_classes_lock_); -  // Disable method tracing by uninstalling instrumentation entry/exit stubs. -  void DisableMethodTracing() +  // Disable method tracing by uninstalling instrumentation entry/exit stubs or interpreter. +  void DisableMethodTracing(const char* key)        EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)        LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::classlinker_classes_lock_); @@ -236,6 +240,10 @@ class Instrumentation {      return have_method_exit_listeners_;    } +  bool HasMethodUnwindListeners() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +    return have_method_unwind_listeners_; +  } +    bool HasDexPcListeners() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {      return have_dex_pc_listeners_;    } @@ -355,8 +363,14 @@ class Instrumentation {        LOCKS_EXCLUDED(deoptimized_methods_lock_);   private: +  InstrumentationLevel GetCurrentInstrumentationLevel() const; +    // Does the job of installing or removing instrumentation code within methods. -  void ConfigureStubs(bool require_entry_exit_stubs, bool require_interpreter) +  // In order to support multiple clients using instrumentation at the same time, +  // the caller must pass a unique key (a string) identifying it so we remind which +  // instrumentation level it needs. Therefore the current instrumentation level +  // becomes the highest instrumentation level required by a client. +  void ConfigureStubs(const char* key, InstrumentationLevel desired_instrumentation_level)        EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)        LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::classlinker_classes_lock_,                       deoptimized_methods_lock_); @@ -452,6 +466,11 @@ class Instrumentation {    // Do we have any backward branch listeners? Short-cut to avoid taking the instrumentation_lock_.    bool have_backward_branch_listeners_ GUARDED_BY(Locks::mutator_lock_); +  // Contains the instrumentation level required by each client of the instrumentation identified +  // by a string key. +  typedef SafeMap<const char*, InstrumentationLevel> InstrumentationLevelTable; +  InstrumentationLevelTable requested_instrumentation_levels_ GUARDED_BY(Locks::mutator_lock_); +    // The event listeners, written to with the mutator_lock_ exclusively held.    std::list<InstrumentationListener*> method_entry_listeners_ GUARDED_BY(Locks::mutator_lock_);    std::list<InstrumentationListener*> method_exit_listeners_ GUARDED_BY(Locks::mutator_lock_); @@ -481,9 +500,12 @@ class Instrumentation {    size_t quick_alloc_entry_points_instrumentation_counter_        GUARDED_BY(Locks::instrument_entrypoints_lock_); +  friend class InstrumentationTest;  // For GetCurrentInstrumentationLevel and ConfigureStubs. +    DISALLOW_COPY_AND_ASSIGN(Instrumentation);  };  std::ostream& operator<<(std::ostream& os, const Instrumentation::InstrumentationEvent& rhs); +std::ostream& operator<<(std::ostream& os, const Instrumentation::InstrumentationLevel& rhs);  // An element in the instrumentation side stack maintained in art::Thread.  struct InstrumentationStackFrame { diff --git a/runtime/instrumentation_test.cc b/runtime/instrumentation_test.cc new file mode 100644 index 0000000000..5afacb8feb --- /dev/null +++ b/runtime/instrumentation_test.cc @@ -0,0 +1,791 @@ +/* + * 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 "instrumentation.h" + +#include "common_runtime_test.h" +#include "common_throws.h" +#include "class_linker-inl.h" +#include "dex_file.h" +#include "handle_scope-inl.h" +#include "jvalue.h" +#include "runtime.h" +#include "scoped_thread_state_change.h" +#include "thread_list.h" +#include "thread-inl.h" + +namespace art { +namespace instrumentation { + +class TestInstrumentationListener FINAL : public instrumentation::InstrumentationListener { + public: +  TestInstrumentationListener() +    : received_method_enter_event(false), received_method_exit_event(false), +      received_method_unwind_event(false), received_dex_pc_moved_event(false), +      received_field_read_event(false), received_field_written_event(false), +      received_exception_caught_event(false), received_backward_branch_event(false) {} + +  virtual ~TestInstrumentationListener() {} + +  void MethodEntered(Thread* thread ATTRIBUTE_UNUSED, +                     mirror::Object* this_object ATTRIBUTE_UNUSED, +                     mirror::ArtMethod* method ATTRIBUTE_UNUSED, +                     uint32_t dex_pc ATTRIBUTE_UNUSED) +      OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +    received_method_enter_event = true; +  } + +  void MethodExited(Thread* thread ATTRIBUTE_UNUSED, +                    mirror::Object* this_object ATTRIBUTE_UNUSED, +                    mirror::ArtMethod* method ATTRIBUTE_UNUSED, +                    uint32_t dex_pc ATTRIBUTE_UNUSED, +                    const JValue& return_value ATTRIBUTE_UNUSED) +      OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +    received_method_exit_event = true; +  } + +  void MethodUnwind(Thread* thread ATTRIBUTE_UNUSED, +                    mirror::Object* this_object ATTRIBUTE_UNUSED, +                    mirror::ArtMethod* method ATTRIBUTE_UNUSED, +                    uint32_t dex_pc ATTRIBUTE_UNUSED) +      OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +    received_method_unwind_event = true; +  } + +  void DexPcMoved(Thread* thread ATTRIBUTE_UNUSED, +                  mirror::Object* this_object ATTRIBUTE_UNUSED, +                  mirror::ArtMethod* method ATTRIBUTE_UNUSED, +                  uint32_t new_dex_pc ATTRIBUTE_UNUSED) +      OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +    received_dex_pc_moved_event = true; +  } + +  void FieldRead(Thread* thread ATTRIBUTE_UNUSED, +                 mirror::Object* this_object ATTRIBUTE_UNUSED, +                 mirror::ArtMethod* method ATTRIBUTE_UNUSED, +                 uint32_t dex_pc ATTRIBUTE_UNUSED, +                 ArtField* field ATTRIBUTE_UNUSED) +      OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +    received_field_read_event = true; +  } + +  void FieldWritten(Thread* thread ATTRIBUTE_UNUSED, +                    mirror::Object* this_object ATTRIBUTE_UNUSED, +                    mirror::ArtMethod* method ATTRIBUTE_UNUSED, +                    uint32_t dex_pc ATTRIBUTE_UNUSED, +                    ArtField* field ATTRIBUTE_UNUSED, +                    const JValue& field_value ATTRIBUTE_UNUSED) +      OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +    received_field_written_event = true; +  } + +  void ExceptionCaught(Thread* thread ATTRIBUTE_UNUSED, +                       mirror::Throwable* exception_object ATTRIBUTE_UNUSED) +      OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +    received_exception_caught_event = true; +  } + +  void BackwardBranch(Thread* thread ATTRIBUTE_UNUSED, +                      mirror::ArtMethod* method ATTRIBUTE_UNUSED, +                      int32_t dex_pc_offset ATTRIBUTE_UNUSED) +      OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +    received_backward_branch_event = true; +  } + +  void Reset() { +    received_method_enter_event = false; +    received_method_exit_event = false; +    received_method_unwind_event = false; +    received_dex_pc_moved_event = false; +    received_field_read_event = false; +    received_field_written_event = false; +    received_exception_caught_event = false; +    received_backward_branch_event = false; +  } + +  bool received_method_enter_event; +  bool received_method_exit_event; +  bool received_method_unwind_event; +  bool received_dex_pc_moved_event; +  bool received_field_read_event; +  bool received_field_written_event; +  bool received_exception_caught_event; +  bool received_backward_branch_event; + + private: +  DISALLOW_COPY_AND_ASSIGN(TestInstrumentationListener); +}; + +class InstrumentationTest : public CommonRuntimeTest { + public: +  // Unique keys used to test Instrumentation::ConfigureStubs. +  static constexpr const char* kClientOneKey = "TestClient1"; +  static constexpr const char* kClientTwoKey = "TestClient2"; + +  void CheckConfigureStubs(const char* key, Instrumentation::InstrumentationLevel level) { +    ScopedObjectAccess soa(Thread::Current()); +    instrumentation::Instrumentation* instr = Runtime::Current()->GetInstrumentation(); +    { +      soa.Self()->TransitionFromRunnableToSuspended(kSuspended); +      Runtime* runtime = Runtime::Current(); +      runtime->GetThreadList()->SuspendAll("Instrumentation::ConfigureStubs"); +      instr->ConfigureStubs(key, level); +      runtime->GetThreadList()->ResumeAll(); +      soa.Self()->TransitionFromSuspendedToRunnable(); +    } +  } + +  Instrumentation::InstrumentationLevel GetCurrentInstrumentationLevel() { +    return Runtime::Current()->GetInstrumentation()->GetCurrentInstrumentationLevel(); +  } + +  size_t GetInstrumentationUserCount() { +    ScopedObjectAccess soa(Thread::Current()); +    return Runtime::Current()->GetInstrumentation()->requested_instrumentation_levels_.size(); +  } + +  void TestEvent(uint32_t instrumentation_event) { +    ScopedObjectAccess soa(Thread::Current()); +    instrumentation::Instrumentation* instr = Runtime::Current()->GetInstrumentation(); +    TestInstrumentationListener listener; +    { +      soa.Self()->TransitionFromRunnableToSuspended(kSuspended); +      Runtime* runtime = Runtime::Current(); +      runtime->GetThreadList()->SuspendAll("Add instrumentation listener"); +      instr->AddListener(&listener, instrumentation_event); +      runtime->GetThreadList()->ResumeAll(); +      soa.Self()->TransitionFromSuspendedToRunnable(); +    } + +    mirror::ArtMethod* const event_method = nullptr; +    mirror::Object* const event_obj = nullptr; +    const uint32_t event_dex_pc = 0; + +    // Check the listener is registered and is notified of the event. +    EXPECT_TRUE(HasEventListener(instr, instrumentation_event)); +    EXPECT_FALSE(DidListenerReceiveEvent(listener, instrumentation_event)); +    ReportEvent(instr, instrumentation_event, soa.Self(), event_method, event_obj, event_dex_pc); +    EXPECT_TRUE(DidListenerReceiveEvent(listener, instrumentation_event)); + +    listener.Reset(); +    { +      soa.Self()->TransitionFromRunnableToSuspended(kSuspended); +      Runtime* runtime = Runtime::Current(); +      runtime->GetThreadList()->SuspendAll("Remove instrumentation listener"); +      instr->RemoveListener(&listener, instrumentation_event); +      runtime->GetThreadList()->ResumeAll(); +      soa.Self()->TransitionFromSuspendedToRunnable(); +    } + +    // Check the listener is not registered and is not notified of the event. +    EXPECT_FALSE(HasEventListener(instr, instrumentation_event)); +    EXPECT_FALSE(DidListenerReceiveEvent(listener, instrumentation_event)); +    ReportEvent(instr, instrumentation_event, soa.Self(), event_method, event_obj, event_dex_pc); +    EXPECT_FALSE(DidListenerReceiveEvent(listener, instrumentation_event)); +  } + +  void DeoptimizeMethod(Thread* self, Handle<mirror::ArtMethod> method, +                        bool enable_deoptimization) +      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +    Runtime* runtime = Runtime::Current(); +    instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation(); +    self->TransitionFromRunnableToSuspended(kSuspended); +    runtime->GetThreadList()->SuspendAll("Single method deoptimization"); +    if (enable_deoptimization) { +      instrumentation->EnableDeoptimization(); +    } +    instrumentation->Deoptimize(method.Get()); +    runtime->GetThreadList()->ResumeAll(); +    self->TransitionFromSuspendedToRunnable(); +  } + +  void UndeoptimizeMethod(Thread* self, Handle<mirror::ArtMethod> method, +                          const char* key, bool disable_deoptimization) +      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +    Runtime* runtime = Runtime::Current(); +    instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation(); +    self->TransitionFromRunnableToSuspended(kSuspended); +    runtime->GetThreadList()->SuspendAll("Single method undeoptimization"); +    instrumentation->Undeoptimize(method.Get()); +    if (disable_deoptimization) { +      instrumentation->DisableDeoptimization(key); +    } +    runtime->GetThreadList()->ResumeAll(); +    self->TransitionFromSuspendedToRunnable(); +  } + +  void DeoptimizeEverything(Thread* self, const char* key, bool enable_deoptimization) +        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +    Runtime* runtime = Runtime::Current(); +    instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation(); +    self->TransitionFromRunnableToSuspended(kSuspended); +    runtime->GetThreadList()->SuspendAll("Full deoptimization"); +    if (enable_deoptimization) { +      instrumentation->EnableDeoptimization(); +    } +    instrumentation->DeoptimizeEverything(key); +    runtime->GetThreadList()->ResumeAll(); +    self->TransitionFromSuspendedToRunnable(); +  } + +  void UndeoptimizeEverything(Thread* self, const char* key, bool disable_deoptimization) +        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +    Runtime* runtime = Runtime::Current(); +    instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation(); +    self->TransitionFromRunnableToSuspended(kSuspended); +    runtime->GetThreadList()->SuspendAll("Full undeoptimization"); +    instrumentation->UndeoptimizeEverything(key); +    if (disable_deoptimization) { +      instrumentation->DisableDeoptimization(key); +    } +    runtime->GetThreadList()->ResumeAll(); +    self->TransitionFromSuspendedToRunnable(); +  } + +  void EnableMethodTracing(Thread* self, const char* key, bool needs_interpreter) +        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +    Runtime* runtime = Runtime::Current(); +    instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation(); +    self->TransitionFromRunnableToSuspended(kSuspended); +    runtime->GetThreadList()->SuspendAll("EnableMethodTracing"); +    instrumentation->EnableMethodTracing(key, needs_interpreter); +    runtime->GetThreadList()->ResumeAll(); +    self->TransitionFromSuspendedToRunnable(); +  } + +  void DisableMethodTracing(Thread* self, const char* key) +        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +    Runtime* runtime = Runtime::Current(); +    instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation(); +    self->TransitionFromRunnableToSuspended(kSuspended); +    runtime->GetThreadList()->SuspendAll("EnableMethodTracing"); +    instrumentation->DisableMethodTracing(key); +    runtime->GetThreadList()->ResumeAll(); +    self->TransitionFromSuspendedToRunnable(); +  } + + private: +  static bool HasEventListener(const instrumentation::Instrumentation* instr, uint32_t event_type) +      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +    switch (event_type) { +      case instrumentation::Instrumentation::kMethodEntered: +        return instr->HasMethodEntryListeners(); +      case instrumentation::Instrumentation::kMethodExited: +        return instr->HasMethodExitListeners(); +      case instrumentation::Instrumentation::kMethodUnwind: +        return instr->HasMethodUnwindListeners(); +      case instrumentation::Instrumentation::kDexPcMoved: +        return instr->HasDexPcListeners(); +      case instrumentation::Instrumentation::kFieldRead: +        return instr->HasFieldReadListeners(); +      case instrumentation::Instrumentation::kFieldWritten: +        return instr->HasFieldWriteListeners(); +      case instrumentation::Instrumentation::kExceptionCaught: +        return instr->HasExceptionCaughtListeners(); +      case instrumentation::Instrumentation::kBackwardBranch: +        return instr->HasBackwardBranchListeners(); +      default: +        LOG(FATAL) << "Unknown instrumentation event " << event_type; +        UNREACHABLE(); +    } +  } + +  static void ReportEvent(const instrumentation::Instrumentation* instr, uint32_t event_type, +                          Thread* self, mirror::ArtMethod* method, mirror::Object* obj, +                          uint32_t dex_pc) +      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +    switch (event_type) { +      case instrumentation::Instrumentation::kMethodEntered: +        instr->MethodEnterEvent(self, obj, method, dex_pc); +        break; +      case instrumentation::Instrumentation::kMethodExited: { +        JValue value; +        instr->MethodExitEvent(self, obj, method, dex_pc, value); +        break; +      } +      case instrumentation::Instrumentation::kMethodUnwind: +        instr->MethodUnwindEvent(self, obj, method, dex_pc); +        break; +      case instrumentation::Instrumentation::kDexPcMoved: +        instr->DexPcMovedEvent(self, obj, method, dex_pc); +        break; +      case instrumentation::Instrumentation::kFieldRead: +        instr->FieldReadEvent(self, obj, method, dex_pc, nullptr); +        break; +      case instrumentation::Instrumentation::kFieldWritten: { +        JValue value; +        instr->FieldWriteEvent(self, obj, method, dex_pc, nullptr, value); +        break; +      } +      case instrumentation::Instrumentation::kExceptionCaught: { +        ThrowArithmeticExceptionDivideByZero(); +        mirror::Throwable* event_exception = self->GetException(); +        instr->ExceptionCaughtEvent(self, event_exception); +        self->ClearException(); +        break; +      } +      case instrumentation::Instrumentation::kBackwardBranch: +        instr->BackwardBranch(self, method, dex_pc); +        break; +      default: +        LOG(FATAL) << "Unknown instrumentation event " << event_type; +        UNREACHABLE(); +    } +  } + +  static bool DidListenerReceiveEvent(const TestInstrumentationListener& listener, +                                      uint32_t event_type) { +    switch (event_type) { +      case instrumentation::Instrumentation::kMethodEntered: +        return listener.received_method_enter_event; +      case instrumentation::Instrumentation::kMethodExited: +        return listener.received_method_exit_event; +      case instrumentation::Instrumentation::kMethodUnwind: +        return listener.received_method_unwind_event; +      case instrumentation::Instrumentation::kDexPcMoved: +        return listener.received_dex_pc_moved_event; +      case instrumentation::Instrumentation::kFieldRead: +        return listener.received_field_read_event; +      case instrumentation::Instrumentation::kFieldWritten: +        return listener.received_field_written_event; +      case instrumentation::Instrumentation::kExceptionCaught: +        return listener.received_exception_caught_event; +      case instrumentation::Instrumentation::kBackwardBranch: +        return listener.received_backward_branch_event; +      default: +        LOG(FATAL) << "Unknown instrumentation event " << event_type; +        UNREACHABLE(); +    } +  } +}; + +TEST_F(InstrumentationTest, NoInstrumentation) { +  ScopedObjectAccess soa(Thread::Current()); +  instrumentation::Instrumentation* instr = Runtime::Current()->GetInstrumentation(); +  ASSERT_NE(instr, nullptr); + +  EXPECT_FALSE(instr->AreExitStubsInstalled()); +  EXPECT_FALSE(instr->AreAllMethodsDeoptimized()); +  EXPECT_FALSE(instr->IsActive()); +  EXPECT_FALSE(instr->ShouldNotifyMethodEnterExitEvents()); + +  // Test interpreter table is the default one. +  EXPECT_EQ(instrumentation::kMainHandlerTable, instr->GetInterpreterHandlerTable()); + +  // Check there is no registered listener. +  EXPECT_FALSE(instr->HasDexPcListeners()); +  EXPECT_FALSE(instr->HasExceptionCaughtListeners()); +  EXPECT_FALSE(instr->HasFieldReadListeners()); +  EXPECT_FALSE(instr->HasFieldWriteListeners()); +  EXPECT_FALSE(instr->HasMethodEntryListeners()); +  EXPECT_FALSE(instr->HasMethodExitListeners()); +  EXPECT_FALSE(instr->IsActive()); +} + +// Test instrumentation listeners for each event. +TEST_F(InstrumentationTest, MethodEntryEvent) { +  TestEvent(instrumentation::Instrumentation::kMethodEntered); +} + +TEST_F(InstrumentationTest, MethodExitEvent) { +  TestEvent(instrumentation::Instrumentation::kMethodExited); +} + +TEST_F(InstrumentationTest, MethodUnwindEvent) { +  TestEvent(instrumentation::Instrumentation::kMethodUnwind); +} + +TEST_F(InstrumentationTest, DexPcMovedEvent) { +  TestEvent(instrumentation::Instrumentation::kDexPcMoved); +} + +TEST_F(InstrumentationTest, FieldReadEvent) { +  TestEvent(instrumentation::Instrumentation::kFieldRead); +} + +TEST_F(InstrumentationTest, FieldWriteEvent) { +  TestEvent(instrumentation::Instrumentation::kFieldWritten); +} + +TEST_F(InstrumentationTest, ExceptionCaughtEvent) { +  TestEvent(instrumentation::Instrumentation::kExceptionCaught); +} + +TEST_F(InstrumentationTest, BackwardBranchEvent) { +  TestEvent(instrumentation::Instrumentation::kBackwardBranch); +} + +TEST_F(InstrumentationTest, DeoptimizeDirectMethod) { +  ScopedObjectAccess soa(Thread::Current()); +  jobject class_loader = LoadDex("Instrumentation"); +  Runtime* const runtime = Runtime::Current(); +  instrumentation::Instrumentation* instr = runtime->GetInstrumentation(); +  ClassLinker* class_linker = runtime->GetClassLinker(); +  StackHandleScope<2> hs(soa.Self()); +  Handle<mirror::ClassLoader> loader(hs.NewHandle(soa.Decode<mirror::ClassLoader*>(class_loader))); +  mirror::Class* klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader); +  ASSERT_TRUE(klass != nullptr); +  Handle<mirror::ArtMethod> method_to_deoptimize( +      hs.NewHandle(klass->FindDeclaredDirectMethod("instanceMethod", "()V"))); +  ASSERT_TRUE(method_to_deoptimize.Get() != nullptr); + +  EXPECT_FALSE(instr->AreAllMethodsDeoptimized()); +  EXPECT_FALSE(instr->IsDeoptimized(method_to_deoptimize.Get())); + +  DeoptimizeMethod(soa.Self(), method_to_deoptimize, true); + +  EXPECT_FALSE(instr->AreAllMethodsDeoptimized()); +  EXPECT_TRUE(instr->AreExitStubsInstalled()); +  EXPECT_TRUE(instr->IsDeoptimized(method_to_deoptimize.Get())); + +  constexpr const char* instrumentation_key = "DeoptimizeDirectMethod"; +  UndeoptimizeMethod(soa.Self(), method_to_deoptimize, instrumentation_key, true); + +  EXPECT_FALSE(instr->AreAllMethodsDeoptimized()); +  EXPECT_FALSE(instr->IsDeoptimized(method_to_deoptimize.Get())); +} + +TEST_F(InstrumentationTest, FullDeoptimization) { +  ScopedObjectAccess soa(Thread::Current()); +  Runtime* const runtime = Runtime::Current(); +  instrumentation::Instrumentation* instr = runtime->GetInstrumentation(); +  EXPECT_FALSE(instr->AreAllMethodsDeoptimized()); + +  constexpr const char* instrumentation_key = "FullDeoptimization"; +  DeoptimizeEverything(soa.Self(), instrumentation_key, true); + +  EXPECT_TRUE(instr->AreAllMethodsDeoptimized()); +  EXPECT_TRUE(instr->AreExitStubsInstalled()); + +  UndeoptimizeEverything(soa.Self(), instrumentation_key, true); + +  EXPECT_FALSE(instr->AreAllMethodsDeoptimized()); +} + +TEST_F(InstrumentationTest, MixedDeoptimization) { +  ScopedObjectAccess soa(Thread::Current()); +  jobject class_loader = LoadDex("Instrumentation"); +  Runtime* const runtime = Runtime::Current(); +  instrumentation::Instrumentation* instr = runtime->GetInstrumentation(); +  ClassLinker* class_linker = runtime->GetClassLinker(); +  StackHandleScope<2> hs(soa.Self()); +  Handle<mirror::ClassLoader> loader(hs.NewHandle(soa.Decode<mirror::ClassLoader*>(class_loader))); +  mirror::Class* klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader); +  ASSERT_TRUE(klass != nullptr); +  Handle<mirror::ArtMethod> method_to_deoptimize( +      hs.NewHandle(klass->FindDeclaredDirectMethod("instanceMethod", "()V"))); +  ASSERT_TRUE(method_to_deoptimize.Get() != nullptr); + +  EXPECT_FALSE(instr->AreAllMethodsDeoptimized()); +  EXPECT_FALSE(instr->IsDeoptimized(method_to_deoptimize.Get())); + +  DeoptimizeMethod(soa.Self(), method_to_deoptimize, true); +  // Deoptimizing a method does not change instrumentation level. +  EXPECT_EQ(Instrumentation::InstrumentationLevel::kInstrumentNothing, +            GetCurrentInstrumentationLevel()); +  EXPECT_FALSE(instr->AreAllMethodsDeoptimized()); +  EXPECT_TRUE(instr->AreExitStubsInstalled()); +  EXPECT_TRUE(instr->IsDeoptimized(method_to_deoptimize.Get())); + +  constexpr const char* instrumentation_key = "MixedDeoptimization"; +  DeoptimizeEverything(soa.Self(), instrumentation_key, false); +  EXPECT_EQ(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter, +            GetCurrentInstrumentationLevel()); +  EXPECT_TRUE(instr->AreAllMethodsDeoptimized()); +  EXPECT_TRUE(instr->AreExitStubsInstalled()); +  EXPECT_TRUE(instr->IsDeoptimized(method_to_deoptimize.Get())); + +  UndeoptimizeEverything(soa.Self(), instrumentation_key, false); +  EXPECT_EQ(Instrumentation::InstrumentationLevel::kInstrumentNothing, +            GetCurrentInstrumentationLevel()); +  EXPECT_FALSE(instr->AreAllMethodsDeoptimized()); +  EXPECT_TRUE(instr->AreExitStubsInstalled()); +  EXPECT_TRUE(instr->IsDeoptimized(method_to_deoptimize.Get())); + +  UndeoptimizeMethod(soa.Self(), method_to_deoptimize, instrumentation_key, true); +  EXPECT_EQ(Instrumentation::InstrumentationLevel::kInstrumentNothing, +            GetCurrentInstrumentationLevel()); +  EXPECT_FALSE(instr->AreAllMethodsDeoptimized()); +  EXPECT_FALSE(instr->IsDeoptimized(method_to_deoptimize.Get())); +} + +TEST_F(InstrumentationTest, MethodTracing_Interpreter) { +  ScopedObjectAccess soa(Thread::Current()); +  Runtime* const runtime = Runtime::Current(); +  instrumentation::Instrumentation* instr = runtime->GetInstrumentation(); +  EXPECT_FALSE(instr->AreAllMethodsDeoptimized()); + +  constexpr const char* instrumentation_key = "MethodTracing"; +  EnableMethodTracing(soa.Self(), instrumentation_key, true); +  EXPECT_EQ(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter, +            GetCurrentInstrumentationLevel()); +  EXPECT_TRUE(instr->AreAllMethodsDeoptimized()); +  EXPECT_TRUE(instr->AreExitStubsInstalled()); + +  DisableMethodTracing(soa.Self(), instrumentation_key); +  EXPECT_EQ(Instrumentation::InstrumentationLevel::kInstrumentNothing, +            GetCurrentInstrumentationLevel()); +  EXPECT_FALSE(instr->AreAllMethodsDeoptimized()); +} + +TEST_F(InstrumentationTest, MethodTracing_InstrumentationEntryExitStubs) { +  ScopedObjectAccess soa(Thread::Current()); +  Runtime* const runtime = Runtime::Current(); +  instrumentation::Instrumentation* instr = runtime->GetInstrumentation(); +  EXPECT_FALSE(instr->AreAllMethodsDeoptimized()); + +  constexpr const char* instrumentation_key = "MethodTracing"; +  EnableMethodTracing(soa.Self(), instrumentation_key, false); +  EXPECT_EQ(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs, +            GetCurrentInstrumentationLevel()); +  EXPECT_FALSE(instr->AreAllMethodsDeoptimized()); +  EXPECT_TRUE(instr->AreExitStubsInstalled()); + +  DisableMethodTracing(soa.Self(), instrumentation_key); +  EXPECT_EQ(Instrumentation::InstrumentationLevel::kInstrumentNothing, +            GetCurrentInstrumentationLevel()); +  EXPECT_FALSE(instr->AreAllMethodsDeoptimized()); +} + +// We use a macro to print the line number where the test is failing. +#define CHECK_INSTRUMENTATION(_level, _user_count)                                      \ +  do {                                                                                  \ +    Instrumentation* const instr = Runtime::Current()->GetInstrumentation();            \ +    bool interpreter =                                                                  \ +      (_level == Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter);    \ +    EXPECT_EQ(_level, GetCurrentInstrumentationLevel());                                \ +    EXPECT_EQ(_user_count, GetInstrumentationUserCount());                              \ +    if (instr->IsForcedInterpretOnly()) {                                               \ +      EXPECT_TRUE(instr->InterpretOnly());                                              \ +    } else if (interpreter) {                                                           \ +      EXPECT_TRUE(instr->InterpretOnly());                                              \ +    } else {                                                                            \ +      EXPECT_FALSE(instr->InterpretOnly());                                             \ +    }                                                                                   \ +    if (interpreter) {                                                                  \ +      EXPECT_TRUE(instr->AreAllMethodsDeoptimized());                                   \ +    } else {                                                                            \ +      EXPECT_FALSE(instr->AreAllMethodsDeoptimized());                                  \ +    }                                                                                   \ +  } while (false) + +TEST_F(InstrumentationTest, ConfigureStubs_Nothing) { +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U); + +  // Check no-op. +  CheckConfigureStubs(kClientOneKey, Instrumentation::InstrumentationLevel::kInstrumentNothing); +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U); +} + +TEST_F(InstrumentationTest, ConfigureStubs_InstrumentationStubs) { +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U); + +  // Check we can switch to instrumentation stubs +  CheckConfigureStubs(kClientOneKey, +                      Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs); +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs, +                        1U); + +  // Check we can disable instrumentation. +  CheckConfigureStubs(kClientOneKey, Instrumentation::InstrumentationLevel::kInstrumentNothing); +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U); +} + +TEST_F(InstrumentationTest, ConfigureStubs_Interpreter) { +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U); + +  // Check we can switch to interpreter +  CheckConfigureStubs(kClientOneKey, +                      Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter); +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter, 1U); + +  // Check we can disable instrumentation. +  CheckConfigureStubs(kClientOneKey, Instrumentation::InstrumentationLevel::kInstrumentNothing); +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U); +} + +TEST_F(InstrumentationTest, ConfigureStubs_InstrumentationStubsToInterpreter) { +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U); + +  // Configure stubs with instrumentation stubs. +  CheckConfigureStubs(kClientOneKey, +                      Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs); +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs, +                        1U); + +  // Configure stubs with interpreter. +  CheckConfigureStubs(kClientOneKey, +                      Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter); +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter, 1U); + +  // Check we can disable instrumentation. +  CheckConfigureStubs(kClientOneKey, Instrumentation::InstrumentationLevel::kInstrumentNothing); +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U); +} + +TEST_F(InstrumentationTest, ConfigureStubs_InterpreterToInstrumentationStubs) { +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U); + +  // Configure stubs with interpreter. +  CheckConfigureStubs(kClientOneKey, +                      Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter); +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter, 1U); + +  // Configure stubs with instrumentation stubs. +  CheckConfigureStubs(kClientOneKey, +                      Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs); +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs, +                        1U); + +  // Check we can disable instrumentation. +  CheckConfigureStubs(kClientOneKey, Instrumentation::InstrumentationLevel::kInstrumentNothing); +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U); +} + +TEST_F(InstrumentationTest, +       ConfigureStubs_InstrumentationStubsToInterpreterToInstrumentationStubs) { +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U); + +  // Configure stubs with instrumentation stubs. +  CheckConfigureStubs(kClientOneKey, +                      Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs); +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs, +                        1U); + +  // Configure stubs with interpreter. +  CheckConfigureStubs(kClientOneKey, +                      Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter); +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter, 1U); + +  // Configure stubs with instrumentation stubs again. +  CheckConfigureStubs(kClientOneKey, +                      Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs); +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs, +                        1U); + +  // Check we can disable instrumentation. +  CheckConfigureStubs(kClientOneKey, Instrumentation::InstrumentationLevel::kInstrumentNothing); +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U); +} + +TEST_F(InstrumentationTest, MultiConfigureStubs_Nothing) { +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U); + +  // Check kInstrumentNothing with two clients. +  CheckConfigureStubs(kClientOneKey, Instrumentation::InstrumentationLevel::kInstrumentNothing); +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U); + +  CheckConfigureStubs(kClientTwoKey, Instrumentation::InstrumentationLevel::kInstrumentNothing); +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U); +} + +TEST_F(InstrumentationTest, MultiConfigureStubs_InstrumentationStubs) { +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U); + +  // Configure stubs with instrumentation stubs for 1st client. +  CheckConfigureStubs(kClientOneKey, +                      Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs); +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs, +                        1U); + +  // Configure stubs with instrumentation stubs for 2nd client. +  CheckConfigureStubs(kClientTwoKey, +                      Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs); +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs, +                        2U); + +  // 1st client requests instrumentation deactivation but 2nd client still needs +  // instrumentation stubs. +  CheckConfigureStubs(kClientOneKey, Instrumentation::InstrumentationLevel::kInstrumentNothing); +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs, +                        1U); + +  // 2nd client requests instrumentation deactivation +  CheckConfigureStubs(kClientTwoKey, Instrumentation::InstrumentationLevel::kInstrumentNothing); +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U); +} + +TEST_F(InstrumentationTest, MultiConfigureStubs_Interpreter) { +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U); + +  // Configure stubs with interpreter for 1st client. +  CheckConfigureStubs(kClientOneKey, +                      Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter); +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter, 1U); + +  // Configure stubs with interpreter for 2nd client. +  CheckConfigureStubs(kClientTwoKey, +                      Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter); +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter, 2U); + +  // 1st client requests instrumentation deactivation but 2nd client still needs interpreter. +  CheckConfigureStubs(kClientOneKey, Instrumentation::InstrumentationLevel::kInstrumentNothing); +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter, 1U); + +  // 2nd client requests instrumentation deactivation +  CheckConfigureStubs(kClientTwoKey, Instrumentation::InstrumentationLevel::kInstrumentNothing); +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U); +} + +TEST_F(InstrumentationTest, MultiConfigureStubs_InstrumentationStubsThenInterpreter) { +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U); + +  // Configure stubs with instrumentation stubs for 1st client. +  CheckConfigureStubs(kClientOneKey, +                      Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs); +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs, +                        1U); + +  // Configure stubs with interpreter for 2nd client. +  CheckConfigureStubs(kClientTwoKey, +                      Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter); +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter, 2U); + +  // 1st client requests instrumentation deactivation but 2nd client still needs interpreter. +  CheckConfigureStubs(kClientOneKey, Instrumentation::InstrumentationLevel::kInstrumentNothing); +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter, 1U); + +  // 2nd client requests instrumentation deactivation +  CheckConfigureStubs(kClientTwoKey, Instrumentation::InstrumentationLevel::kInstrumentNothing); +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U); +} + +TEST_F(InstrumentationTest, MultiConfigureStubs_InterpreterThenInstrumentationStubs) { +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U); + +  // Configure stubs with interpreter for 1st client. +  CheckConfigureStubs(kClientOneKey, +                      Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter); +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter, 1U); + +  // Configure stubs with instrumentation stubs for 2nd client. +  CheckConfigureStubs(kClientTwoKey, +                      Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs); +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInterpreter, 2U); + +  // 1st client requests instrumentation deactivation but 2nd client still needs +  // instrumentation stubs. +  CheckConfigureStubs(kClientOneKey, Instrumentation::InstrumentationLevel::kInstrumentNothing); +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentWithInstrumentationStubs, +                        1U); + +  // 2nd client requests instrumentation deactivation +  CheckConfigureStubs(kClientTwoKey, Instrumentation::InstrumentationLevel::kInstrumentNothing); +  CHECK_INSTRUMENTATION(Instrumentation::InstrumentationLevel::kInstrumentNothing, 0U); +} + +}  // namespace instrumentation +}  // namespace art diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc index 878efba1a5..dd1f55e6b2 100644 --- a/runtime/interpreter/interpreter_goto_table_impl.cc +++ b/runtime/interpreter/interpreter_goto_table_impl.cc @@ -156,7 +156,6 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF    const Instruction* inst = Instruction::At(code_item->insns_ + dex_pc);    uint16_t inst_data;    const void* const* currentHandlersTable; -  bool notified_method_entry_event = false;    UPDATE_HANDLER_TABLE();    if (LIKELY(dex_pc == 0)) {  // We are entering the method as opposed to deoptimizing.      if (kIsDebugBuild) { @@ -166,7 +165,6 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF      if (UNLIKELY(instrumentation->HasMethodEntryListeners())) {        instrumentation->MethodEnterEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),                                          shadow_frame.GetMethod(), 0); -      notified_method_entry_event = true;      }    } @@ -264,9 +262,6 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF        instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),                                         shadow_frame.GetMethod(), dex_pc,                                         result); -    } else if (UNLIKELY(instrumentation->HasDexPcListeners())) { -      instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), -                                       shadow_frame.GetMethod(), dex_pc);      }      return result;    } @@ -281,9 +276,6 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF        instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),                                         shadow_frame.GetMethod(), dex_pc,                                         result); -    } else if (UNLIKELY(instrumentation->HasDexPcListeners())) { -      instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), -                                       shadow_frame.GetMethod(), dex_pc);      }      return result;    } @@ -299,9 +291,6 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF        instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),                                         shadow_frame.GetMethod(), dex_pc,                                         result); -    } else if (UNLIKELY(instrumentation->HasDexPcListeners())) { -      instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), -                                       shadow_frame.GetMethod(), dex_pc);      }      return result;    } @@ -316,9 +305,6 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF        instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),                                         shadow_frame.GetMethod(), dex_pc,                                         result); -    } else if (UNLIKELY(instrumentation->HasDexPcListeners())) { -      instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), -                                       shadow_frame.GetMethod(), dex_pc);      }      return result;    } @@ -352,9 +338,6 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF        instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),                                         shadow_frame.GetMethod(), dex_pc,                                         result); -    } else if (UNLIKELY(instrumentation->HasDexPcListeners())) { -      instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), -                                       shadow_frame.GetMethod(), dex_pc);      }      return result;    } @@ -2510,26 +2493,16 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF  // Note: we do not use the kReturn instruction flag here (to test the instruction is a return). The  // compiler seems to not evaluate "(Instruction::FlagsOf(Instruction::code) & kReturn) != 0" to  // a constant condition that would remove the "if" statement so the test is free. -#define INSTRUMENTATION_INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v)                            \ -  alt_op_##code: {                                                                                \ -    if (Instruction::code != Instruction::RETURN_VOID &&                                          \ -        Instruction::code != Instruction::RETURN_VOID_NO_BARRIER &&                               \ -        Instruction::code != Instruction::RETURN &&                                               \ -        Instruction::code != Instruction::RETURN_WIDE &&                                          \ -        Instruction::code != Instruction::RETURN_OBJECT) {                                        \ -      if (LIKELY(!notified_method_entry_event)) {                                                 \ -        Runtime* runtime = Runtime::Current();                                                    \ -        const instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();  \ -        if (UNLIKELY(instrumentation->HasDexPcListeners())) {                                     \ -          Object* this_object = shadow_frame.GetThisObject(code_item->ins_size_);                 \ -          instrumentation->DexPcMovedEvent(self, this_object, shadow_frame.GetMethod(), dex_pc);  \ -        }                                                                                         \ -      } else {                                                                                    \ -        notified_method_entry_event = false;                                                      \ -      }                                                                                           \ -    }                                                                                             \ -    UPDATE_HANDLER_TABLE();                                                                       \ -    goto *handlersTable[instrumentation::kMainHandlerTable][Instruction::code];                   \ +#define INSTRUMENTATION_INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v)                        \ +  alt_op_##code: {                                                                            \ +    Runtime* const runtime = Runtime::Current();                                              \ +    const instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();  \ +    if (UNLIKELY(instrumentation->HasDexPcListeners())) {                                     \ +      Object* this_object = shadow_frame.GetThisObject(code_item->ins_size_);                 \ +      instrumentation->DexPcMovedEvent(self, this_object, shadow_frame.GetMethod(), dex_pc);  \ +    }                                                                                         \ +    UPDATE_HANDLER_TABLE();                                                                   \ +    goto *handlersTable[instrumentation::kMainHandlerTable][Instruction::code];               \    }  #include "dex_instruction_list.h"        DEX_INSTRUCTION_LIST(INSTRUMENTATION_INSTRUCTION_HANDLER) diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index a5e5299b34..0e3420ffb5 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -47,10 +47,7 @@ namespace interpreter {  // Code to run before each dex instruction.  #define PREAMBLE()                                                                              \    do {                                                                                          \ -    DCHECK(!inst->IsReturn());                                                                  \ -    if (UNLIKELY(notified_method_entry_event)) {                                                \ -      notified_method_entry_event = false;                                                      \ -    } else if (UNLIKELY(instrumentation->HasDexPcListeners())) {                                \ +    if (UNLIKELY(instrumentation->HasDexPcListeners())) {                                       \        instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),  \                                         shadow_frame.GetMethod(), dex_pc);                       \      }                                                                                           \ @@ -67,7 +64,6 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,    self->VerifyStack();    uint32_t dex_pc = shadow_frame.GetDexPC(); -  bool notified_method_entry_event = false;    const auto* const instrumentation = Runtime::Current()->GetInstrumentation();    if (LIKELY(dex_pc == 0)) {  // We are entering the method as opposed to deoptimizing.      if (kIsDebugBuild) { @@ -76,7 +72,6 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,      if (UNLIKELY(instrumentation->HasMethodEntryListeners())) {        instrumentation->MethodEnterEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),                                          shadow_frame.GetMethod(), 0); -      notified_method_entry_event = true;      }    }    const uint16_t* const insns = code_item->insns_; @@ -171,19 +166,18 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,          break;        }        case Instruction::RETURN_VOID_NO_BARRIER: { +        PREAMBLE();          JValue result;          self->AllowThreadSuspension();          if (UNLIKELY(instrumentation->HasMethodExitListeners())) {            instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),                                             shadow_frame.GetMethod(), inst->GetDexPc(insns),                                             result); -        } else if (UNLIKELY(instrumentation->HasDexPcListeners())) { -          instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), -                                           shadow_frame.GetMethod(), dex_pc);          }          return result;        }        case Instruction::RETURN_VOID: { +        PREAMBLE();          QuasiAtomic::ThreadFenceForConstructor();          JValue result;          self->AllowThreadSuspension(); @@ -191,13 +185,11 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,            instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),                                             shadow_frame.GetMethod(), inst->GetDexPc(insns),                                             result); -        } else if (UNLIKELY(instrumentation->HasDexPcListeners())) { -          instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), -                                           shadow_frame.GetMethod(), dex_pc);          }          return result;        }        case Instruction::RETURN: { +        PREAMBLE();          JValue result;          result.SetJ(0);          result.SetI(shadow_frame.GetVReg(inst->VRegA_11x(inst_data))); @@ -206,13 +198,11 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,            instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),                                             shadow_frame.GetMethod(), inst->GetDexPc(insns),                                             result); -        } else if (UNLIKELY(instrumentation->HasDexPcListeners())) { -          instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), -                                           shadow_frame.GetMethod(), dex_pc);          }          return result;        }        case Instruction::RETURN_WIDE: { +        PREAMBLE();          JValue result;          result.SetJ(shadow_frame.GetVRegLong(inst->VRegA_11x(inst_data)));          self->AllowThreadSuspension(); @@ -220,13 +210,11 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,            instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),                                             shadow_frame.GetMethod(), inst->GetDexPc(insns),                                             result); -        } else if (UNLIKELY(instrumentation->HasDexPcListeners())) { -          instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), -                                           shadow_frame.GetMethod(), dex_pc);          }          return result;        }        case Instruction::RETURN_OBJECT: { +        PREAMBLE();          JValue result;          self->AllowThreadSuspension();          const size_t ref_idx = inst->VRegA_11x(inst_data); @@ -254,9 +242,6 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,            instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),                                             shadow_frame.GetMethod(), inst->GetDexPc(insns),                                             result); -        } else if (UNLIKELY(instrumentation->HasDexPcListeners())) { -          instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), -                                           shadow_frame.GetMethod(), dex_pc);          }          return result;        } diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc index 1ec800fce6..ab3f2e4ddd 100644 --- a/runtime/jdwp/jdwp_event.cc +++ b/runtime/jdwp/jdwp_event.cc @@ -141,6 +141,8 @@ static bool NeedsFullDeoptimization(JdwpEventKind eventKind) {      }  } +// Returns the instrumentation event the DebugInstrumentationListener must +// listen to in order to properly report the given JDWP event to the debugger.  static uint32_t GetInstrumentationEventFor(JdwpEventKind eventKind) {    switch (eventKind) {      case EK_BREAKPOINT: diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h index f5ad8b837c..c698cfc180 100644 --- a/runtime/jit/jit.h +++ b/runtime/jit/jit.h @@ -19,8 +19,6 @@  #include <unordered_map> -#include "instrumentation.h" -  #include "atomic.h"  #include "base/macros.h"  #include "base/mutex.h" diff --git a/runtime/jit/jit_instrumentation.cc b/runtime/jit/jit_instrumentation.cc index e2f9cec2ce..32326740c6 100644 --- a/runtime/jit/jit_instrumentation.cc +++ b/runtime/jit/jit_instrumentation.cc @@ -77,7 +77,7 @@ void JitInstrumentationCache::AddSamples(Thread* self, mirror::ArtMethod* method    ScopedObjectAccessUnchecked soa(self);    // Since we don't have on-stack replacement, some methods can remain in the interpreter longer    // than we want resulting in samples even after the method is compiled. -  if (method->IsClassInitializer() || +  if (method->IsClassInitializer() || method->IsNative() ||        Runtime::Current()->GetJit()->GetCodeCache()->ContainsMethod(method)) {      return;    } diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc index 959bb75c93..cf4233c3d1 100644 --- a/runtime/mem_map.cc +++ b/runtime/mem_map.cc @@ -153,7 +153,7 @@ static bool ContainedWithinExistingMap(uint8_t* ptr, size_t size,        return true;      }    } -  PrintFileToLog("/proc/self/maps", LogSeverity::WARNING); +  PrintFileToLog("/proc/self/maps", LogSeverity::ERROR);    *error_msg = StringPrintf("Requested region 0x%08" PRIxPTR "-0x%08" PRIxPTR " does not overlap "                              "any existing map. See process maps in the log.", begin, end);    return false; @@ -256,7 +256,7 @@ MemMap* MemMap::MapAnonymous(const char* name, uint8_t* expected_ptr, size_t byt      // Only use this if you actually made the page reservation yourself.      CHECK(expected_ptr != nullptr); -    DCHECK(ContainedWithinExistingMap(expected_ptr, byte_count, error_msg)) << error_msg; +    DCHECK(ContainedWithinExistingMap(expected_ptr, byte_count, error_msg)) << *error_msg;      flags |= MAP_FIXED;    } @@ -411,7 +411,7 @@ MemMap* MemMap::MapFileAtAddress(uint8_t* expected_ptr, size_t byte_count, int p      // Only use this if you actually made the page reservation yourself.      CHECK(expected_ptr != nullptr); -    DCHECK(ContainedWithinExistingMap(expected_ptr, byte_count, error_msg)) << error_msg; +    DCHECK(ContainedWithinExistingMap(expected_ptr, byte_count, error_msg)) << *error_msg;      flags |= MAP_FIXED;    } else {      CHECK_EQ(0, flags & MAP_FIXED); @@ -617,13 +617,68 @@ bool MemMap::CheckNoGaps(MemMap* begin_map, MemMap* end_map) {    return true;  } -void MemMap::DumpMaps(std::ostream& os) { +void MemMap::DumpMaps(std::ostream& os, bool terse) {    MutexLock mu(Thread::Current(), *Locks::mem_maps_lock_); -  DumpMapsLocked(os); +  DumpMapsLocked(os, terse);  } -void MemMap::DumpMapsLocked(std::ostream& os) { -  os << *maps_; +void MemMap::DumpMapsLocked(std::ostream& os, bool terse) { +  const auto& mem_maps = *maps_; +  if (!terse) { +    os << mem_maps; +    return; +  } + +  // Terse output example: +  //   [MemMap: 0x409be000+0x20P~0x11dP+0x20P~0x61cP+0x20P prot=0x3 LinearAlloc] +  //   [MemMap: 0x451d6000+0x6bP(3) prot=0x3 large object space allocation] +  // The details: +  //   "+0x20P" means 0x20 pages taken by a single mapping, +  //   "~0x11dP" means a gap of 0x11d pages, +  //   "+0x6bP(3)" means 3 mappings one after another, together taking 0x6b pages. +  os << "MemMap:" << std::endl; +  for (auto it = mem_maps.begin(), maps_end = mem_maps.end(); it != maps_end;) { +    MemMap* map = it->second; +    void* base = it->first; +    CHECK_EQ(base, map->BaseBegin()); +    os << "[MemMap: " << base; +    ++it; +    // Merge consecutive maps with the same protect flags and name. +    constexpr size_t kMaxGaps = 9; +    size_t num_gaps = 0; +    size_t num = 1u; +    size_t size = map->BaseSize(); +    CHECK(IsAligned<kPageSize>(size)); +    void* end = map->BaseEnd(); +    while (it != maps_end && +        it->second->GetProtect() == map->GetProtect() && +        it->second->GetName() == map->GetName() && +        (it->second->BaseBegin() == end || num_gaps < kMaxGaps)) { +      if (it->second->BaseBegin() != end) { +        ++num_gaps; +        os << "+0x" << std::hex << (size / kPageSize) << "P"; +        if (num != 1u) { +          os << "(" << std::dec << num << ")"; +        } +        size_t gap = +            reinterpret_cast<uintptr_t>(it->second->BaseBegin()) - reinterpret_cast<uintptr_t>(end); +        CHECK(IsAligned<kPageSize>(gap)); +        os << "~0x" << std::hex << (gap / kPageSize) << "P"; +        num = 0u; +        size = 0u; +      } +      CHECK(IsAligned<kPageSize>(it->second->BaseSize())); +      ++num; +      size += it->second->BaseSize(); +      end = it->second->BaseEnd(); +      ++it; +    } +    os << "+0x" << std::hex << (size / kPageSize) << "P"; +    if (num != 1u) { +      os << "(" << std::dec << num << ")"; +    } +    os << " prot=0x" << std::hex << map->GetProtect() << " " << map->GetName() << "]" << std::endl; +  }  }  bool MemMap::HasMemMap(MemMap* map) { diff --git a/runtime/mem_map.h b/runtime/mem_map.h index dc6d93536d..6023a703c5 100644 --- a/runtime/mem_map.h +++ b/runtime/mem_map.h @@ -137,7 +137,7 @@ class MemMap {    static bool CheckNoGaps(MemMap* begin_map, MemMap* end_map)        LOCKS_EXCLUDED(Locks::mem_maps_lock_); -  static void DumpMaps(std::ostream& os) +  static void DumpMaps(std::ostream& os, bool terse = false)        LOCKS_EXCLUDED(Locks::mem_maps_lock_);    typedef AllocationTrackingMultiMap<void*, MemMap*, kAllocatorTagMaps> Maps; @@ -149,7 +149,7 @@ class MemMap {    MemMap(const std::string& name, uint8_t* begin, size_t size, void* base_begin, size_t base_size,           int prot, bool reuse) LOCKS_EXCLUDED(Locks::mem_maps_lock_); -  static void DumpMapsLocked(std::ostream& os) +  static void DumpMapsLocked(std::ostream& os, bool terse)        EXCLUSIVE_LOCKS_REQUIRED(Locks::mem_maps_lock_);    static bool HasMemMap(MemMap* map)        EXCLUSIVE_LOCKS_REQUIRED(Locks::mem_maps_lock_); diff --git a/runtime/native/dalvik_system_VMStack.cc b/runtime/native/dalvik_system_VMStack.cc index 17fbc4f85d..1d7d853431 100644 --- a/runtime/native/dalvik_system_VMStack.cc +++ b/runtime/native/dalvik_system_VMStack.cc @@ -84,7 +84,8 @@ static jobject VMStack_getCallingClassLoader(JNIEnv* env, jclass) {  static jobject VMStack_getClosestUserClassLoader(JNIEnv* env, jclass) {    struct ClosestUserClassLoaderVisitor : public StackVisitor {      explicit ClosestUserClassLoaderVisitor(Thread* thread) -      : StackVisitor(thread, nullptr), class_loader(nullptr) {} +      : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), +        class_loader(nullptr) {}      bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {        DCHECK(class_loader == nullptr); diff --git a/runtime/nth_caller_visitor.h b/runtime/nth_caller_visitor.h index 632ccdedc0..d2d7fa8a21 100644 --- a/runtime/nth_caller_visitor.h +++ b/runtime/nth_caller_visitor.h @@ -27,8 +27,11 @@ class Thread;  // Walks up the stack 'n' callers, when used with Thread::WalkStack.  struct NthCallerVisitor : public StackVisitor {    NthCallerVisitor(Thread* thread, size_t n_in, bool include_runtime_and_upcalls = false) -      : StackVisitor(thread, nullptr), n(n_in), -        include_runtime_and_upcalls_(include_runtime_and_upcalls), count(0), caller(nullptr) {} +      : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), +        n(n_in), +        include_runtime_and_upcalls_(include_runtime_and_upcalls), +        count(0), +        caller(nullptr) {}    bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {      mirror::ArtMethod* m = GetMethod(); diff --git a/runtime/oat.h b/runtime/oat.h index a31e09a3cf..aaf442a77a 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,7 +32,7 @@ class InstructionSetFeatures;  class PACKED(4) OatHeader {   public:    static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; -  static constexpr uint8_t kOatVersion[] = { '0', '6', '1', '\0' }; +  static constexpr uint8_t kOatVersion[] = { '0', '6', '2', '\0' };    static constexpr const char* kImageLocationKey = "image-location";    static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h index a25ee31d0d..3b2e2c45ca 100644 --- a/runtime/oat_file_assistant.h +++ b/runtime/oat_file_assistant.h @@ -405,9 +405,9 @@ class OatFileAssistant {    bool cached_oat_file_name_found_;    std::string cached_oat_file_name_; -  // Cached value of the loaded odex file. +  // Cached value of the loaded oat file.    // Use the GetOatFile method rather than accessing this directly, unless you -  // know the odex file isn't out of date. +  // know the oat file isn't out of date.    bool oat_file_load_attempted_ = false;    std::unique_ptr<OatFile> cached_oat_file_; diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index 3f6b2d2cc6..865fcb063a 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -29,6 +29,7 @@  #include "class_linker-inl.h"  #include "common_runtime_test.h"  #include "compiler_callbacks.h" +#include "gc/space/image_space.h"  #include "mem_map.h"  #include "os.h"  #include "scoped_thread_state_change.h" @@ -610,10 +611,23 @@ TEST_F(OatFileAssistantTest, OdexOatOverlap) {    // Things aren't relocated, so it should fall back to interpreted.    std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();    ASSERT_TRUE(oat_file.get() != nullptr); +    EXPECT_FALSE(oat_file->IsExecutable());    std::vector<std::unique_ptr<const DexFile>> dex_files;    dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());    EXPECT_EQ(1u, dex_files.size()); + +  // Add some extra checks to help diagnose apparently flaky test failures. +  Runtime* runtime = Runtime::Current(); +  const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace(); +  ASSERT_TRUE(image_space != nullptr); +  const ImageHeader& image_header = image_space->GetImageHeader(); +  const OatHeader& oat_header = oat_file->GetOatHeader(); +  EXPECT_FALSE(oat_file->IsPic()); +  EXPECT_EQ(image_header.GetOatChecksum(), oat_header.GetImageFileLocationOatChecksum()); +  EXPECT_NE(reinterpret_cast<uintptr_t>(image_header.GetOatDataBegin()), +      oat_header.GetImageFileLocationOatDataBegin()); +  EXPECT_NE(image_header.GetPatchDelta(), oat_header.GetImagePatchDelta());  }  // Case: We have a DEX file and a PIC ODEX file, but no OAT file. diff --git a/runtime/profiler.cc b/runtime/profiler.cc index 90a47b38c2..5354fd8486 100644 --- a/runtime/profiler.cc +++ b/runtime/profiler.cc @@ -58,8 +58,10 @@ class BoundedStackVisitor : public StackVisitor {    BoundedStackVisitor(std::vector<std::pair<mirror::ArtMethod*, uint32_t>>* stack,        Thread* thread, uint32_t max_depth)        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) -      : StackVisitor(thread, nullptr), stack_(stack), max_depth_(max_depth), depth_(0) { -  } +      : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), +        stack_(stack), +        max_depth_(max_depth), +        depth_(0) {}    bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {      mirror::ArtMethod* m = GetMethod(); diff --git a/runtime/quick/inline_method_analyser.cc b/runtime/quick/inline_method_analyser.cc index 9cf4b16582..1c404ff141 100644 --- a/runtime/quick/inline_method_analyser.cc +++ b/runtime/quick/inline_method_analyser.cc @@ -134,7 +134,10 @@ bool InlineMethodAnalyser::AnalyseMethodCode(verifier::MethodVerifier* verifier,  bool InlineMethodAnalyser::IsSyntheticAccessor(MethodReference ref) {    const DexFile::MethodId& method_id = ref.dex_file->GetMethodId(ref.dex_method_index);    const char* method_name = ref.dex_file->GetMethodName(method_id); -  return strncmp(method_name, "access$", strlen("access$")) == 0; +  // javac names synthetic accessors "access$nnn", +  // jack names them "-getN", "-putN", "-wrapN". +  return strncmp(method_name, "access$", strlen("access$")) == 0 || +      strncmp(method_name, "-", strlen("-")) == 0;  }  bool InlineMethodAnalyser::AnalyseReturnMethod(const DexFile::CodeItem* code_item, diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc index a80eed6073..730759a71b 100644 --- a/runtime/quick_exception_handler.cc +++ b/runtime/quick_exception_handler.cc @@ -46,7 +46,9 @@ class CatchBlockStackVisitor FINAL : public StackVisitor {    CatchBlockStackVisitor(Thread* self, Context* context, Handle<mirror::Throwable>* exception,                           QuickExceptionHandler* exception_handler)        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) -      : StackVisitor(self, context), self_(self), exception_(exception), +      : StackVisitor(self, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames), +        self_(self), +        exception_(exception),          exception_handler_(exception_handler) {    } @@ -160,7 +162,9 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor {   public:    DeoptimizeStackVisitor(Thread* self, Context* context, QuickExceptionHandler* exception_handler)        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) -      : StackVisitor(self, context), self_(self), exception_handler_(exception_handler), +      : StackVisitor(self, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames), +        self_(self), +        exception_handler_(exception_handler),          prev_shadow_frame_(nullptr) {      CHECK(!self_->HasDeoptimizationShadowFrame());    } @@ -223,7 +227,10 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor {            break;          case kReferenceVReg: {            uint32_t value = 0; -          if (GetVReg(h_method.Get(), reg, kind, &value)) { +          // Check IsReferenceVReg in case the compiled GC map doesn't agree with the verifier. +          // We don't want to copy a stale reference into the shadow frame as a reference. +          // b/20736048 +          if (GetVReg(h_method.Get(), reg, kind, &value) && IsReferenceVReg(h_method.Get(), reg)) {              new_frame->SetVRegReference(reg, reinterpret_cast<mirror::Object*>(value));            } else {              new_frame->SetVReg(reg, kDeadValue); @@ -335,7 +342,7 @@ class InstrumentationStackVisitor : public StackVisitor {   public:    InstrumentationStackVisitor(Thread* self, size_t frame_depth)        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) -      : StackVisitor(self, nullptr), +      : StackVisitor(self, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),          frame_depth_(frame_depth),          instrumentation_frames_to_pop_(0) {      CHECK_NE(frame_depth_, kInvalidFrameDepth); @@ -346,7 +353,12 @@ class InstrumentationStackVisitor : public StackVisitor {      if (current_frame_depth < frame_depth_) {        CHECK(GetMethod() != nullptr);        if (UNLIKELY(reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc()) == GetReturnPc())) { -        ++instrumentation_frames_to_pop_; +        if (!IsInInlinedFrame()) { +          // We do not count inlined frames, because we do not instrument them. The reason we +          // include them in the stack walking is the check against `frame_depth_`, which is +          // given to us by a visitor that visits inlined frames. +          ++instrumentation_frames_to_pop_; +        }        }        return true;      } else { diff --git a/runtime/reflection.cc b/runtime/reflection.cc index 329ceb561d..49e1b8edf6 100644 --- a/runtime/reflection.cc +++ b/runtime/reflection.cc @@ -520,23 +520,6 @@ JValue InvokeVirtualOrInterfaceWithVarArgs(const ScopedObjectAccessAlreadyRunnab    return result;  } -void InvokeWithShadowFrame(Thread* self, ShadowFrame* shadow_frame, uint16_t arg_offset, -                           JValue* result) { -  // We want to make sure that the stack is not within a small distance from the -  // protected region in case we are calling into a leaf function whose stack -  // check has been elided. -  if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEnd())) { -    ThrowStackOverflowError(self); -    return; -  } -  uint32_t shorty_len; -  const char* shorty = shadow_frame->GetMethod()->GetShorty(&shorty_len); -  ArgArray arg_array(shorty, shorty_len); -  arg_array.BuildArgArrayFromFrame(shadow_frame, arg_offset); -  shadow_frame->GetMethod()->Invoke(self, arg_array.GetArray(), arg_array.GetNumBytes(), result, -                                    shorty); -} -  jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject javaMethod,                       jobject javaReceiver, jobject javaArgs, size_t num_frames) {    // We want to make sure that the stack is not within a small distance from the diff --git a/runtime/reflection.h b/runtime/reflection.h index 6305d68bd5..37f8a6af55 100644 --- a/runtime/reflection.h +++ b/runtime/reflection.h @@ -61,10 +61,6 @@ JValue InvokeVirtualOrInterfaceWithVarArgs(const ScopedObjectAccessAlreadyRunnab                                             jobject obj, jmethodID mid, va_list args)      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); -void InvokeWithShadowFrame(Thread* self, ShadowFrame* shadow_frame, uint16_t arg_offset, -                           JValue* result) -    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); -  // num_frames is number of frames we look up for access check.  jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject method, jobject receiver,                       jobject args, size_t num_frames = 1) diff --git a/runtime/scoped_thread_state_change.h b/runtime/scoped_thread_state_change.h index 99750a16d0..60ed55a914 100644 --- a/runtime/scoped_thread_state_change.h +++ b/runtime/scoped_thread_state_change.h @@ -133,6 +133,7 @@ class ScopedObjectAccessAlreadyRunnable {    T AddLocalReference(mirror::Object* obj) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {      Locks::mutator_lock_->AssertSharedHeld(Self());      DCHECK(IsRunnable());  // Don't work with raw objects in non-runnable states. +    DCHECK_NE(obj, Runtime::Current()->GetClearedJniWeakGlobal());      return obj == nullptr ? nullptr : Env()->AddLocalReference<T>(obj);    } diff --git a/runtime/stack.cc b/runtime/stack.cc index e49bc1d78f..800acaa320 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -19,6 +19,7 @@  #include "arch/context.h"  #include "base/hex_dump.h"  #include "entrypoints/runtime_asm_entrypoints.h" +#include "gc_map.h"  #include "mirror/art_method-inl.h"  #include "mirror/class-inl.h"  #include "mirror/object.h" @@ -84,16 +85,20 @@ bool ManagedStack::ShadowFramesContain(StackReference<mirror::Object>* shadow_fr    return false;  } -StackVisitor::StackVisitor(Thread* thread, Context* context) -    : thread_(thread), cur_shadow_frame_(nullptr), -      cur_quick_frame_(nullptr), cur_quick_frame_pc_(0), num_frames_(0), cur_depth_(0), -      context_(context) { -  DCHECK(thread == Thread::Current() || thread->IsSuspended()) << *thread; -} - -StackVisitor::StackVisitor(Thread* thread, Context* context, size_t num_frames) -    : thread_(thread), cur_shadow_frame_(nullptr), -      cur_quick_frame_(nullptr), cur_quick_frame_pc_(0), num_frames_(num_frames), cur_depth_(0), +StackVisitor::StackVisitor(Thread* thread, Context* context, StackWalkKind walk_kind) +    : StackVisitor(thread, context, walk_kind, 0) {} + +StackVisitor::StackVisitor(Thread* thread, +                           Context* context, +                           StackWalkKind walk_kind, +                           size_t num_frames) +    : thread_(thread), +      walk_kind_(walk_kind), +      cur_shadow_frame_(nullptr), +      cur_quick_frame_(nullptr), +      cur_quick_frame_pc_(0), +      num_frames_(num_frames), +      cur_depth_(0),        context_(context) {    DCHECK(thread == Thread::Current() || thread->IsSuspended()) << *thread;  } @@ -151,6 +156,33 @@ size_t StackVisitor::GetNativePcOffset() const {    return GetMethod()->NativeQuickPcOffset(cur_quick_frame_pc_);  } +bool StackVisitor::IsReferenceVReg(mirror::ArtMethod* m, uint16_t vreg) { +  // Process register map (which native and runtime methods don't have) +  if (m->IsNative() || m->IsRuntimeMethod() || m->IsProxyMethod()) { +    return false; +  } +  if (m->IsOptimized(sizeof(void*))) { +    return true;  // TODO: Implement. +  } +  const uint8_t* native_gc_map = m->GetNativeGcMap(sizeof(void*)); +  CHECK(native_gc_map != nullptr) << PrettyMethod(m); +  const DexFile::CodeItem* code_item = m->GetCodeItem(); +  // Can't be null or how would we compile its instructions? +  DCHECK(code_item != nullptr) << PrettyMethod(m); +  NativePcOffsetToReferenceMap map(native_gc_map); +  size_t num_regs = std::min(map.RegWidth() * 8, static_cast<size_t>(code_item->registers_size_)); +  const uint8_t* reg_bitmap = nullptr; +  if (num_regs > 0) { +    Runtime* runtime = Runtime::Current(); +    const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(m, sizeof(void*)); +    uintptr_t native_pc_offset = m->NativeQuickPcOffset(GetCurrentQuickFramePc(), entry_point); +    reg_bitmap = map.FindBitMap(native_pc_offset); +    DCHECK(reg_bitmap != nullptr); +  } +  // Does this register hold a reference? +  return vreg < num_regs && TestBitmap(vreg, reg_bitmap); +} +  bool StackVisitor::GetVReg(mirror::ArtMethod* m, uint16_t vreg, VRegKind kind,                             uint32_t* val) const {    if (cur_quick_frame_ != nullptr) { @@ -537,10 +569,10 @@ void StackVisitor::SetReturnPc(uintptr_t new_ret_pc) {    *reinterpret_cast<uintptr_t*>(pc_addr) = new_ret_pc;  } -size_t StackVisitor::ComputeNumFrames(Thread* thread) { +size_t StackVisitor::ComputeNumFrames(Thread* thread, StackWalkKind walk_kind) {    struct NumFramesVisitor : public StackVisitor { -    explicit NumFramesVisitor(Thread* thread_in) -        : StackVisitor(thread_in, nullptr), frames(0) {} +    NumFramesVisitor(Thread* thread_in, StackWalkKind walk_kind_in) +        : StackVisitor(thread_in, nullptr, walk_kind_in), frames(0) {}      bool VisitFrame() OVERRIDE {        frames++; @@ -549,16 +581,23 @@ size_t StackVisitor::ComputeNumFrames(Thread* thread) {      size_t frames;    }; -  NumFramesVisitor visitor(thread); +  NumFramesVisitor visitor(thread, walk_kind);    visitor.WalkStack(true);    return visitor.frames;  }  bool StackVisitor::GetNextMethodAndDexPc(mirror::ArtMethod** next_method, uint32_t* next_dex_pc) {    struct HasMoreFramesVisitor : public StackVisitor { -    explicit HasMoreFramesVisitor(Thread* thread, size_t num_frames, size_t frame_height) -        : StackVisitor(thread, nullptr, num_frames), frame_height_(frame_height), -          found_frame_(false), has_more_frames_(false), next_method_(nullptr), next_dex_pc_(0) { +    HasMoreFramesVisitor(Thread* thread, +                         StackWalkKind walk_kind, +                         size_t num_frames, +                         size_t frame_height) +        : StackVisitor(thread, nullptr, walk_kind, num_frames), +          frame_height_(frame_height), +          found_frame_(false), +          has_more_frames_(false), +          next_method_(nullptr), +          next_dex_pc_(0) {      }      bool VisitFrame() OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -582,7 +621,7 @@ bool StackVisitor::GetNextMethodAndDexPc(mirror::ArtMethod** next_method, uint32      mirror::ArtMethod* next_method_;      uint32_t next_dex_pc_;    }; -  HasMoreFramesVisitor visitor(thread_, GetNumFrames(), GetFrameHeight()); +  HasMoreFramesVisitor visitor(thread_, walk_kind_, GetNumFrames(), GetFrameHeight());    visitor.WalkStack(true);    *next_method = visitor.next_method_;    *next_dex_pc = visitor.next_dex_pc_; @@ -592,7 +631,7 @@ bool StackVisitor::GetNextMethodAndDexPc(mirror::ArtMethod** next_method, uint32  void StackVisitor::DescribeStack(Thread* thread) {    struct DescribeStackVisitor : public StackVisitor {      explicit DescribeStackVisitor(Thread* thread_in) -        : StackVisitor(thread_in, nullptr) {} +        : StackVisitor(thread_in, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames) {}      bool VisitFrame() OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {        LOG(INFO) << "Frame Id=" << GetFrameId() << " " << DescribeLocation(); diff --git a/runtime/stack.h b/runtime/stack.h index 3f1bff8b9c..bf6101619d 100644 --- a/runtime/stack.h +++ b/runtime/stack.h @@ -409,8 +409,17 @@ class PACKED(4) ManagedStack {  };  class StackVisitor { + public: +  // This enum defines a flag to control whether inlined frames are included +  // when walking the stack. +  enum class StackWalkKind { +    kIncludeInlinedFrames, +    kSkipInlinedFrames, +  }; +   protected: -  StackVisitor(Thread* thread, Context* context) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +  StackVisitor(Thread* thread, Context* context, StackWalkKind walk_kind) +      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);   public:    virtual ~StackVisitor() {} @@ -465,7 +474,7 @@ class StackVisitor {    size_t GetNumFrames() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {      if (num_frames_ == 0) { -      num_frames_ = ComputeNumFrames(thread_); +      num_frames_ = ComputeNumFrames(thread_, walk_kind_);      }      return num_frames_;    } @@ -478,6 +487,9 @@ class StackVisitor {    bool GetNextMethodAndDexPc(mirror::ArtMethod** next_method, uint32_t* next_dex_pc)        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +  bool IsReferenceVReg(mirror::ArtMethod* m, uint16_t vreg) +      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +    bool GetVReg(mirror::ArtMethod* m, uint16_t vreg, VRegKind kind, uint32_t* val) const        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -598,6 +610,10 @@ class StackVisitor {      return sizeof(StackReference<mirror::ArtMethod>) + (out_num * sizeof(uint32_t));    } +  bool IsInInlinedFrame() const { +    return false; +  } +    uintptr_t GetCurrentQuickFramePc() const {      return cur_quick_frame_pc_;    } @@ -618,13 +634,14 @@ class StackVisitor {    std::string DescribeLocation() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); -  static size_t ComputeNumFrames(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +  static size_t ComputeNumFrames(Thread* thread, StackWalkKind walk_kind) +      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);    static void DescribeStack(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);   private:    // Private constructor known in the case that num_frames_ has already been computed. -  StackVisitor(Thread* thread, Context* context, size_t num_frames) +  StackVisitor(Thread* thread, Context* context, StackWalkKind walk_kind, size_t num_frames)        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);    bool IsAccessibleRegister(uint32_t reg, bool is_float) const { @@ -687,6 +704,7 @@ class StackVisitor {    void SanityCheckFrame() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);    Thread* const thread_; +  const StackWalkKind walk_kind_;    ShadowFrame* cur_shadow_frame_;    StackReference<mirror::ArtMethod>* cur_quick_frame_;    uintptr_t cur_quick_frame_pc_; diff --git a/runtime/thread.cc b/runtime/thread.cc index c8aad1b787..b7cfc7299d 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -940,10 +940,14 @@ void Thread::DumpState(std::ostream& os) const {  struct StackDumpVisitor : public StackVisitor {    StackDumpVisitor(std::ostream& os_in, Thread* thread_in, Context* context, bool can_allocate_in)        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) -      : StackVisitor(thread_in, context), os(os_in), thread(thread_in), -        can_allocate(can_allocate_in), last_method(nullptr), last_line_number(0), -        repetition_count(0), frame_count(0) { -  } +      : StackVisitor(thread_in, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames), +        os(os_in), +        thread(thread_in), +        can_allocate(can_allocate_in), +        last_method(nullptr), +        last_line_number(0), +        repetition_count(0), +        frame_count(0) {}    virtual ~StackDumpVisitor() {      if (frame_count == 0) { @@ -1528,7 +1532,7 @@ class CountStackDepthVisitor : public StackVisitor {   public:    explicit CountStackDepthVisitor(Thread* thread)        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) -      : StackVisitor(thread, nullptr), +      : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),          depth_(0), skip_depth_(0), skipping_(true) {}    bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -1568,8 +1572,12 @@ template<bool kTransactionActive>  class BuildInternalStackTraceVisitor : public StackVisitor {   public:    explicit BuildInternalStackTraceVisitor(Thread* self, Thread* thread, int skip_depth) -      : StackVisitor(thread, nullptr), self_(self), -        skip_depth_(skip_depth), count_(0), dex_pc_trace_(nullptr), method_trace_(nullptr) {} +      : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), +        self_(self), +        skip_depth_(skip_depth), +        count_(0), +        dex_pc_trace_(nullptr), +        method_trace_(nullptr) {}    bool Init(int depth)        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -2111,7 +2119,10 @@ Context* Thread::GetLongJumpContext() {  struct CurrentMethodVisitor FINAL : public StackVisitor {    CurrentMethodVisitor(Thread* thread, Context* context, bool abort_on_error)        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) -      : StackVisitor(thread, context), this_object_(nullptr), method_(nullptr), dex_pc_(0), +      : StackVisitor(thread, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames), +        this_object_(nullptr), +        method_(nullptr), +        dex_pc_(0),          abort_on_error_(abort_on_error) {}    bool VisitFrame() OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {      mirror::ArtMethod* m = GetMethod(); @@ -2154,7 +2165,10 @@ class ReferenceMapVisitor : public StackVisitor {   public:    ReferenceMapVisitor(Thread* thread, Context* context, RootVisitor& visitor)        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) -      : StackVisitor(thread, context), visitor_(visitor) {} +        // We are visiting the references in compiled frames, so we do not need +        // to know the inlined frames. +      : StackVisitor(thread, context, StackVisitor::StackWalkKind::kSkipInlinedFrames), +        visitor_(visitor) {}    bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {      if (false) { @@ -2311,10 +2325,6 @@ class ReferenceMapVisitor : public StackVisitor {      }    } -  static bool TestBitmap(size_t reg, const uint8_t* reg_vector) { -    return ((reg_vector[reg / kBitsPerByte] >> (reg % kBitsPerByte)) & 0x01) != 0; -  } -    // Visitor for when we visit a root.    RootVisitor& visitor_;  }; diff --git a/runtime/thread.h b/runtime/thread.h index e766daabed..9346813ec3 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -752,6 +752,18 @@ class Thread {      tls32_.ready_for_debug_invoke = ready;    } +  bool IsDebugMethodEntry() const { +    return tls32_.debug_method_entry_; +  } + +  void SetDebugMethodEntry() { +    tls32_.debug_method_entry_ = true; +  } + +  void ClearDebugMethodEntry() { +    tls32_.debug_method_entry_ = false; +  } +    // Activates single step control for debugging. The thread takes the    // ownership of the given SingleStepControl*. It is deleted by a call    // to DeactivateSingleStepControl or upon thread destruction. @@ -1028,7 +1040,7 @@ class Thread {        suspend_count(0), debug_suspend_count(0), thin_lock_thread_id(0), tid(0),        daemon(is_daemon), throwing_OutOfMemoryError(false), no_thread_suspension(0),        thread_exit_check_count(0), handling_signal_(false), suspended_at_suspend_check(false), -      ready_for_debug_invoke(false) { +      ready_for_debug_invoke(false), debug_method_entry_(false) {      }      union StateAndFlags state_and_flags; @@ -1077,6 +1089,10 @@ class Thread {      // used to invoke method from the debugger which is only allowed when      // the thread is suspended by an event.      bool32_t ready_for_debug_invoke; + +    // True if the thread enters a method. This is used to detect method entry +    // event for the debugger. +    bool32_t debug_method_entry_;    } tls32_;    struct PACKED(8) tls_64bit_sized_values { diff --git a/runtime/trace.cc b/runtime/trace.cc index 9eca517dca..76367923c0 100644 --- a/runtime/trace.cc +++ b/runtime/trace.cc @@ -91,8 +91,9 @@ static constexpr uint8_t kOpNewThread = 2U;  class BuildStackTraceVisitor : public StackVisitor {   public: -  explicit BuildStackTraceVisitor(Thread* thread) : StackVisitor(thread, nullptr), -      method_trace_(Trace::AllocStackTrace()) {} +  explicit BuildStackTraceVisitor(Thread* thread) +      : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), +        method_trace_(Trace::AllocStackTrace()) {}    bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {      mirror::ArtMethod* m = GetMethod(); @@ -126,6 +127,9 @@ Trace* volatile Trace::the_trace_ = nullptr;  pthread_t Trace::sampling_pthread_ = 0U;  std::unique_ptr<std::vector<mirror::ArtMethod*>> Trace::temp_stack_trace_; +// The key identifying the tracer to update instrumentation. +static constexpr const char* kTracerInstrumentationKey = "Tracer"; +  static mirror::ArtMethod* DecodeTraceMethodId(uint32_t tmid) {    return reinterpret_cast<mirror::ArtMethod*>(tmid & ~kTraceMethodActionMask);  } @@ -393,7 +397,7 @@ void Trace::Start(const char* trace_filename, int trace_fd, size_t buffer_size,                                                     instrumentation::Instrumentation::kMethodExited |                                                     instrumentation::Instrumentation::kMethodUnwind);          // TODO: In full-PIC mode, we don't need to fully deopt. -        runtime->GetInstrumentation()->EnableMethodTracing(); +        runtime->GetInstrumentation()->EnableMethodTracing(kTracerInstrumentationKey);        }      }    } @@ -440,7 +444,7 @@ void Trace::StopTracing(bool finish_tracing, bool flush_file) {        MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);        runtime->GetThreadList()->ForEach(ClearThreadStackTraceAndClockBase, nullptr);      } else { -      runtime->GetInstrumentation()->DisableMethodTracing(); +      runtime->GetInstrumentation()->DisableMethodTracing(kTracerInstrumentationKey);        runtime->GetInstrumentation()->RemoveListener(            the_trace, instrumentation::Instrumentation::kMethodEntered |            instrumentation::Instrumentation::kMethodExited | @@ -522,7 +526,7 @@ void Trace::Pause() {        MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);        runtime->GetThreadList()->ForEach(ClearThreadStackTraceAndClockBase, nullptr);      } else { -      runtime->GetInstrumentation()->DisableMethodTracing(); +      runtime->GetInstrumentation()->DisableMethodTracing(kTracerInstrumentationKey);        runtime->GetInstrumentation()->RemoveListener(the_trace,                                                      instrumentation::Instrumentation::kMethodEntered |                                                      instrumentation::Instrumentation::kMethodExited | @@ -566,7 +570,7 @@ void Trace::Resume() {                                                 instrumentation::Instrumentation::kMethodExited |                                                 instrumentation::Instrumentation::kMethodUnwind);      // TODO: In full-PIC mode, we don't need to fully deopt. -    runtime->GetInstrumentation()->EnableMethodTracing(); +    runtime->GetInstrumentation()->EnableMethodTracing(kTracerInstrumentationKey);    }    runtime->GetThreadList()->ResumeAll(); diff --git a/runtime/utf.h b/runtime/utf.h index dd38afa172..7f05248c29 100644 --- a/runtime/utf.h +++ b/runtime/utf.h @@ -87,9 +87,9 @@ size_t ComputeModifiedUtf8Hash(const char* chars);  /*   * Retrieve the next UTF-16 character or surrogate pair from a UTF-8 string.   * single byte, 2-byte and 3-byte UTF-8 sequences result in a single UTF-16 - * character whereas 4-byte UTF-8 sequences result in a surrogate pair. Use - * GetLeadingUtf16Char and GetTrailingUtf16Char to process the return value - * of this function. + * character (possibly one half of a surrogate) whereas 4-byte UTF-8 sequences + * result in a surrogate pair. Use GetLeadingUtf16Char and GetTrailingUtf16Char + * to process the return value of this function.   *   * Advances "*utf8_data_in" to the start of the next character.   * diff --git a/runtime/utils.cc b/runtime/utils.cc index 650214f67b..7986cdcbf9 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -827,14 +827,21 @@ bool IsValidPartOfMemberNameUtf8Slow(const char** pUtf8Ptr) {     */    const uint32_t pair = GetUtf16FromUtf8(pUtf8Ptr); -    const uint16_t leading = GetLeadingUtf16Char(pair); -  const uint32_t trailing = GetTrailingUtf16Char(pair); -  if (trailing == 0) { -    // Perform follow-up tests based on the high 8 bits of the -    // lower surrogate. -    switch (leading >> 8) { +  // We have a surrogate pair resulting from a valid 4 byte UTF sequence. +  // No further checks are necessary because 4 byte sequences span code +  // points [U+10000, U+1FFFFF], which are valid codepoints in a dex +  // identifier. Furthermore, GetUtf16FromUtf8 guarantees that each of +  // the surrogate halves are valid and well formed in this instance. +  if (GetTrailingUtf16Char(pair) != 0) { +    return true; +  } + + +  // We've encountered a one, two or three byte UTF-8 sequence. The +  // three byte UTF-8 sequence could be one half of a surrogate pair. +  switch (leading >> 8) {      case 0x00:        // It's only valid if it's above the ISO-8859-1 high space (0xa0).        return (leading > 0x00a0); @@ -842,9 +849,14 @@ bool IsValidPartOfMemberNameUtf8Slow(const char** pUtf8Ptr) {      case 0xd9:      case 0xda:      case 0xdb: -      // It looks like a leading surrogate but we didn't find a trailing -      // surrogate if we're here. -      return false; +      { +        // We found a three byte sequence encoding one half of a surrogate. +        // Look for the other half. +        const uint32_t pair2 = GetUtf16FromUtf8(pUtf8Ptr); +        const uint16_t trailing = GetLeadingUtf16Char(pair2); + +        return (GetTrailingUtf16Char(pair2) == 0) && (0xdc00 <= trailing && trailing <= 0xdfff); +      }      case 0xdc:      case 0xdd:      case 0xde: @@ -855,21 +867,19 @@ bool IsValidPartOfMemberNameUtf8Slow(const char** pUtf8Ptr) {      case 0xff:        // It's in the range that has spaces, controls, and specials.        switch (leading & 0xfff8) { -      case 0x2000: -      case 0x2008: -      case 0x2028: -      case 0xfff0: -      case 0xfff8: -        return false; +        case 0x2000: +        case 0x2008: +        case 0x2028: +        case 0xfff0: +        case 0xfff8: +          return false;        } -      break; -    } - -    return true; +      return true; +    default: +      return true;    } -  // We have a surrogate pair. Check that trailing surrogate is well formed. -  return (trailing >= 0xdc00 && trailing <= 0xdfff); +  UNREACHABLE();  }  /* Return whether the pointed-at modified-UTF-8 encoded character is diff --git a/runtime/utils.h b/runtime/utils.h index eaafcf0a64..71ccf85277 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -604,6 +604,11 @@ std::unique_ptr<T> MakeUnique(Args&& ... args) {    return std::unique_ptr<T>(new T(std::forward<Args>(args)...));  } +inline bool TestBitmap(size_t idx, const uint8_t* bitmap) { +  return ((bitmap[idx / kBitsPerByte] >> (idx % kBitsPerByte)) & 0x01) != 0; +} + +  }  // namespace art  #endif  // ART_RUNTIME_UTILS_H_ diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc index 195de0c121..869d305120 100644 --- a/runtime/utils_test.cc +++ b/runtime/utils_test.cc @@ -521,4 +521,27 @@ TEST_F(UtilsTest, TestSleep) {    EXPECT_GT(NanoTime() - start, MsToNs(1000));  } +TEST_F(UtilsTest, IsValidDescriptor) { +  std::vector<uint8_t> descriptor( +      { 'L', 'a', '/', 'b', '$', 0xed, 0xa0, 0x80, 0xed, 0xb0, 0x80, ';', 0x00 }); +  EXPECT_TRUE(IsValidDescriptor(reinterpret_cast<char*>(&descriptor[0]))); + +  std::vector<uint8_t> unpaired_surrogate( +      { 'L', 'a', '/', 'b', '$', 0xed, 0xa0, 0x80, ';', 0x00 }); +  EXPECT_FALSE(IsValidDescriptor(reinterpret_cast<char*>(&unpaired_surrogate[0]))); + +  std::vector<uint8_t> unpaired_surrogate_at_end( +      { 'L', 'a', '/', 'b', '$', 0xed, 0xa0, 0x80, 0x00 }); +  EXPECT_FALSE(IsValidDescriptor(reinterpret_cast<char*>(&unpaired_surrogate_at_end[0]))); + +  std::vector<uint8_t> invalid_surrogate( +      { 'L', 'a', '/', 'b', '$', 0xed, 0xb0, 0x80, ';', 0x00 }); +  EXPECT_FALSE(IsValidDescriptor(reinterpret_cast<char*>(&invalid_surrogate[0]))); + +  std::vector<uint8_t> unpaired_surrogate_with_multibyte_sequence( +      { 'L', 'a', '/', 'b', '$', 0xed, 0xb0, 0x80, 0xf0, 0x9f, 0x8f, 0xa0, ';', 0x00 }); +  EXPECT_FALSE( +      IsValidDescriptor(reinterpret_cast<char*>(&unpaired_surrogate_with_multibyte_sequence[0]))); +} +  }  // namespace art diff --git a/test/082-inline-execute/src/Main.java b/test/082-inline-execute/src/Main.java index 0e90c4d6a2..f2b8e60976 100644 --- a/test/082-inline-execute/src/Main.java +++ b/test/082-inline-execute/src/Main.java @@ -236,15 +236,6 @@ public class Main {      String str10 = "abcdefghij";      String str40 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabc"; -    int supplementaryChar = 0x20b9f; -    String surrogatePair = "\ud842\udf9f"; -    String stringWithSurrogates = "hello " + surrogatePair + " world"; - -    Assert.assertEquals(stringWithSurrogates.indexOf(supplementaryChar), "hello ".length()); -    Assert.assertEquals(stringWithSurrogates.indexOf(supplementaryChar, 2), "hello ".length()); -    Assert.assertEquals(stringWithSurrogates.indexOf(supplementaryChar, 6), 6); -    Assert.assertEquals(stringWithSurrogates.indexOf(supplementaryChar, 7), -1); -      Assert.assertEquals(str0.indexOf('a'), -1);      Assert.assertEquals(str3.indexOf('a'), 0);      Assert.assertEquals(str3.indexOf('b'), 1); @@ -269,24 +260,120 @@ public class Main {      Assert.assertEquals(str40.indexOf('a',10), 10);      Assert.assertEquals(str40.indexOf('b',40), -1); +    testIndexOfNull(); + +    // Same data as above, but stored so it's not a literal in the next test. -2 stands for +    // indexOf(I) instead of indexOf(II). +    start--; +    int[][] searchData = { +        { 'a', -2, -1 }, +        { 'a', -2, 0 }, +        { 'b', -2, 1 }, +        { 'c', -2, 2 }, +        { 'j', -2, 9 }, +        { 'a', -2, 0 }, +        { 'b', -2, 38 }, +        { 'c', -2, 39 }, +        { 'a', 20, -1 }, +        { 'a', 0, -1 }, +        { 'a', -1, -1 }, +        { '/', ++start, -1 }, +        { 'a', negIndex[0], -1 }, +        { 'a', 0, 0 }, +        { 'a', 1, -1 }, +        { 'a', 1234, -1 }, +        { 'b', 0, 1 }, +        { 'b', 1, 1 }, +        { 'c', 2, 2 }, +        { 'j', 5, 9 }, +        { 'j', 9, 9 }, +        { 'a', 10, 10 }, +        { 'b', 40, -1 }, +    }; +    testStringIndexOfChars(searchData); + +    testSurrogateIndexOf(); +  } + +  private static void testStringIndexOfChars(int[][] searchData) { +    // Use a try-catch to avoid inlining. +    try { +      testStringIndexOfCharsImpl(searchData); +    } catch (Exception e) { +      System.out.println("Unexpected exception"); +    } +  } + +  private static void testStringIndexOfCharsImpl(int[][] searchData) { +    String str0 = ""; +    String str1 = "/"; +    String str3 = "abc"; +    String str10 = "abcdefghij"; +    String str40 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabc"; + +    Assert.assertEquals(str0.indexOf(searchData[0][0]), searchData[0][2]); +    Assert.assertEquals(str3.indexOf(searchData[1][0]), searchData[1][2]); +    Assert.assertEquals(str3.indexOf(searchData[2][0]), searchData[2][2]); +    Assert.assertEquals(str3.indexOf(searchData[3][0]), searchData[3][2]); +    Assert.assertEquals(str10.indexOf(searchData[4][0]), searchData[4][2]); +    Assert.assertEquals(str40.indexOf(searchData[5][0]), searchData[5][2]); +    Assert.assertEquals(str40.indexOf(searchData[6][0]), searchData[6][2]); +    Assert.assertEquals(str40.indexOf(searchData[7][0]), searchData[7][2]); +    Assert.assertEquals(str0.indexOf(searchData[8][0], searchData[8][1]), searchData[8][2]); +    Assert.assertEquals(str0.indexOf(searchData[9][0], searchData[9][1]), searchData[9][2]); +    Assert.assertEquals(str0.indexOf(searchData[10][0], searchData[10][1]), searchData[10][2]); +    Assert.assertEquals(str1.indexOf(searchData[11][0], searchData[11][1]), searchData[11][2]); +    Assert.assertEquals(str1.indexOf(searchData[12][0], searchData[12][1]), searchData[12][2]); +    Assert.assertEquals(str3.indexOf(searchData[13][0], searchData[13][1]), searchData[13][2]); +    Assert.assertEquals(str3.indexOf(searchData[14][0], searchData[14][1]), searchData[14][2]); +    Assert.assertEquals(str3.indexOf(searchData[15][0], searchData[15][1]), searchData[15][2]); +    Assert.assertEquals(str3.indexOf(searchData[16][0], searchData[16][1]), searchData[16][2]); +    Assert.assertEquals(str3.indexOf(searchData[17][0], searchData[17][1]), searchData[17][2]); +    Assert.assertEquals(str3.indexOf(searchData[18][0], searchData[18][1]), searchData[18][2]); +    Assert.assertEquals(str10.indexOf(searchData[19][0], searchData[19][1]), searchData[19][2]); +    Assert.assertEquals(str10.indexOf(searchData[20][0], searchData[20][1]), searchData[20][2]); +    Assert.assertEquals(str40.indexOf(searchData[21][0], searchData[21][1]), searchData[21][2]); +    Assert.assertEquals(str40.indexOf(searchData[22][0], searchData[22][1]), searchData[22][2]); +  } + +  private static void testSurrogateIndexOf() { +    int supplementaryChar = 0x20b9f; +    String surrogatePair = "\ud842\udf9f"; +    String stringWithSurrogates = "hello " + surrogatePair + " world"; + +    Assert.assertEquals(stringWithSurrogates.indexOf(supplementaryChar), "hello ".length()); +    Assert.assertEquals(stringWithSurrogates.indexOf(supplementaryChar, 2), "hello ".length()); +    Assert.assertEquals(stringWithSurrogates.indexOf(supplementaryChar, 6), 6); +    Assert.assertEquals(stringWithSurrogates.indexOf(supplementaryChar, 7), -1); +  } + +  private static void testIndexOfNull() {      String strNull = null;      try { -      strNull.indexOf('a'); +      testNullIndex(strNull, 'a');        Assert.fail();      } catch (NullPointerException expected) {      }      try { -      strNull.indexOf('a', 0); +      testNullIndex(strNull, 'a', 0);        Assert.fail();      } catch (NullPointerException expected) {      }      try { -      strNull.indexOf('a', -1); +        testNullIndex(strNull, 'a', -1);        Assert.fail();      } catch (NullPointerException expected) {      }    } +  private static int testNullIndex(String strNull, int c) { +    return strNull.indexOf(c); +  } + +  private static int testNullIndex(String strNull, int c, int startIndex) { +    return strNull.indexOf(c, startIndex); +  } +    public static void test_String_compareTo() {      String test = "0123456789";      String test1 = new String("0123456789");    // different object diff --git a/test/431-optimizing-arith-shifts/src/Main.java b/test/431-optimizing-arith-shifts/src/Main.java index d8667c63c5..86422bd8e7 100644 --- a/test/431-optimizing-arith-shifts/src/Main.java +++ b/test/431-optimizing-arith-shifts/src/Main.java @@ -52,7 +52,7 @@ public class Main {      expectEquals(Integer.MIN_VALUE, $opt$Shl(1073741824, 1));  // overflow      expectEquals(1073741824, $opt$Shl(268435456, 2)); -   // othe nly 5 lower bits should be used for shifting (& 0x1f). +    // Only the 5 lower bits should be used for shifting (& 0x1f).      expectEquals(7, $opt$Shl(7, 32));  // 32 & 0x1f = 0      expectEquals(14, $opt$Shl(7, 33));  // 33 & 0x1f = 1      expectEquals(32, $opt$Shl(1, 101));  // 101 & 0x1f = 5 @@ -97,6 +97,13 @@ public class Main {      expectEquals(Long.MIN_VALUE, $opt$Shl(7L, Long.MAX_VALUE));      expectEquals(7L, $opt$Shl(7L, Long.MIN_VALUE)); + +    // Exercise some special cases handled by backends/simplifier. +    expectEquals(24L, $opt$ShlConst1(12L)); +    expectEquals(0x2345678900000000L, $opt$ShlConst32(0x123456789L)); +    expectEquals(0x2490249000000000L, $opt$ShlConst33(0x12481248L)); +    expectEquals(0x4920492000000000L, $opt$ShlConst34(0x12481248L)); +    expectEquals(0x9240924000000000L, $opt$ShlConst35(0x12481248L));    }    private static void shrInt() { @@ -277,7 +284,7 @@ public class Main {      return a >>> 2L;    } -    static int $opt$ShlConst0(int a) { +  static int $opt$ShlConst0(int a) {      return a << 0;    } @@ -301,5 +308,25 @@ public class Main {      return a >>> 0L;    } +  static long $opt$ShlConst1(long a) { +    return a << 1L; +  } + +  static long $opt$ShlConst32(long a) { +    return a << 32L; +  } + +  static long $opt$ShlConst33(long a) { +    return a << 33L; +  } + +  static long $opt$ShlConst34(long a) { +    return a << 34L; +  } + +  static long $opt$ShlConst35(long a) { +    return a << 35L; +  } +  } diff --git a/test/454-get-vreg/get_vreg_jni.cc b/test/454-get-vreg/get_vreg_jni.cc index 6b4bc11086..0ef2964e35 100644 --- a/test/454-get-vreg/get_vreg_jni.cc +++ b/test/454-get-vreg/get_vreg_jni.cc @@ -29,7 +29,9 @@ class TestVisitor : public StackVisitor {   public:    TestVisitor(Thread* thread, Context* context, mirror::Object* this_value)        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) -      : StackVisitor(thread, context), this_value_(this_value), found_method_index_(0) {} +      : StackVisitor(thread, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames), +        this_value_(this_value), +        found_method_index_(0) {}    bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {      mirror::ArtMethod* m = GetMethod(); diff --git a/test/455-set-vreg/set_vreg_jni.cc b/test/455-set-vreg/set_vreg_jni.cc index 0a83ac0738..dffbfa47d8 100644 --- a/test/455-set-vreg/set_vreg_jni.cc +++ b/test/455-set-vreg/set_vreg_jni.cc @@ -29,7 +29,8 @@ class TestVisitor : public StackVisitor {   public:    TestVisitor(Thread* thread, Context* context, mirror::Object* this_value)        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) -      : StackVisitor(thread, context), this_value_(this_value) {} +      : StackVisitor(thread, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames), +        this_value_(this_value) {}    bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {      mirror::ArtMethod* m = GetMethod(); diff --git a/test/457-regs/regs_jni.cc b/test/457-regs/regs_jni.cc index 1b32348e25..193ab9dc4e 100644 --- a/test/457-regs/regs_jni.cc +++ b/test/457-regs/regs_jni.cc @@ -29,7 +29,7 @@ class TestVisitor : public StackVisitor {   public:    TestVisitor(Thread* thread, Context* context)        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) -      : StackVisitor(thread, context) {} +      : StackVisitor(thread, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames) {}    bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {      mirror::ArtMethod* m = GetMethod(); diff --git a/test/458-checker-instruction-simplification/src/Main.java b/test/458-checker-instruction-simplification/src/Main.java index 5d5a6b3627..efb7b83e33 100644 --- a/test/458-checker-instruction-simplification/src/Main.java +++ b/test/458-checker-instruction-simplification/src/Main.java @@ -223,6 +223,24 @@ public class Main {      return arg << 0;    } +  // CHECK-START: int Main.Shl1(int) instruction_simplifier (before) +  // CHECK-DAG:     [[Arg:i\d+]]      ParameterValue +  // CHECK-DAG:     [[Const1:i\d+]]   IntConstant 1 +  // CHECK-DAG:     [[Shl:i\d+]]      Shl [ [[Arg]] [[Const1]] ] +  // CHECK-DAG:                       Return [ [[Shl]] ] + +  // CHECK-START: int Main.Shl1(int) instruction_simplifier (after) +  // CHECK-DAG:     [[Arg:i\d+]]      ParameterValue +  // CHECK-DAG:     [[Add:i\d+]]      Add [ [[Arg]] [[Arg]] ] +  // CHECK-DAG:                       Return [ [[Add]] ] + +  // CHECK-START: int Main.Shl1(int) instruction_simplifier (after) +  // CHECK-NOT:                       Shl + +  public static int Shl1(int arg) { +    return arg << 1; +  } +    // CHECK-START: long Main.Shr0(long) instruction_simplifier (before)    // CHECK-DAG:     [[Arg:j\d+]]      ParameterValue    // CHECK-DAG:     [[Const0:i\d+]]   IntConstant 0 @@ -1060,5 +1078,6 @@ public class Main {      assertDoubleEquals(Div2(150.0), 75.0);      assertFloatEquals(DivMP25(100.0f), -400.0f);      assertDoubleEquals(DivMP25(150.0), -600.0); +    assertLongEquals(Shl1(100), 200);    }  } diff --git a/test/461-get-reference-vreg/get_reference_vreg_jni.cc b/test/461-get-reference-vreg/get_reference_vreg_jni.cc index f0b78e1f5e..a8ef684e93 100644 --- a/test/461-get-reference-vreg/get_reference_vreg_jni.cc +++ b/test/461-get-reference-vreg/get_reference_vreg_jni.cc @@ -29,7 +29,9 @@ class TestVisitor : public StackVisitor {   public:    TestVisitor(Thread* thread, Context* context, mirror::Object* this_value)        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) -      : StackVisitor(thread, context), this_value_(this_value), found_method_index_(0) {} +      : StackVisitor(thread, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames), +        this_value_(this_value), +        found_method_index_(0) {}    bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {      mirror::ArtMethod* m = GetMethod(); diff --git a/test/466-get-live-vreg/get_live_vreg_jni.cc b/test/466-get-live-vreg/get_live_vreg_jni.cc index 6715ba17e6..4724e8ebe4 100644 --- a/test/466-get-live-vreg/get_live_vreg_jni.cc +++ b/test/466-get-live-vreg/get_live_vreg_jni.cc @@ -28,7 +28,7 @@ namespace {  class TestVisitor : public StackVisitor {   public:    TestVisitor(Thread* thread, Context* context) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) -      : StackVisitor(thread, context) {} +      : StackVisitor(thread, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames) {}    bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {      mirror::ArtMethod* m = GetMethod(); diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index c7e68775cc..40f5f00b8f 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -385,7 +385,6 @@ TEST_ART_BROKEN_OPTIMIZING_ARM64_RUN_TESTS :=  # Known broken tests for the optimizing compiler.  TEST_ART_BROKEN_OPTIMIZING_RUN_TESTS := -TEST_ART_BROKEN_OPTIMIZING_RUN_TESTS += 472-unreachable-if-regression # b/19988134  ifneq (,$(filter optimizing,$(COMPILER_TYPES)))    ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ @@ -428,6 +427,17 @@ endif  TEST_ART_BROKEN_OPTIMIZING_DEBUGGABLE_RUN_TESTS := +# Tests that should fail in the read barrier configuration. +TEST_ART_BROKEN_READ_BARRIER_RUN_TESTS := \ +  098-ddmc  # b/20720510 + +ifeq ($(ART_USE_READ_BARRIER),true) +  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_READ_BARRIER_RUN_TESTS),$(ALL_ADDRESS_SIZES)) +endif + +TEST_ART_BROKEN_READ_BARRIER_RUN_TESTS :=  # Clear variables ahead of appending to them when defining tests.  $(foreach target, $(TARGET_TYPES), $(eval ART_RUN_TEST_$(call name-to-var,$(target))_RULES :=)) diff --git a/test/Instrumentation/Instrumentation.java b/test/Instrumentation/Instrumentation.java new file mode 100644 index 0000000000..09d434213b --- /dev/null +++ b/test/Instrumentation/Instrumentation.java @@ -0,0 +1,22 @@ +/* + * 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. + */ + +public class Instrumentation { +  // Direct method +  private void instanceMethod() { +    System.out.println("instanceMethod"); +  } +} @@ -95,6 +95,7 @@ ANDROID_DATA=$ANDROID_DATA \    PATH=$ANDROID_ROOT/bin:$PATH \    $invoke_with $ANDROID_ROOT/bin/$DALVIKVM $lib \      -XXlib:$LIBART \ +    -Xnorelocate \      -Ximage:$ANDROID_ROOT/framework/core.art \      -Xcompiler-option --include-debug-symbols \      "$@" diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh index a007fa2171..1dd443b2bc 100755 --- a/tools/run-jdwp-tests.sh +++ b/tools/run-jdwp-tests.sh @@ -19,11 +19,6 @@ if [ ! -d libcore ]; then    exit 1  fi -if [[ $ANDROID_SERIAL == HT4CTJT03670 ]] || [[ $ANDROID_SERIAL == HT49CJT00070 ]]; then -  echo "Not running on buildbot because of failures on volantis. Investigating." -  exit 0 -fi -  # Jar containing all the tests.  test_jar=out/host/linux-x86/framework/apache-harmony-jdwp-tests-hostdex.jar  junit_jar=out/host/linux-x86/framework/junit.jar @@ -79,7 +74,7 @@ vogar $vm_command \        $args \        $device_dir \        $image_compiler_option \ -      --timeout 600 \ +      --timeout 800 \        --vm-arg -Djpda.settings.verbose=true \        --vm-arg -Djpda.settings.syncPort=34016 \        --vm-arg -Djpda.settings.transportAddress=127.0.0.1:55107 \  |