diff --git a/runtime/vm/block_scheduler.cc b/runtime/vm/block_scheduler.cc index 2594e8aff18..27d2a9e6fe8 100644 --- a/runtime/vm/block_scheduler.cc +++ b/runtime/vm/block_scheduler.cc @@ -5,6 +5,7 @@ #include "vm/block_scheduler.h" #include "vm/allocation.h" +#include "vm/code_patcher.h" #include "vm/flow_graph.h" namespace dart { @@ -14,25 +15,9 @@ static intptr_t ComputeEdgeCount(const Code& unoptimized_code, intptr_t deopt_id) { ASSERT(deopt_id != Isolate::kNoDeoptId); - // Intrinsified functions do not have edge counts, so give all edges equal - // weights. - if (unoptimized_code.pointer_offsets_length() == 0) return 1; - uword pc = unoptimized_code.GetPcForDeoptId(deopt_id, PcDescriptors::kDeopt); Array& array = Array::Handle(); - // Pointer offsets are sorted in decreasing order. Find the first one - // after the deopt id's pc. - // TODO(kmillikin): Use a more reliable way to find the counter. - for (intptr_t j = unoptimized_code.pointer_offsets_length() - 1; - j >= 0; - --j) { - uword addr = - unoptimized_code.GetPointerOffsetAt(j) + unoptimized_code.EntryPoint(); - if (addr > pc) { - array ^= *reinterpret_cast(addr); - break; - } - } + array ^= CodePatcher::GetEdgeCounterAt(pc, unoptimized_code); ASSERT(!array.IsNull()); return Smi::Value(Smi::RawCast(array.At(0))); } @@ -208,7 +193,7 @@ void BlockScheduler::ReorderBlocks() const { for (intptr_t i = block_count - 1; i >= 0; --i) { if (chains[i]->first->block == flow_graph()->postorder()[i]) { for (Link* link = chains[i]->first; link != NULL; link = link->next) { - flow_graph()->codegen_block_order(true)->Add(link->block); + flow_graph()->CodegenBlockOrder(true)->Add(link->block); } } } diff --git a/runtime/vm/code_patcher.h b/runtime/vm/code_patcher.h index fea3ba41ff8..9294e0e4f16 100644 --- a/runtime/vm/code_patcher.h +++ b/runtime/vm/code_patcher.h @@ -19,6 +19,7 @@ class ICData; class RawArray; class RawFunction; class RawICData; +class RawObject; class String; class CodePatcher : public AllStatic { @@ -70,6 +71,8 @@ class CodePatcher : public AllStatic { static intptr_t InstanceCallSizeInBytes(); static void InsertCallAt(uword start, uword target); + + static RawObject* GetEdgeCounterAt(uword pc, const Code& code); }; } // namespace dart diff --git a/runtime/vm/code_patcher_arm.cc b/runtime/vm/code_patcher_arm.cc index 105d3a1d374..759bf54dd58 100644 --- a/runtime/vm/code_patcher_arm.cc +++ b/runtime/vm/code_patcher_arm.cc @@ -84,6 +84,47 @@ RawFunction* CodePatcher::GetUnoptimizedStaticCallAt( return ic_data.GetTargetAt(0); } + +// This class pattern matches on a load from the object pool. Loading on +// ARM is complicated because it can take four possible different forms. We +// match backwards from the end of the sequence so we can reuse the code for +// matching object pool loads at calls. +class EdgeCounter : public ValueObject { + public: + EdgeCounter(uword pc, const Code& code) + : end_(pc - kAdjust), object_pool_(Array::Handle(code.ObjectPool())) { + // An IsValid predicate is complicated and duplicates the code in the + // decoding function. Instead we rely on decoding the pattern which + // will assert partial validity. + } + + RawObject* edge_counter() const { + Register ignored; + intptr_t index; + InstructionPattern::DecodeLoadWordFromPool(end_, &ignored, &index); + ASSERT(ignored == R0); + return object_pool_.At(index); + } + + private: + // The object pool load is followed by the fixed-size edge counter + // incrementing code: + // ldr ip, [r0, #+11] + // adds ip, ip, #2 + // str ip, [r0, #+11] + static const intptr_t kAdjust = 3 * Instr::kInstrSize; + + uword end_; + const Array& object_pool_; +}; + + +RawObject* CodePatcher::GetEdgeCounterAt(uword pc, const Code& code) { + ASSERT(code.ContainsInstructionAt(pc)); + EdgeCounter counter(pc, code); + return counter.edge_counter(); +} + } // namespace dart #endif // defined TARGET_ARCH_ARM diff --git a/runtime/vm/code_patcher_ia32.cc b/runtime/vm/code_patcher_ia32.cc index 9cccd56c196..8143d698251 100644 --- a/runtime/vm/code_patcher_ia32.cc +++ b/runtime/vm/code_patcher_ia32.cc @@ -149,7 +149,7 @@ class StaticCall : public ValueObject { }; -// The expected pattern of a dart closure call: +// The expected pattern of a Dart closure call: // mov EDX, arguments_descriptor_array // call target_address // <- return address @@ -259,6 +259,39 @@ intptr_t CodePatcher::InstanceCallSizeInBytes() { return InstanceCall::kNumInstructions * InstanceCall::kInstructionSize; } + +// The expected code pattern of an edge counter in unoptimized code: +// b8 imm32 mov EAX, immediate +class EdgeCounter : public ValueObject { + public: + EdgeCounter(uword pc, const Code& ignored) : end_(pc - kAdjust) { + ASSERT(IsValid(end_)); + } + + static bool IsValid(uword end) { + return (*reinterpret_cast(end - 5) == 0xb8); + } + + RawObject* edge_counter() const { + return *reinterpret_cast(end_ - 4); + } + + private: + // The edge counter load is followed by the fixed-size edge counter + // incrementing code: + // 83 40 0b 02 add [eax+0xb],0x2 + static const intptr_t kAdjust = 4; + + uword end_; +}; + + +RawObject* CodePatcher::GetEdgeCounterAt(uword pc, const Code& code) { + ASSERT(code.ContainsInstructionAt(pc)); + EdgeCounter counter(pc, code); + return counter.edge_counter(); +} + } // namespace dart #endif // defined TARGET_ARCH_IA32 diff --git a/runtime/vm/code_patcher_mips.cc b/runtime/vm/code_patcher_mips.cc index f25b4602ef8..7a845961be1 100644 --- a/runtime/vm/code_patcher_mips.cc +++ b/runtime/vm/code_patcher_mips.cc @@ -84,6 +84,47 @@ RawFunction* CodePatcher::GetUnoptimizedStaticCallAt( return ic_data.GetTargetAt(0); } + +// This class pattern matches on a load from the object pool. Loading on +// MIPS is complicated because it can take four possible different forms. +// We match backwards from the end of the sequence so we can reuse the code +// for matching object pool loads at calls. +class EdgeCounter : public ValueObject { + public: + EdgeCounter(uword pc, const Code& code) + : end_(pc - kAdjust), object_pool_(Array::Handle(code.ObjectPool())) { + // An IsValid predicate is complicated and duplicates the code in the + // decoding function. Instead we rely on decoding the pattern which + // will assert partial validity. + } + + RawObject* edge_counter() const { + Register ignored; + intptr_t index; + InstructionPattern::DecodeLoadWordFromPool(end_, &ignored, &index); + ASSERT(ignored == T0); + return object_pool_.At(index); + } + + private: + // The object pool load is followed by the fixed-size edge counter + // incrementing code: + // lw r9, 11(r8) + // addiu r9, r9, 2 + // sw r9, 11(r8) + static const intptr_t kAdjust = 3 * Instr::kInstrSize; + + uword end_; + const Array& object_pool_; +}; + + +RawObject* CodePatcher::GetEdgeCounterAt(uword pc, const Code& code) { + ASSERT(code.ContainsInstructionAt(pc)); + EdgeCounter counter(pc, code); + return counter.edge_counter(); +} + } // namespace dart #endif // defined TARGET_ARCH_MIPS diff --git a/runtime/vm/code_patcher_x64.cc b/runtime/vm/code_patcher_x64.cc index 5782527c6f7..67beae77eaa 100644 --- a/runtime/vm/code_patcher_x64.cc +++ b/runtime/vm/code_patcher_x64.cc @@ -142,7 +142,7 @@ class StaticCall : public ValueObject { }; -// The expected code pattern of a dart closure call: +// The expected code pattern of a Dart closure call: // 00: 49 ba imm64 mov R10, immediate 2 ; 10 bytes // 10: 4d 8b 9f imm32 mov R11, [PP + off] // 17: 41 ff d3 call R11 ; 3 bytes @@ -248,6 +248,43 @@ RawFunction* CodePatcher::GetUnoptimizedStaticCallAt( return ic_data.GetTargetAt(0); } + +// The expected code pattern of an edge counter in unoptimized code: +// 49 8b 87 imm32 mov RAX, [PP + offset] +class EdgeCounter : public ValueObject { + public: + EdgeCounter(uword pc, const Code& code) + : end_(pc - kAdjust), object_pool_(Array::Handle(code.ObjectPool())) { + ASSERT(IsValid(end_)); + } + + static bool IsValid(uword end) { + uint8_t* bytes = reinterpret_cast(end - 7); + return (bytes[0] == 0x49) && (bytes[1] == 0x8b) && (bytes[2] == 0x87); + } + + RawObject* edge_counter() const { + return object_pool_.At(InstructionPattern::IndexFromPPLoad(end_ - 4)); + } + + private: + // The edge counter load is followed by the fixed-size edge counter + // incrementing code: + // 49 c7 c3 02 00 00 00 movq r11,0x2 + // 4c 01 58 17 addq [rax+0x17],r11 + static const intptr_t kAdjust = 11; + + uword end_; + const Array& object_pool_; +}; + + +RawObject* CodePatcher::GetEdgeCounterAt(uword pc, const Code& code) { + ASSERT(code.ContainsInstructionAt(pc)); + EdgeCounter counter(pc, code); + return counter.edge_counter(); +} + } // namespace dart #endif // defined TARGET_ARCH_X64 diff --git a/runtime/vm/compiler.cc b/runtime/vm/compiler.cc index a4ab7b5c172..5a9ff9d56fc 100644 --- a/runtime/vm/compiler.cc +++ b/runtime/vm/compiler.cc @@ -307,7 +307,9 @@ static bool CompileParsedFunctionHelper(ParsedFunction* parsed_function, } BlockScheduler block_scheduler(flow_graph); - if (optimized && FLAG_reorder_basic_blocks) { + const bool reorder_blocks = + FlowGraph::ShouldReorderBlocks(function, optimized); + if (reorder_blocks) { block_scheduler.AssignEdgeWeights(); } @@ -508,7 +510,7 @@ static bool CompileParsedFunctionHelper(ParsedFunction* parsed_function, // Perform register allocation on the SSA graph. FlowGraphAllocator allocator(*flow_graph); allocator.AllocateRegisters(); - if (FLAG_reorder_basic_blocks) block_scheduler.ReorderBlocks(); + if (reorder_blocks) block_scheduler.ReorderBlocks(); if (FLAG_print_flow_graph || FLAG_print_flow_graph_optimized) { FlowGraphPrinter::PrintGraph("After Optimizations", flow_graph); diff --git a/runtime/vm/flow_graph.cc b/runtime/vm/flow_graph.cc index 0f6dce32ab7..b39671b4e83 100644 --- a/runtime/vm/flow_graph.cc +++ b/runtime/vm/flow_graph.cc @@ -60,9 +60,15 @@ void FlowGraph::AddToGuardedFields( } -GrowableArray* FlowGraph::codegen_block_order( +bool FlowGraph::ShouldReorderBlocks(const Function& function, + bool is_optimized) { + return is_optimized && FLAG_reorder_basic_blocks && !function.is_intrinsic(); +} + + +GrowableArray* FlowGraph::CodegenBlockOrder( bool is_optimized) { - return (is_optimized && FLAG_reorder_basic_blocks) + return ShouldReorderBlocks(parsed_function().function(), is_optimized) ? &optimized_block_order_ : &reverse_postorder_; } diff --git a/runtime/vm/flow_graph.h b/runtime/vm/flow_graph.h index db384d0f337..517a9ebce26 100644 --- a/runtime/vm/flow_graph.h +++ b/runtime/vm/flow_graph.h @@ -82,7 +82,8 @@ class FlowGraph : public ZoneAllocated { const GrowableArray& reverse_postorder() const { return reverse_postorder_; } - GrowableArray* codegen_block_order(bool is_optimized); + static bool ShouldReorderBlocks(const Function& function, bool is_optimized); + GrowableArray* CodegenBlockOrder(bool is_optimized); // Iterators. BlockIterator reverse_postorder_iterator() const { diff --git a/runtime/vm/flow_graph_compiler.cc b/runtime/vm/flow_graph_compiler.cc index 42df32d1531..291471173f7 100644 --- a/runtime/vm/flow_graph_compiler.cc +++ b/runtime/vm/flow_graph_compiler.cc @@ -69,7 +69,7 @@ FlowGraphCompiler::FlowGraphCompiler(Assembler* assembler, : assembler_(assembler), parsed_function_(flow_graph->parsed_function()), flow_graph_(*flow_graph), - block_order_(*flow_graph->codegen_block_order(is_optimizing)), + block_order_(*flow_graph->CodegenBlockOrder(is_optimizing)), current_block_(NULL), exception_handlers_list_(NULL), pc_descriptors_list_(NULL), @@ -225,6 +225,31 @@ void FlowGraphCompiler::CompactBlocks() { } +void FlowGraphCompiler::EmitInstructionPrologue(Instruction* instr) { + if (!is_optimizing()) { + if (FLAG_enable_type_checks && instr->IsAssertAssignable()) { + AssertAssignableInstr* assert = instr->AsAssertAssignable(); + AddCurrentDescriptor(PcDescriptors::kDeopt, + assert->deopt_id(), + assert->token_pos()); + } else if (instr->IsGuardField() || + (instr->CanBecomeDeoptimizationTarget() && !instr->IsGoto())) { + // GuardField and instructions that can be deoptimization targets need + // to record their deopt id. GotoInstr records its own so that it can + // control the placement. + AddCurrentDescriptor(PcDescriptors::kDeopt, + instr->deopt_id(), + Scanner::kDummyTokenIndex); + } + AllocateRegistersLocally(instr); + } else if (instr->MayThrow() && + (CurrentTryIndex() != CatchClauseNode::kInvalidTryIndex)) { + // Optimized try-block: Sync locals to fixed stack locations. + EmitTrySync(instr, CurrentTryIndex()); + } +} + + void FlowGraphCompiler::VisitBlocks() { CompactBlocks(); diff --git a/runtime/vm/flow_graph_compiler.h b/runtime/vm/flow_graph_compiler.h index f53ad0f7109..00f31ec8408 100644 --- a/runtime/vm/flow_graph_compiler.h +++ b/runtime/vm/flow_graph_compiler.h @@ -340,6 +340,8 @@ class FlowGraphCompiler : public ValueObject { void EmitComment(Instruction* instr); + void EmitEdgeCounter(); + void EmitOptimizedInstanceCall(ExternalLabel* target_label, const ICData& ic_data, intptr_t argument_count, diff --git a/runtime/vm/flow_graph_compiler_arm.cc b/runtime/vm/flow_graph_compiler_arm.cc index c07fefbfd8e..fded60aac2b 100644 --- a/runtime/vm/flow_graph_compiler_arm.cc +++ b/runtime/vm/flow_graph_compiler_arm.cc @@ -720,28 +720,6 @@ void FlowGraphCompiler::GenerateAssertAssignable(intptr_t token_pos, } -void FlowGraphCompiler::EmitInstructionPrologue(Instruction* instr) { - if (!is_optimizing()) { - if (FLAG_enable_type_checks && instr->IsAssertAssignable()) { - AssertAssignableInstr* assert = instr->AsAssertAssignable(); - AddCurrentDescriptor(PcDescriptors::kDeopt, - assert->deopt_id(), - assert->token_pos()); - } else if (instr->IsGuardField() || - instr->CanBecomeDeoptimizationTarget()) { - AddCurrentDescriptor(PcDescriptors::kDeopt, - instr->deopt_id(), - Scanner::kDummyTokenIndex); - } - AllocateRegistersLocally(instr); - } else if (instr->MayThrow() && - (CurrentTryIndex() != CatchClauseNode::kInvalidTryIndex)) { - // Optimized try-block: Sync locals to fixed stack locations. - EmitTrySync(instr, CurrentTryIndex()); - } -} - - void FlowGraphCompiler::EmitTrySyncMove(intptr_t dest_offset, Location loc, bool* push_emitted) { @@ -1275,6 +1253,22 @@ void FlowGraphCompiler::GenerateCallRuntime(intptr_t token_pos, } +void FlowGraphCompiler::EmitEdgeCounter() { + // We do not check for overflow when incrementing the edge counter. The + // function should normally be optimized long before the counter can + // overflow; and though we do not reset the counters when we optimize or + // deoptimize, there is a bound on the number of + // optimization/deoptimization cycles we will attempt. + const Array& counter = Array::ZoneHandle(Array::New(1, Heap::kOld)); + counter.SetAt(0, Smi::Handle(Smi::New(0))); + __ Comment("Edge counter"); + __ LoadObject(R0, counter); + __ ldr(IP, FieldAddress(R0, Array::element_offset(0))); + __ adds(IP, IP, ShifterOperand(Smi::RawValue(1))); + __ str(IP, FieldAddress(R0, Array::element_offset(0))); +} + + void FlowGraphCompiler::EmitOptimizedInstanceCall( ExternalLabel* target_label, const ICData& ic_data, diff --git a/runtime/vm/flow_graph_compiler_ia32.cc b/runtime/vm/flow_graph_compiler_ia32.cc index 2ea07d5a3a8..cfcde17ef82 100644 --- a/runtime/vm/flow_graph_compiler_ia32.cc +++ b/runtime/vm/flow_graph_compiler_ia32.cc @@ -742,28 +742,6 @@ void FlowGraphCompiler::GenerateAssertAssignable(intptr_t token_pos, } -void FlowGraphCompiler::EmitInstructionPrologue(Instruction* instr) { - if (!is_optimizing()) { - if (FLAG_enable_type_checks && instr->IsAssertAssignable()) { - AssertAssignableInstr* assert = instr->AsAssertAssignable(); - AddCurrentDescriptor(PcDescriptors::kDeopt, - assert->deopt_id(), - assert->token_pos()); - } else if (instr->IsGuardField() || - instr->CanBecomeDeoptimizationTarget()) { - AddCurrentDescriptor(PcDescriptors::kDeopt, - instr->deopt_id(), - Scanner::kDummyTokenIndex); - } - AllocateRegistersLocally(instr); - } else if (instr->MayThrow() && - (CurrentTryIndex() != CatchClauseNode::kInvalidTryIndex)) { - // Optimized try-block: Sync locals to fixed stack locations. - EmitTrySync(instr, CurrentTryIndex()); - } -} - - void FlowGraphCompiler::EmitTrySyncMove(intptr_t dest_offset, Location loc, bool* push_emitted) { @@ -1323,6 +1301,21 @@ void FlowGraphCompiler::EmitUnoptimizedStaticCall( } +void FlowGraphCompiler::EmitEdgeCounter() { + // We do not check for overflow when incrementing the edge counter. The + // function should normally be optimized long before the counter can + // overflow; and though we do not reset the counters when we optimize or + // deoptimize, there is a bound on the number of + // optimization/deoptimization cycles we will attempt. + const Array& counter = Array::ZoneHandle(Array::New(1, Heap::kOld)); + counter.SetAt(0, Smi::Handle(Smi::New(0))); + __ Comment("Edge counter"); + __ LoadObject(EAX, counter); + __ addl(FieldAddress(EAX, Array::element_offset(0)), + Immediate(Smi::RawValue(1))); +} + + void FlowGraphCompiler::EmitOptimizedInstanceCall( ExternalLabel* target_label, const ICData& ic_data, diff --git a/runtime/vm/flow_graph_compiler_mips.cc b/runtime/vm/flow_graph_compiler_mips.cc index 04f3eabc59e..e18577ec428 100644 --- a/runtime/vm/flow_graph_compiler_mips.cc +++ b/runtime/vm/flow_graph_compiler_mips.cc @@ -744,28 +744,6 @@ void FlowGraphCompiler::GenerateAssertAssignable(intptr_t token_pos, } -void FlowGraphCompiler::EmitInstructionPrologue(Instruction* instr) { - if (!is_optimizing()) { - if (FLAG_enable_type_checks && instr->IsAssertAssignable()) { - AssertAssignableInstr* assert = instr->AsAssertAssignable(); - AddCurrentDescriptor(PcDescriptors::kDeopt, - assert->deopt_id(), - assert->token_pos()); - } else if (instr->IsGuardField() || - instr->CanBecomeDeoptimizationTarget()) { - AddCurrentDescriptor(PcDescriptors::kDeopt, - instr->deopt_id(), - Scanner::kDummyTokenIndex); - } - AllocateRegistersLocally(instr); - } else if (instr->MayThrow() && - (CurrentTryIndex() != CatchClauseNode::kInvalidTryIndex)) { - // Optimized try-block: Sync locals to fixed stack locations. - EmitTrySync(instr, CurrentTryIndex()); - } -} - - void FlowGraphCompiler::EmitTrySyncMove(intptr_t dest_offset, Location loc, bool* push_emitted) { @@ -1319,6 +1297,22 @@ void FlowGraphCompiler::GenerateCallRuntime(intptr_t token_pos, } +void FlowGraphCompiler::EmitEdgeCounter() { + // We do not check for overflow when incrementing the edge counter. The + // function should normally be optimized long before the counter can + // overflow; and though we do not reset the counters when we optimize or + // deoptimize, there is a bound on the number of + // optimization/deoptimization cycles we will attempt. + const Array& counter = Array::ZoneHandle(Array::New(1, Heap::kOld)); + counter.SetAt(0, Smi::Handle(Smi::New(0))); + __ Comment("Edge counter"); + __ LoadObject(T0, counter); + __ lw(T1, FieldAddress(T0, Array::element_offset(0))); + __ AddImmediate(T1, T1, Smi::RawValue(1)); + __ sw(T1, FieldAddress(T0, Array::element_offset(0))); +} + + void FlowGraphCompiler::EmitOptimizedInstanceCall( ExternalLabel* target_label, const ICData& ic_data, diff --git a/runtime/vm/flow_graph_compiler_x64.cc b/runtime/vm/flow_graph_compiler_x64.cc index aeebe90a2b3..30787628fc4 100644 --- a/runtime/vm/flow_graph_compiler_x64.cc +++ b/runtime/vm/flow_graph_compiler_x64.cc @@ -724,28 +724,6 @@ void FlowGraphCompiler::GenerateAssertAssignable(intptr_t token_pos, } -void FlowGraphCompiler::EmitInstructionPrologue(Instruction* instr) { - if (!is_optimizing()) { - if (FLAG_enable_type_checks && instr->IsAssertAssignable()) { - AssertAssignableInstr* assert = instr->AsAssertAssignable(); - AddCurrentDescriptor(PcDescriptors::kDeopt, - assert->deopt_id(), - assert->token_pos()); - } else if (instr->IsGuardField() || - instr->CanBecomeDeoptimizationTarget()) { - AddCurrentDescriptor(PcDescriptors::kDeopt, - instr->deopt_id(), - Scanner::kDummyTokenIndex); - } - AllocateRegistersLocally(instr); - } else if (instr->MayThrow() && - (CurrentTryIndex() != CatchClauseNode::kInvalidTryIndex)) { - // Optimized try-block: Sync locals to fixed stack locations. - EmitTrySync(instr, CurrentTryIndex()); - } -} - - void FlowGraphCompiler::EmitTrySyncMove(intptr_t dest_offset, Location loc, bool* push_emitted) { @@ -1358,6 +1336,21 @@ void FlowGraphCompiler::EmitUnoptimizedStaticCall( } +void FlowGraphCompiler::EmitEdgeCounter() { + // We do not check for overflow when incrementing the edge counter. The + // function should normally be optimized long before the counter can + // overflow; and though we do not reset the counters when we optimize or + // deoptimize, there is a bound on the number of + // optimization/deoptimization cycles we will attempt. + const Array& counter = Array::ZoneHandle(Array::New(1, Heap::kOld)); + counter.SetAt(0, Smi::Handle(Smi::New(0))); + __ Comment("Edge counter"); + __ LoadObject(RAX, counter, PP); + __ AddImmediate(FieldAddress(RAX, Array::element_offset(0)), + Immediate(Smi::RawValue(1)), PP); +} + + void FlowGraphCompiler::EmitOptimizedInstanceCall( ExternalLabel* target_label, const ICData& ic_data, diff --git a/runtime/vm/instructions_arm.cc b/runtime/vm/instructions_arm.cc index 4ae5936a51e..9cc0d582149 100644 --- a/runtime/vm/instructions_arm.cc +++ b/runtime/vm/instructions_arm.cc @@ -13,88 +13,99 @@ namespace dart { CallPattern::CallPattern(uword pc, const Code& code) - : end_(reinterpret_cast(pc)), + : object_pool_(Array::Handle(code.ObjectPool())), + end_(pc), + args_desc_load_end_(0), + ic_data_load_end_(0), target_address_pool_index_(-1), - args_desc_load_end_(-1), args_desc_(Array::Handle()), - ic_data_load_end_(-1), - ic_data_(ICData::Handle()), - object_pool_(Array::Handle(code.ObjectPool())) { + ic_data_(ICData::Handle()) { ASSERT(code.ContainsInstructionAt(pc)); - ASSERT(Back(1) == 0xe12fff3e); // Last instruction: blx lr + // Last instruction: blx lr. + ASSERT(*(reinterpret_cast(end_) - 1) == 0xe12fff3e); + Register reg; ic_data_load_end_ = - DecodeLoadWordFromPool(1, ®, &target_address_pool_index_); + InstructionPattern::DecodeLoadWordFromPool(end_ - Instr::kInstrSize, + ®, + &target_address_pool_index_); ASSERT(reg == LR); } -uword CallPattern::Back(int n) const { - ASSERT(n > 0); - return *(end_ - n); -} - - -// Decodes a load sequence ending at end. Returns the register being loaded and -// the loaded object. -// Returns the location of the load sequence, counting the number of -// instructions back from the end of the call pattern. -int CallPattern::DecodeLoadObject(int end, Register* reg, Object* obj) { - ASSERT(end > 0); - uword instr = Back(end + 1); - if ((instr & 0xfff00000) == 0xe5900000) { // ldr reg, [reg, #+offset] - int index = 0; - end = DecodeLoadWordFromPool(end, reg, &index); - *obj = object_pool_.At(index); +// Decodes a load sequence ending at 'end' (the last instruction of the load +// sequence is the instruction before the one at end). Returns a pointer to +// the first instruction in the sequence. Returns the register being loaded +// and the loaded object in the output parameters 'reg' and 'obj' +// respectively. +uword InstructionPattern::DecodeLoadObject(uword end, + const Array& object_pool, + Register* reg, + Object* obj) { + uword start = 0; + Instr* instr = Instr::At(end - Instr::kInstrSize); + if ((instr->InstructionBits() & 0xfff00000) == 0xe5900000) { + // ldr reg, [reg, #+offset] + intptr_t index = 0; + start = DecodeLoadWordFromPool(end, reg, &index); + *obj = object_pool.At(index); } else { - int value = 0; - end = DecodeLoadWordImmediate(end, reg, &value); + intptr_t value = 0; + start = DecodeLoadWordImmediate(end, reg, &value); *obj = reinterpret_cast(value); } - return end; + return start; } -// Decodes a load sequence ending at end. Returns the register being loaded and -// the loaded immediate value. -// Returns the location of the load sequence, counting the number of -// instructions back from the end of the call pattern. -int CallPattern::DecodeLoadWordImmediate(int end, Register* reg, int* value) { - ASSERT(end > 0); - uword instr = Back(++end); - int imm = 0; +// Decodes a load sequence ending at 'end' (the last instruction of the load +// sequence is the instruction before the one at end). Returns a pointer to +// the first instruction in the sequence. Returns the register being loaded +// and the loaded immediate value in the output parameters 'reg' and 'value' +// respectively. +uword InstructionPattern::DecodeLoadWordImmediate(uword end, + Register* reg, + intptr_t* value) { + uword start = end - Instr::kInstrSize; + int32_t instr = Instr::At(start)->InstructionBits(); + intptr_t imm = 0; if ((instr & 0xfff00000) == 0xe3400000) { // movt reg, #imm_hi imm |= (instr & 0xf0000) << 12; imm |= (instr & 0xfff) << 16; - instr = Back(++end); + start -= Instr::kInstrSize; + instr = Instr::At(start)->InstructionBits(); } ASSERT((instr & 0xfff00000) == 0xe3000000); // movw reg, #imm_lo imm |= (instr & 0xf0000) >> 4; imm |= instr & 0xfff; *reg = static_cast((instr & 0xf000) >> 12); *value = imm; - return end; + return start; } -// Decodes a load sequence ending at end. Returns the register being loaded and -// the index in the pool being read from. -// Returns the location of the load sequence, counting the number of -// instructions back from the end of the call pattern. -int CallPattern::DecodeLoadWordFromPool(int end, Register* reg, int* index) { - ASSERT(end > 0); - uword instr = Back(++end); - int offset = 0; +// Decodes a load sequence ending at 'end' (the last instruction of the load +// sequence is the instruction before the one at end). Returns a pointer to +// the first instruction in the sequence. Returns the register being loaded +// and the index in the pool being read from in the output parameters 'reg' +// and 'index' respectively. +uword InstructionPattern::DecodeLoadWordFromPool(uword end, + Register* reg, + intptr_t* index) { + uword start = end - Instr::kInstrSize; + int32_t instr = Instr::At(start)->InstructionBits(); + intptr_t offset = 0; if ((instr & 0xffff0000) == 0xe59a0000) { // ldr reg, [pp, #+offset] offset = instr & 0xfff; *reg = static_cast((instr & 0xf000) >> 12); } else { ASSERT((instr & 0xfff00000) == 0xe5900000); // ldr reg, [reg, #+offset] offset = instr & 0xfff; - instr = Back(++end); + start -= Instr::kInstrSize; + instr = Instr::At(start)->InstructionBits(); if ((instr & 0xffff0000) == 0xe28a0000) { // add reg, pp, shifter_op - const int rot = (instr & 0xf00) >> 7; - const int imm8 = instr & 0xff; + const intptr_t rot = (instr & 0xf00) >> 7; + const intptr_t imm8 = instr & 0xff; offset += (imm8 >> rot) | (imm8 << (32 - rot)); *reg = static_cast((instr & 0xf000) >> 12); } else { @@ -104,15 +115,19 @@ int CallPattern::DecodeLoadWordFromPool(int end, Register* reg, int* index) { } offset += kHeapObjectTag; ASSERT(Utils::IsAligned(offset, 4)); - *index = (offset - Array::data_offset())/4; - return end; + *index = (offset - Array::data_offset()) / 4; + return start; } RawICData* CallPattern::IcData() { if (ic_data_.IsNull()) { Register reg; - args_desc_load_end_ = DecodeLoadObject(ic_data_load_end_, ®, &ic_data_); + args_desc_load_end_ = + InstructionPattern::DecodeLoadObject(ic_data_load_end_, + object_pool_, + ®, + &ic_data_); ASSERT(reg == R5); } return ic_data_.raw(); @@ -123,7 +138,10 @@ RawArray* CallPattern::ClosureArgumentsDescriptor() { if (args_desc_.IsNull()) { IcData(); // Loading of the ic_data must be decoded first, if not already. Register reg; - DecodeLoadObject(args_desc_load_end_, ®, &args_desc_); + InstructionPattern::DecodeLoadObject(args_desc_load_end_, + object_pool_, + ®, + &args_desc_); ASSERT(reg == R4); } return args_desc_.raw(); @@ -198,4 +216,3 @@ void JumpPattern::SetTargetAddress(uword target_address) const { } // namespace dart #endif // defined TARGET_ARCH_ARM - diff --git a/runtime/vm/instructions_arm.h b/runtime/vm/instructions_arm.h index 59cb2595866..05235d1d19e 100644 --- a/runtime/vm/instructions_arm.h +++ b/runtime/vm/instructions_arm.h @@ -15,6 +15,38 @@ namespace dart { +class InstructionPattern : public AllStatic { + public: + // Decodes a load sequence ending at 'end' (the last instruction of the + // load sequence is the instruction before the one at end). Returns the + // address of the first instruction in the sequence. Returns the register + // being loaded and the loaded object in the output parameters 'reg' and + // 'obj' respectively. + static uword DecodeLoadObject(uword end, + const Array& object_pool, + Register* reg, + Object* obj); + + // Decodes a load sequence ending at 'end' (the last instruction of the + // load sequence is the instruction before the one at end). Returns the + // address of the first instruction in the sequence. Returns the register + // being loaded and the loaded immediate value in the output parameters + // 'reg' and 'value' respectively. + static uword DecodeLoadWordImmediate(uword end, + Register* reg, + intptr_t* value); + + // Decodes a load sequence ending at 'end' (the last instruction of the + // load sequence is the instruction before the one at end). Returns the + // address of the first instruction in the sequence. Returns the register + // being loaded and the index in the pool being read from in the output + // parameters 'reg' and 'index' respectively. + static uword DecodeLoadWordFromPool(uword end, + Register* reg, + intptr_t* index); +}; + + class CallPattern : public ValueObject { public: CallPattern(uword pc, const Code& code); @@ -32,18 +64,16 @@ class CallPattern : public ValueObject { static void InsertAt(uword pc, uword target_address); private: - uword Back(int n) const; - int DecodeLoadObject(int end, Register* reg, Object* obj); - int DecodeLoadWordImmediate(int end, Register* reg, int* value); - int DecodeLoadWordFromPool(int end, Register* reg, int* index); - const uword* end_; - int target_address_pool_index_; - int args_desc_load_end_; - Array& args_desc_; - int ic_data_load_end_; - ICData& ic_data_; const Array& object_pool_; + uword end_; + uword args_desc_load_end_; + uword ic_data_load_end_; + + intptr_t target_address_pool_index_; + Array& args_desc_; + ICData& ic_data_; + DISALLOW_COPY_AND_ASSIGN(CallPattern); }; @@ -71,4 +101,3 @@ class JumpPattern : public ValueObject { } // namespace dart #endif // VM_INSTRUCTIONS_ARM_H_ - diff --git a/runtime/vm/instructions_mips.cc b/runtime/vm/instructions_mips.cc index 7215ec08f6f..ae6fbf24ed5 100644 --- a/runtime/vm/instructions_mips.cc +++ b/runtime/vm/instructions_mips.cc @@ -13,82 +13,88 @@ namespace dart { CallPattern::CallPattern(uword pc, const Code& code) - : end_(reinterpret_cast(pc)), + : object_pool_(Array::Handle(code.ObjectPool())), + end_(pc), + args_desc_load_end_(0), + ic_data_load_end_(0), target_address_pool_index_(-1), - args_desc_load_end_(-1), args_desc_(Array::Handle()), - ic_data_load_end_(-1), - ic_data_(ICData::Handle()), - object_pool_(Array::Handle(code.ObjectPool())) { + ic_data_(ICData::Handle()) { ASSERT(code.ContainsInstructionAt(pc)); - ASSERT(Back(2) == 0x0020f809); // Last instruction: jalr RA, TMP(=R1) + // Last instruction: jalr RA, TMP(=R1). + ASSERT(*(reinterpret_cast(end_) - 2) == 0x0020f809); Register reg; - // First end is 0 so that we begin from the delay slot of the jalr. + // The end of the pattern is the instruction after the delay slot of the jalr. ic_data_load_end_ = - DecodeLoadWordFromPool(2, ®, &target_address_pool_index_); + InstructionPattern::DecodeLoadWordFromPool(end_ - (2 * Instr::kInstrSize), + ®, + &target_address_pool_index_); ASSERT(reg == TMP); } -uword CallPattern::Back(int n) const { - ASSERT(n > 0); - return *(end_ - n); -} - - -// Decodes a load sequence ending at end. Returns the register being loaded and -// the loaded object. -// Returns the location of the load sequence, counting the number of -// instructions back from the end of the call pattern. -int CallPattern::DecodeLoadObject(int end, Register* reg, Object* obj) { - ASSERT(end > 0); - uword i = Back(end + 1); - Instr* instr = Instr::At(reinterpret_cast(&i)); +// Decodes a load sequence ending at 'end' (the last instruction of the load +// sequence is the instruction before the one at end). Returns a pointer to +// the first instruction in the sequence. Returns the register being loaded +// and the loaded object in the output parameters 'reg' and 'obj' +// respectively. +uword InstructionPattern::DecodeLoadObject(uword end, + const Array& object_pool, + Register* reg, + Object* obj) { + uword start = 0; + Instr* instr = Instr::At(end - Instr::kInstrSize); if (instr->OpcodeField() == LW) { - int index = 0; - end = DecodeLoadWordFromPool(end, reg, &index); - *obj = object_pool_.At(index); + intptr_t index = 0; + start = DecodeLoadWordFromPool(end, reg, &index); + *obj = object_pool.At(index); } else { - int value = 0; - end = DecodeLoadWordImmediate(end, reg, &value); + intptr_t value = 0; + start = DecodeLoadWordImmediate(end, reg, &value); *obj = reinterpret_cast(value); } - return end; + return start; } -// Decodes a load sequence ending at end. Returns the register being loaded and -// the loaded immediate value. -// Returns the location of the load sequence, counting the number of -// instructions back from the end of the call pattern. -int CallPattern::DecodeLoadWordImmediate(int end, Register* reg, int* value) { - ASSERT(end > 0); - int imm = 0; - uword i = Back(++end); - Instr* instr = Instr::At(reinterpret_cast(&i)); +// Decodes a load sequence ending at 'end' (the last instruction of the load +// sequence is the instruction before the one at end). Returns a pointer to +// the first instruction in the sequence. Returns the register being loaded +// and the loaded immediate value in the output parameters 'reg' and 'value' +// respectively. +uword InstructionPattern::DecodeLoadWordImmediate(uword end, + Register* reg, + intptr_t* value) { + // The pattern is a fixed size, but match backwards for uniformity with + // DecodeLoadWordFromPool. + uword start = end - Instr::kInstrSize; + Instr* instr = Instr::At(start); + intptr_t imm = 0; ASSERT(instr->OpcodeField() == ORI); imm = instr->UImmField(); *reg = instr->RtField(); - i = Back(++end); - instr = Instr::At(reinterpret_cast(&i)); + start -= Instr::kInstrSize; + instr = Instr::At(start); ASSERT(instr->OpcodeField() == LUI); ASSERT(instr->RtField() == *reg); imm |= (instr->UImmField() << 16); *value = imm; - return end; + return start; } -// Decodes a load sequence ending at end. Returns the register being loaded and -// the index in the pool being read from. -// Returns the location of the load sequence, counting the number of -// instructions back from the end of the call pattern. -int CallPattern::DecodeLoadWordFromPool(int end, Register* reg, int* index) { - ASSERT(end > 0); - uword i = Back(++end); - Instr* instr = Instr::At(reinterpret_cast(&i)); - int offset = 0; +// Decodes a load sequence ending at 'end' (the last instruction of the load +// sequence is the instruction before the one at end). Returns a pointer to +// the first instruction in the sequence. Returns the register being loaded +// and the index in the pool being read from in the output parameters 'reg' +// and 'index' respectively. +uword InstructionPattern::DecodeLoadWordFromPool(uword end, + Register* reg, + intptr_t* index) { + uword start = end - Instr::kInstrSize; + Instr* instr = Instr::At(start); + intptr_t offset = 0; if ((instr->OpcodeField() == LW) && (instr->RsField() == PP)) { offset = instr->SImmField(); *reg = instr->RtField(); @@ -97,16 +103,16 @@ int CallPattern::DecodeLoadWordFromPool(int end, Register* reg, int* index) { offset = instr->SImmField(); *reg = instr->RtField(); - i = Back(++end); - instr = Instr::At(reinterpret_cast(&i)); + start -= Instr::kInstrSize; + instr = Instr::At(start); ASSERT(instr->OpcodeField() == SPECIAL); ASSERT(instr->FunctionField() == ADDU); ASSERT(instr->RdField() == *reg); ASSERT(instr->RsField() == *reg); ASSERT(instr->RtField() == PP); - i = Back(++end); - instr = Instr::At(reinterpret_cast(&i)); + start -= Instr::kInstrSize; + instr = Instr::At(start); ASSERT(instr->OpcodeField() == LUI); ASSERT(instr->RtField() == *reg); // Offset is signed, so add the upper 16 bits. @@ -114,15 +120,19 @@ int CallPattern::DecodeLoadWordFromPool(int end, Register* reg, int* index) { } offset += kHeapObjectTag; ASSERT(Utils::IsAligned(offset, 4)); - *index = (offset - Array::data_offset())/4; - return end; + *index = (offset - Array::data_offset()) / 4; + return start; } RawICData* CallPattern::IcData() { if (ic_data_.IsNull()) { Register reg; - args_desc_load_end_ = DecodeLoadObject(ic_data_load_end_, ®, &ic_data_); + args_desc_load_end_ = + InstructionPattern::DecodeLoadObject(ic_data_load_end_, + object_pool_, + ®, + &ic_data_); ASSERT(reg == S5); } return ic_data_.raw(); @@ -133,7 +143,10 @@ RawArray* CallPattern::ClosureArgumentsDescriptor() { if (args_desc_.IsNull()) { IcData(); // Loading of the ic_data must be decoded first, if not already. Register reg; - DecodeLoadObject(args_desc_load_end_, ®, &args_desc_); + InstructionPattern::DecodeLoadObject(args_desc_load_end_, + object_pool_, + ®, + &args_desc_); ASSERT(reg == S4); } return args_desc_.raw(); @@ -217,4 +230,3 @@ void JumpPattern::SetTargetAddress(uword target_address) const { } // namespace dart #endif // defined TARGET_ARCH_MIPS - diff --git a/runtime/vm/instructions_mips.h b/runtime/vm/instructions_mips.h index 7cecc700e58..b4864b74665 100644 --- a/runtime/vm/instructions_mips.h +++ b/runtime/vm/instructions_mips.h @@ -15,6 +15,38 @@ namespace dart { +class InstructionPattern : public AllStatic { + public: + // Decodes a load sequence ending at 'end' (the last instruction of the + // load sequence is the instruction before the one at end). Returns the + // address of the first instruction in the sequence. Returns the register + // being loaded and the loaded object in the output parameters 'reg' and + // 'obj' respectively. + static uword DecodeLoadObject(uword end, + const Array& object_pool, + Register* reg, + Object* obj); + + // Decodes a load sequence ending at 'end' (the last instruction of the + // load sequence is the instruction before the one at end). Returns the + // address of the first instruction in the sequence. Returns the register + // being loaded and the loaded immediate value in the output parameters + // 'reg' and 'value' respectively. + static uword DecodeLoadWordImmediate(uword end, + Register* reg, + intptr_t* value); + + // Decodes a load sequence ending at 'end' (the last instruction of the + // load sequence is the instruction before the one at end). Returns the + // address of the first instruction in the sequence. Returns the register + // being loaded and the index in the pool being read from in the output + // parameters 'reg' and 'index' respectively. + static uword DecodeLoadWordFromPool(uword end, + Register* reg, + intptr_t* index); +}; + + class CallPattern : public ValueObject { public: CallPattern(uword pc, const Code& code); @@ -32,18 +64,16 @@ class CallPattern : public ValueObject { static void InsertAt(uword pc, uword target_address); private: - uword Back(int n) const; - int DecodeLoadObject(int end, Register* reg, Object* obj); - int DecodeLoadWordImmediate(int end, Register* reg, int* value); - int DecodeLoadWordFromPool(int end, Register* reg, int* index); - const uword* end_; - int target_address_pool_index_; - int args_desc_load_end_; - Array& args_desc_; - int ic_data_load_end_; - ICData& ic_data_; const Array& object_pool_; + uword end_; + uword args_desc_load_end_; + uword ic_data_load_end_; + + intptr_t target_address_pool_index_; + Array& args_desc_; + ICData& ic_data_; + DISALLOW_COPY_AND_ASSIGN(CallPattern); }; @@ -72,4 +102,3 @@ class JumpPattern : public ValueObject { } // namespace dart #endif // VM_INSTRUCTIONS_MIPS_H_ - diff --git a/runtime/vm/instructions_x64.cc b/runtime/vm/instructions_x64.cc index 0f78807306b..7b0b4aed15b 100644 --- a/runtime/vm/instructions_x64.cc +++ b/runtime/vm/instructions_x64.cc @@ -13,7 +13,7 @@ namespace dart { intptr_t InstructionPattern::IndexFromPPLoad(uword start) { int32_t offset = *reinterpret_cast(start); - offset += kHeapObjectTag; + offset += kHeapObjectTag; return (offset - Array::data_offset()) / kWordSize; } diff --git a/runtime/vm/intermediate_language.cc b/runtime/vm/intermediate_language.cc index 365c8074b2b..50c4930838a 100644 --- a/runtime/vm/intermediate_language.cc +++ b/runtime/vm/intermediate_language.cc @@ -1664,6 +1664,12 @@ void JoinEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) { LocationSummary* TargetEntryInstr::MakeLocationSummary() const { + // FlowGraphCompiler::EmitInstructionPrologue is not called for block + // entry instructions, so this function is unused. If it becomes + // reachable, note that the deoptimization descriptor in unoptimized code + // comes after the point of local register allocation due to pattern + // matching the edge counter code backwards (as a code reuse convenience + // on some platforms). UNREACHABLE(); return NULL; } diff --git a/runtime/vm/intermediate_language_arm.cc b/runtime/vm/intermediate_language_arm.cc index 95e4732397f..35b43d9c547 100644 --- a/runtime/vm/intermediate_language_arm.cc +++ b/runtime/vm/intermediate_language_arm.cc @@ -4382,18 +4382,14 @@ void GraphEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) { void TargetEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) { __ Bind(compiler->GetJumpLabel(this)); if (!compiler->is_optimizing()) { + compiler->EmitEdgeCounter(); + // Add an edge counter. + // On ARM the deoptimization descriptor points after the edge counter + // code so that we can reuse the same pattern matching code as at call + // sites, which matches backwards from the end of the pattern. compiler->AddCurrentDescriptor(PcDescriptors::kDeopt, deopt_id_, Scanner::kDummyTokenIndex); - // Add an edge counter. - const Array& counter = Array::ZoneHandle(Array::New(1, Heap::kOld)); - counter.SetAt(0, Smi::Handle(Smi::New(0))); - __ Comment("Edge counter"); - __ LoadObject(R0, counter); - __ ldr(IP, FieldAddress(R0, Array::element_offset(0))); - __ adds(IP, IP, ShifterOperand(Smi::RawValue(1))); - __ LoadImmediate(IP, Smi::RawValue(Smi::kMaxValue), VS); // If overflow. - __ str(IP, FieldAddress(R0, Array::element_offset(0))); } if (HasParallelMove()) { compiler->parallel_move_resolver()->EmitNativeCode(parallel_move()); @@ -4407,6 +4403,17 @@ LocationSummary* GotoInstr::MakeLocationSummary() const { void GotoInstr::EmitNativeCode(FlowGraphCompiler* compiler) { + if (!compiler->is_optimizing()) { + compiler->EmitEdgeCounter(); + // Add a deoptimization descriptor for deoptimizing instructions that + // may be inserted before this instruction. On ARM this descriptor + // points after the edge counter code so that we can reuse the same + // pattern matching code as at call sites, which matches backwards from + // the end of the pattern. + compiler->AddCurrentDescriptor(PcDescriptors::kDeopt, + GetDeoptId(), + Scanner::kDummyTokenIndex); + } if (HasParallelMove()) { compiler->parallel_move_resolver()->EmitNativeCode(parallel_move()); } diff --git a/runtime/vm/intermediate_language_ia32.cc b/runtime/vm/intermediate_language_ia32.cc index e7c587ebea2..7e13f1d30ce 100644 --- a/runtime/vm/intermediate_language_ia32.cc +++ b/runtime/vm/intermediate_language_ia32.cc @@ -4713,21 +4713,13 @@ void GraphEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) { void TargetEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) { __ Bind(compiler->GetJumpLabel(this)); if (!compiler->is_optimizing()) { + compiler->EmitEdgeCounter(); + // The deoptimization descriptor points after the edge counter code for + // uniformity with ARM and MIPS, where we can reuse pattern matching + // code that matches backwards from the end of the pattern. compiler->AddCurrentDescriptor(PcDescriptors::kDeopt, deopt_id_, Scanner::kDummyTokenIndex); - // Add an edge counter. - const Array& counter = Array::ZoneHandle(Array::New(1, Heap::kOld)); - counter.SetAt(0, Smi::Handle(Smi::New(0))); - Label done; - __ Comment("Edge counter"); - __ LoadObject(EAX, counter); - __ addl(FieldAddress(EAX, Array::element_offset(0)), - Immediate(Smi::RawValue(1))); - __ j(NO_OVERFLOW, &done); - __ movl(FieldAddress(EAX, Array::element_offset(0)), - Immediate(Smi::RawValue(Smi::kMaxValue))); - __ Bind(&done); } if (HasParallelMove()) { compiler->parallel_move_resolver()->EmitNativeCode(parallel_move()); @@ -4742,23 +4734,15 @@ LocationSummary* GotoInstr::MakeLocationSummary() const { void GotoInstr::EmitNativeCode(FlowGraphCompiler* compiler) { if (!compiler->is_optimizing()) { - // Add deoptimization descriptor for deoptimizing instructions that may - // be inserted before this instruction. + compiler->EmitEdgeCounter(); + // Add a deoptimization descriptor for deoptimizing instructions that + // may be inserted before this instruction. This descriptor points + // after the edge counter for uniformity with ARM and MIPS, where we can + // reuse pattern matching that matches backwards from the end of the + // pattern. compiler->AddCurrentDescriptor(PcDescriptors::kDeopt, GetDeoptId(), - 0); // No token position. - // Add an edge counter. - const Array& counter = Array::ZoneHandle(Array::New(1, Heap::kOld)); - counter.SetAt(0, Smi::Handle(Smi::New(0))); - Label done; - __ Comment("Edge counter"); - __ LoadObject(EAX, counter); - __ addl(FieldAddress(EAX, Array::element_offset(0)), - Immediate(Smi::RawValue(1))); - __ j(NO_OVERFLOW, &done); - __ movl(FieldAddress(EAX, Array::element_offset(0)), - Immediate(Smi::RawValue(Smi::kMaxValue))); - __ Bind(&done); + Scanner::kDummyTokenIndex); } if (HasParallelMove()) { compiler->parallel_move_resolver()->EmitNativeCode(parallel_move()); diff --git a/runtime/vm/intermediate_language_mips.cc b/runtime/vm/intermediate_language_mips.cc index 9c72ba1877f..4456f6ea2dd 100644 --- a/runtime/vm/intermediate_language_mips.cc +++ b/runtime/vm/intermediate_language_mips.cc @@ -3773,22 +3773,13 @@ void GraphEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) { void TargetEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) { __ Bind(compiler->GetJumpLabel(this)); if (!compiler->is_optimizing()) { + compiler->EmitEdgeCounter(); + // On MIPS the deoptimization descriptor points after the edge counter + // code so that we can reuse the same pattern matching code as at call + // sites, which matches backwards from the end of the pattern. compiler->AddCurrentDescriptor(PcDescriptors::kDeopt, deopt_id_, Scanner::kDummyTokenIndex); - // Add an edge counter. - const Array& counter = Array::ZoneHandle(Array::New(1, Heap::kOld)); - counter.SetAt(0, Smi::Handle(Smi::New(0))); - Label done; - __ Comment("Edge counter"); - __ LoadObject(T0, counter); - __ lw(T1, FieldAddress(T0, Array::element_offset(0))); - __ AddImmediateDetectOverflow(T1, T1, Smi::RawValue(1), CMPRES, T2); - __ bgez(CMPRES, &done); - __ delay_slot()->sw(T1, FieldAddress(T0, Array::element_offset(0))); - __ LoadImmediate(TMP1, Smi::RawValue(Smi::kMaxValue)); - __ sw(TMP1, FieldAddress(T0, Array::element_offset(0))); // If overflow. - __ Bind(&done); } if (HasParallelMove()) { compiler->parallel_move_resolver()->EmitNativeCode(parallel_move()); @@ -3803,6 +3794,17 @@ LocationSummary* GotoInstr::MakeLocationSummary() const { void GotoInstr::EmitNativeCode(FlowGraphCompiler* compiler) { __ TraceSimMsg("GotoInstr"); + if (!compiler->is_optimizing()) { + compiler->EmitEdgeCounter(); + // Add a deoptimization descriptor for deoptimizing instructions that + // may be inserted before this instruction. On MIPS this descriptor + // points after the edge counter code so that we can reuse the same + // pattern matching code as at call sites, which matches backwards from + // the end of the pattern. + compiler->AddCurrentDescriptor(PcDescriptors::kDeopt, + GetDeoptId(), + Scanner::kDummyTokenIndex); + } if (HasParallelMove()) { compiler->parallel_move_resolver()->EmitNativeCode(parallel_move()); } diff --git a/runtime/vm/intermediate_language_x64.cc b/runtime/vm/intermediate_language_x64.cc index 4d2ee9df452..4b5562b189f 100644 --- a/runtime/vm/intermediate_language_x64.cc +++ b/runtime/vm/intermediate_language_x64.cc @@ -4499,21 +4499,13 @@ void GraphEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) { void TargetEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) { __ Bind(compiler->GetJumpLabel(this)); if (!compiler->is_optimizing()) { + compiler->EmitEdgeCounter(); + // The deoptimization descriptor points after the edge counter code for + // uniformity with ARM and MIPS, where we can reuse pattern matching + // code that matches backwards from the end of the pattern. compiler->AddCurrentDescriptor(PcDescriptors::kDeopt, deopt_id_, Scanner::kDummyTokenIndex); - // Add an edge counter. - const Array& counter = Array::ZoneHandle(Array::New(1, Heap::kOld)); - counter.SetAt(0, Smi::Handle(Smi::New(0))); - Label done; - __ Comment("Edge counter"); - __ LoadObject(RAX, counter, PP); - __ AddImmediate(FieldAddress(RAX, Array::element_offset(0)), - Immediate(Smi::RawValue(1)), PP); - __ j(NO_OVERFLOW, &done); - __ LoadImmediate(FieldAddress(RAX, Array::element_offset(0)), - Immediate(Smi::RawValue(Smi::kMaxValue)), PP); - __ Bind(&done); } if (HasParallelMove()) { compiler->parallel_move_resolver()->EmitNativeCode(parallel_move()); @@ -4528,23 +4520,15 @@ LocationSummary* GotoInstr::MakeLocationSummary() const { void GotoInstr::EmitNativeCode(FlowGraphCompiler* compiler) { if (!compiler->is_optimizing()) { - // Add deoptimization descriptor for deoptimizing instructions that may - // be inserted before this instruction. + compiler->EmitEdgeCounter(); + // Add a deoptimization descriptor for deoptimizing instructions that + // may be inserted before this instruction. This descriptor points + // after the edge counter for uniformity with ARM and MIPS, where we can + // reuse pattern matching that matches backwards from the end of the + // pattern. compiler->AddCurrentDescriptor(PcDescriptors::kDeopt, GetDeoptId(), - 0); // No token position. - // Add an edge counter. - const Array& counter = Array::ZoneHandle(Array::New(1, Heap::kOld)); - counter.SetAt(0, Smi::Handle(Smi::New(0))); - Label done; - __ Comment("Edge counter"); - __ LoadObject(RAX, counter, PP); - __ AddImmediate(FieldAddress(RAX, Array::element_offset(0)), - Immediate(Smi::RawValue(1)), PP); - __ j(NO_OVERFLOW, &done); - __ LoadImmediate(FieldAddress(RAX, Array::element_offset(0)), - Immediate(Smi::RawValue(Smi::kMaxValue)), PP); - __ Bind(&done); + Scanner::kDummyTokenIndex); } if (HasParallelMove()) { compiler->parallel_move_resolver()->EmitNativeCode(parallel_move());