Pattern match on generated code to find edge counters.
In unoptimized code, use platform-specific pattern matching on generated code to find edge counter arrays. Previously we searched pointer offsets, but that does not work on platforms that encode the edge counters as indexes into an object pool (i.e., x64, ARM, MIPS). BUG= R=fschneider@google.com, zra@google.com Review URL: https://codereview.chromium.org//24744002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@28085 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
@@ -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<RawObject**>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<uint8_t*>(end - 5) == 0xb8);
|
||||
}
|
||||
|
||||
RawObject* edge_counter() const {
|
||||
return *reinterpret_cast<RawObject**>(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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<uint8_t*>(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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -60,9 +60,15 @@ void FlowGraph::AddToGuardedFields(
|
||||
}
|
||||
|
||||
|
||||
GrowableArray<BlockEntryInstr*>* FlowGraph::codegen_block_order(
|
||||
bool FlowGraph::ShouldReorderBlocks(const Function& function,
|
||||
bool is_optimized) {
|
||||
return is_optimized && FLAG_reorder_basic_blocks && !function.is_intrinsic();
|
||||
}
|
||||
|
||||
|
||||
GrowableArray<BlockEntryInstr*>* FlowGraph::CodegenBlockOrder(
|
||||
bool is_optimized) {
|
||||
return (is_optimized && FLAG_reorder_basic_blocks)
|
||||
return ShouldReorderBlocks(parsed_function().function(), is_optimized)
|
||||
? &optimized_block_order_
|
||||
: &reverse_postorder_;
|
||||
}
|
||||
|
||||
@@ -82,7 +82,8 @@ class FlowGraph : public ZoneAllocated {
|
||||
const GrowableArray<BlockEntryInstr*>& reverse_postorder() const {
|
||||
return reverse_postorder_;
|
||||
}
|
||||
GrowableArray<BlockEntryInstr*>* codegen_block_order(bool is_optimized);
|
||||
static bool ShouldReorderBlocks(const Function& function, bool is_optimized);
|
||||
GrowableArray<BlockEntryInstr*>* CodegenBlockOrder(bool is_optimized);
|
||||
|
||||
// Iterators.
|
||||
BlockIterator reverse_postorder_iterator() const {
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -13,88 +13,99 @@
|
||||
namespace dart {
|
||||
|
||||
CallPattern::CallPattern(uword pc, const Code& code)
|
||||
: end_(reinterpret_cast<uword*>(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<uword*>(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<RawObject*>(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<Register>((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<Register>((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<Register>((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
|
||||
|
||||
|
||||
@@ -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_
|
||||
|
||||
|
||||
@@ -13,82 +13,88 @@
|
||||
namespace dart {
|
||||
|
||||
CallPattern::CallPattern(uword pc, const Code& code)
|
||||
: end_(reinterpret_cast<uword*>(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<uword*>(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<uword>(&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<RawObject*>(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<uword>(&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<uword>(&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<uword>(&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<uword>(&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<uword>(&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
|
||||
|
||||
|
||||
@@ -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_
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace dart {
|
||||
|
||||
intptr_t InstructionPattern::IndexFromPPLoad(uword start) {
|
||||
int32_t offset = *reinterpret_cast<int32_t*>(start);
|
||||
offset += kHeapObjectTag;
|
||||
offset += kHeapObjectTag;
|
||||
return (offset - Array::data_offset()) / kWordSize;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
Reference in New Issue
Block a user