mirror of
https://github.com/dart-lang/sdk
synced 2024-11-05 18:22:09 +00:00
Disconnects code objects from infrequently used unoptimized functions.
Every 30 seconds (configurable by --code-collection-interval), before a MarkSweep collection, this change halves a function's usage count if it is unoptimized. If the function's usage count reaches 0 as a result of this halving, it sets the function's code pointers to null. Then, if the code object isn't marked during the MarkSweep, it will be collected. This change also checks for null code pointers in various places, and recompiles/reconnects code if needed. R=srdjan@google.com Review URL: https://codereview.chromium.org//27802002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@29209 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
parent
d93cbb1e41
commit
039a4c4078
16 changed files with 419 additions and 28 deletions
|
@ -2807,9 +2807,9 @@ void Assembler::EnterDartFrameWithInfo(intptr_t frame_size,
|
|||
pushq(new_pc);
|
||||
pushq(PP);
|
||||
movq(PP, new_pp);
|
||||
}
|
||||
if (frame_size != 0) {
|
||||
subq(RSP, Immediate(frame_size));
|
||||
if (frame_size != 0) {
|
||||
subq(RSP, Immediate(frame_size));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1418,10 +1418,22 @@ DEFINE_RUNTIME_ENTRY(FixCallersTarget, 0) {
|
|||
ASSERT(caller_code.is_optimized());
|
||||
const Function& target_function = Function::Handle(
|
||||
caller_code.GetStaticCallTargetFunctionAt(frame->pc()));
|
||||
const Code& target_code = Code::Handle(target_function.CurrentCode());
|
||||
CodePatcher::PatchStaticCallAt(frame->pc(), caller_code,
|
||||
target_code.EntryPoint());
|
||||
caller_code.SetStaticCallTargetCodeAt(frame->pc(), target_code);
|
||||
|
||||
// Check whether the code object has been detached from the target function.
|
||||
// If it has been detached, reattach it.
|
||||
Code& target_code = Code::Handle();
|
||||
if (target_function.HasCode()) {
|
||||
target_code ^= target_function.CurrentCode();
|
||||
CodePatcher::PatchStaticCallAt(frame->pc(), caller_code,
|
||||
target_code.EntryPoint());
|
||||
caller_code.SetStaticCallTargetCodeAt(frame->pc(), target_code);
|
||||
} else {
|
||||
ASSERT(target_function.unoptimized_code() == Code::null());
|
||||
target_code ^= caller_code.GetStaticCallTargetCodeAt(frame->pc());
|
||||
ASSERT(!target_code.IsNull());
|
||||
ASSERT(!target_code.is_optimized());
|
||||
target_function.ReattachCode(target_code);
|
||||
}
|
||||
if (FLAG_trace_patching) {
|
||||
OS::PrintErr("FixCallersTarget: patching from %#" Px " to '%s' %#" Px "\n",
|
||||
frame->pc(),
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "vm/flow_graph_compiler.h"
|
||||
|
||||
#include "vm/ast_printer.h"
|
||||
#include "vm/compiler.h"
|
||||
#include "vm/dart_entry.h"
|
||||
#include "vm/deopt_instructions.h"
|
||||
#include "vm/il_printer.h"
|
||||
|
@ -1368,8 +1369,18 @@ void FlowGraphCompiler::EmitMegamorphicInstanceCall(
|
|||
// be invoked as a normal Dart function.
|
||||
__ add(IP, R2, ShifterOperand(R3, LSL, 2));
|
||||
__ ldr(R0, FieldAddress(IP, base + kWordSize));
|
||||
__ ldr(R0, FieldAddress(R0, Function::code_offset()));
|
||||
__ ldr(R0, FieldAddress(R0, Code::instructions_offset()));
|
||||
__ ldr(R1, FieldAddress(R0, Function::code_offset()));
|
||||
if (FLAG_collect_code) {
|
||||
// If we are collecting code, the code object may be null.
|
||||
Label is_compiled;
|
||||
__ CompareImmediate(R1, reinterpret_cast<intptr_t>(Object::null()));
|
||||
__ b(&is_compiled, NE);
|
||||
__ BranchLink(&StubCode::CompileFunctionRuntimeCallLabel());
|
||||
// R0: target function.
|
||||
__ ldr(R1, FieldAddress(R0, Function::code_offset()));
|
||||
__ Bind(&is_compiled);
|
||||
}
|
||||
__ ldr(R0, FieldAddress(R1, Code::instructions_offset()));
|
||||
__ LoadObject(R5, ic_data);
|
||||
__ LoadObject(R4, arguments_descriptor);
|
||||
__ AddImmediate(R0, Instructions::HeaderSize() - kHeapObjectTag);
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "vm/flow_graph_compiler.h"
|
||||
|
||||
#include "vm/ast_printer.h"
|
||||
#include "vm/compiler.h"
|
||||
#include "vm/dart_entry.h"
|
||||
#include "vm/deopt_instructions.h"
|
||||
#include "vm/il_printer.h"
|
||||
|
@ -1412,8 +1413,19 @@ void FlowGraphCompiler::EmitMegamorphicInstanceCall(
|
|||
// illegal class id was found, the target is a cache miss handler that can
|
||||
// be invoked as a normal Dart function.
|
||||
__ movl(EAX, FieldAddress(EDI, ECX, TIMES_4, base + kWordSize));
|
||||
__ movl(EAX, FieldAddress(EAX, Function::code_offset()));
|
||||
__ movl(EAX, FieldAddress(EAX, Code::instructions_offset()));
|
||||
__ movl(EBX, FieldAddress(EAX, Function::code_offset()));
|
||||
if (FLAG_collect_code) {
|
||||
// If we are collecting code, the code object may be null.
|
||||
Label is_compiled;
|
||||
const Immediate& raw_null =
|
||||
Immediate(reinterpret_cast<intptr_t>(Object::null()));
|
||||
__ cmpl(EBX, raw_null);
|
||||
__ j(NOT_EQUAL, &is_compiled, Assembler::kNearJump);
|
||||
__ call(&StubCode::CompileFunctionRuntimeCallLabel());
|
||||
__ movl(EBX, FieldAddress(EAX, Function::code_offset()));
|
||||
__ Bind(&is_compiled);
|
||||
}
|
||||
__ movl(EAX, FieldAddress(EBX, Code::instructions_offset()));
|
||||
__ LoadObject(ECX, ic_data);
|
||||
__ LoadObject(EDX, arguments_descriptor);
|
||||
__ addl(EAX, Immediate(Instructions::HeaderSize() - kHeapObjectTag));
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "vm/flow_graph_compiler.h"
|
||||
|
||||
#include "vm/ast_printer.h"
|
||||
#include "vm/compiler.h"
|
||||
#include "vm/dart_entry.h"
|
||||
#include "vm/deopt_instructions.h"
|
||||
#include "vm/il_printer.h"
|
||||
|
@ -1412,11 +1413,20 @@ void FlowGraphCompiler::EmitMegamorphicInstanceCall(
|
|||
// proper target for the given name and arguments descriptor. If the
|
||||
// illegal class id was found, the target is a cache miss handler that can
|
||||
// be invoked as a normal Dart function.
|
||||
__ sll(TMP1, T3, 2);
|
||||
__ addu(TMP1, T2, TMP1);
|
||||
__ lw(T0, FieldAddress(TMP, base + kWordSize));
|
||||
__ lw(T0, FieldAddress(T0, Function::code_offset()));
|
||||
__ lw(T0, FieldAddress(T0, Code::instructions_offset()));
|
||||
__ sll(T1, T3, 2);
|
||||
__ addu(T1, T2, T1);
|
||||
__ lw(T0, FieldAddress(T1, base + kWordSize));
|
||||
__ lw(T1, FieldAddress(T0, Function::code_offset()));
|
||||
if (FLAG_collect_code) {
|
||||
// If we are collecting code, the code object may be null.
|
||||
Label is_compiled;
|
||||
__ BranchNotEqual(T1, reinterpret_cast<int32_t>(Object::null()),
|
||||
&is_compiled);
|
||||
__ BranchLink(&StubCode::CompileFunctionRuntimeCallLabel());
|
||||
__ lw(T1, FieldAddress(T0, Function::code_offset()));
|
||||
__ Bind(&is_compiled);
|
||||
}
|
||||
__ lw(T0, FieldAddress(T1, Code::instructions_offset()));
|
||||
__ LoadObject(S5, ic_data);
|
||||
__ LoadObject(S4, arguments_descriptor);
|
||||
__ AddImmediate(T0, Instructions::HeaderSize() - kHeapObjectTag);
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "vm/flow_graph_compiler.h"
|
||||
|
||||
#include "vm/ast_printer.h"
|
||||
#include "vm/compiler.h"
|
||||
#include "vm/dart_entry.h"
|
||||
#include "vm/deopt_instructions.h"
|
||||
#include "vm/il_printer.h"
|
||||
|
@ -1447,8 +1448,19 @@ void FlowGraphCompiler::EmitMegamorphicInstanceCall(
|
|||
// illegal class id was found, the target is a cache miss handler that can
|
||||
// be invoked as a normal Dart function.
|
||||
__ movq(RAX, FieldAddress(RDI, RCX, TIMES_8, base + kWordSize));
|
||||
__ movq(RAX, FieldAddress(RAX, Function::code_offset()));
|
||||
__ movq(RAX, FieldAddress(RAX, Code::instructions_offset()));
|
||||
__ movq(RBX, FieldAddress(RAX, Function::code_offset()));
|
||||
if (FLAG_collect_code) {
|
||||
// If we are collecting code, the code object may be null.
|
||||
Label is_compiled;
|
||||
const Immediate& raw_null =
|
||||
Immediate(reinterpret_cast<intptr_t>(Object::null()));
|
||||
__ cmpq(RBX, raw_null);
|
||||
__ j(NOT_EQUAL, &is_compiled, Assembler::kNearJump);
|
||||
__ call(&StubCode::CompileFunctionRuntimeCallLabel());
|
||||
__ movq(RBX, FieldAddress(RAX, Function::code_offset()));
|
||||
__ Bind(&is_compiled);
|
||||
}
|
||||
__ movq(RAX, FieldAddress(RBX, Code::instructions_offset()));
|
||||
__ LoadObject(RBX, ic_data, PP);
|
||||
__ LoadObject(R10, arguments_descriptor, PP);
|
||||
__ AddImmediate(
|
||||
|
|
|
@ -4039,6 +4039,22 @@ void Function::SetCode(const Code& value) const {
|
|||
}
|
||||
|
||||
|
||||
void Function::DetachCode() const {
|
||||
// Set unoptimized code as non-entrant, and set code and unoptimized code
|
||||
// to null.
|
||||
CodePatcher::PatchEntry(Code::Handle(unoptimized_code()));
|
||||
StorePointer(&raw_ptr()->code_, Code::null());
|
||||
StorePointer(&raw_ptr()->unoptimized_code_, Code::null());
|
||||
}
|
||||
|
||||
|
||||
void Function::ReattachCode(const Code& code) const {
|
||||
set_unoptimized_code(code);
|
||||
SetCode(code);
|
||||
CodePatcher::RestoreEntry(code);
|
||||
}
|
||||
|
||||
|
||||
void Function::SwitchToUnoptimizedCode() const {
|
||||
ASSERT(HasOptimizedCode());
|
||||
|
||||
|
@ -4067,6 +4083,7 @@ void Function::SwitchToUnoptimizedCode() const {
|
|||
|
||||
|
||||
void Function::set_unoptimized_code(const Code& value) const {
|
||||
ASSERT(!value.is_optimized());
|
||||
StorePointer(&raw_ptr()->unoptimized_code_, value.raw());
|
||||
}
|
||||
|
||||
|
@ -9264,6 +9281,19 @@ RawFunction* Code::GetStaticCallTargetFunctionAt(uword pc) const {
|
|||
}
|
||||
|
||||
|
||||
RawCode* Code::GetStaticCallTargetCodeAt(uword pc) const {
|
||||
const intptr_t i = BinarySearchInSCallTable(pc);
|
||||
if (i < 0) {
|
||||
return Code::null();
|
||||
}
|
||||
const Array& array =
|
||||
Array::Handle(raw_ptr()->static_calls_target_table_);
|
||||
Code& code = Code::Handle();
|
||||
code ^= array.At(i + kSCallTableCodeEntry);
|
||||
return code.raw();
|
||||
}
|
||||
|
||||
|
||||
void Code::SetStaticCallTargetCodeAt(uword pc, const Code& code) const {
|
||||
const intptr_t i = BinarySearchInSCallTable(pc);
|
||||
ASSERT(i >= 0);
|
||||
|
|
|
@ -1477,6 +1477,13 @@ class Function : public Object {
|
|||
// Sets function's code and code's function.
|
||||
void SetCode(const Code& value) const;
|
||||
|
||||
// Detaches code from the function by setting the code to null, and patches
|
||||
// the code to be non-entrant.
|
||||
void DetachCode() const;
|
||||
|
||||
// Reattaches code to the function, and patches the code to be entrant.
|
||||
void ReattachCode(const Code& code) const;
|
||||
|
||||
// Disables optimized code and switches to unoptimized code.
|
||||
void SwitchToUnoptimizedCode() const;
|
||||
|
||||
|
@ -1487,6 +1494,9 @@ class Function : public Object {
|
|||
RawCode* unoptimized_code() const { return raw_ptr()->unoptimized_code_; }
|
||||
void set_unoptimized_code(const Code& value) const;
|
||||
static intptr_t code_offset() { return OFFSET_OF(RawFunction, code_); }
|
||||
static intptr_t unoptimized_code_offset() {
|
||||
return OFFSET_OF(RawFunction, unoptimized_code_);
|
||||
}
|
||||
inline bool HasCode() const;
|
||||
|
||||
// Returns true if there is at least one debugger breakpoint
|
||||
|
@ -2659,6 +2669,9 @@ class Instructions : public Object {
|
|||
public:
|
||||
intptr_t size() const { return raw_ptr()->size_; } // Excludes HeaderSize().
|
||||
RawCode* code() const { return raw_ptr()->code_; }
|
||||
static intptr_t code_offset() {
|
||||
return OFFSET_OF(RawInstructions, code_);
|
||||
}
|
||||
RawArray* object_pool() const { return raw_ptr()->object_pool_; }
|
||||
static intptr_t object_pool_offset() {
|
||||
return OFFSET_OF(RawInstructions, object_pool_);
|
||||
|
@ -3121,6 +3134,8 @@ class Code : public Object {
|
|||
|
||||
// Returns null if there is no static call at 'pc'.
|
||||
RawFunction* GetStaticCallTargetFunctionAt(uword pc) const;
|
||||
// Returns null if there is no static call at 'pc'.
|
||||
RawCode* GetStaticCallTargetCodeAt(uword pc) const;
|
||||
// Aborts if there is no static call at 'pc'.
|
||||
void SetStaticCallTargetCodeAt(uword pc, const Code& code) const;
|
||||
|
||||
|
|
|
@ -23,6 +23,12 @@ DEFINE_FLAG(bool, print_free_list_before_gc, false,
|
|||
"Print free list statistics before a GC");
|
||||
DEFINE_FLAG(bool, print_free_list_after_gc, false,
|
||||
"Print free list statistics after a GC");
|
||||
DEFINE_FLAG(bool, collect_code, true,
|
||||
"Attempt to GC infrequently used code.");
|
||||
DEFINE_FLAG(int, code_collection_interval_in_us, 30000000,
|
||||
"Time between attempts to collect unused code.");
|
||||
DEFINE_FLAG(bool, log_code_drop, false,
|
||||
"Emit a log message when pointers to unused code are dropped.");
|
||||
|
||||
HeapPage* HeapPage::Initialize(VirtualMemory* memory, PageType type) {
|
||||
ASSERT(memory->size() > VirtualMemory::PageSize());
|
||||
|
@ -378,11 +384,68 @@ void PageSpace::WriteProtect(bool read_only) {
|
|||
}
|
||||
|
||||
|
||||
|
||||
class CodeDetacherVisitor : public ObjectVisitor {
|
||||
public:
|
||||
explicit CodeDetacherVisitor(Isolate* isolate) : ObjectVisitor(isolate) { }
|
||||
|
||||
virtual void VisitObject(RawObject* obj);
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(CodeDetacherVisitor);
|
||||
};
|
||||
|
||||
|
||||
void CodeDetacherVisitor::VisitObject(RawObject* raw_obj) {
|
||||
Isolate* isolate = Isolate::Current();
|
||||
HANDLESCOPE(isolate);
|
||||
const Object& obj = Object::Handle(raw_obj);
|
||||
if (obj.GetClassId() == kFunctionCid) {
|
||||
const Function& fn = Function::Cast(obj);
|
||||
if (!fn.HasOptimizedCode() &&
|
||||
!fn.HasBreakpoint() &&
|
||||
fn.HasCode() && // Not already detached.
|
||||
(fn.usage_counter() > 0)) {
|
||||
fn.set_usage_counter(fn.usage_counter() / 2);
|
||||
if (fn.usage_counter() == 0) {
|
||||
if (FLAG_log_code_drop) {
|
||||
const String& name = String::Handle(fn.name());
|
||||
OS::Print("Detaching code for function %s\n", name.ToCString());
|
||||
}
|
||||
fn.DetachCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PageSpace::TryDetachingCode() {
|
||||
// Try to collect code if enough time has passed since the last attempt.
|
||||
const int64_t start = OS::GetCurrentTimeMicros();
|
||||
const int64_t last_code_collection_in_us =
|
||||
page_space_controller_.last_code_collection_in_us();
|
||||
if ((start - last_code_collection_in_us) >
|
||||
FLAG_code_collection_interval_in_us) {
|
||||
if (FLAG_log_code_drop) {
|
||||
OS::Print("Trying to detach code.\n");
|
||||
}
|
||||
Isolate* isolate = Isolate::Current();
|
||||
CodeDetacherVisitor code_detacher(isolate);
|
||||
heap_->IterateObjects(&code_detacher);
|
||||
page_space_controller_.set_last_code_collection_in_us(start);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PageSpace::MarkSweep(bool invoke_api_callbacks) {
|
||||
// MarkSweep is not reentrant. Make sure that is the case.
|
||||
ASSERT(!sweeping_);
|
||||
sweeping_ = true;
|
||||
Isolate* isolate = Isolate::Current();
|
||||
if (FLAG_collect_code) {
|
||||
TryDetachingCode();
|
||||
}
|
||||
|
||||
NoHandleScope no_handles(isolate);
|
||||
|
||||
if (FLAG_print_free_list_before_gc) {
|
||||
|
@ -398,7 +461,7 @@ void PageSpace::MarkSweep(bool invoke_api_callbacks) {
|
|||
OS::PrintErr(" done.\n");
|
||||
}
|
||||
|
||||
int64_t start = OS::GetCurrentTimeMicros();
|
||||
const int64_t start = OS::GetCurrentTimeMicros();
|
||||
|
||||
// Mark all reachable old-gen objects.
|
||||
GCMarker marker(heap_);
|
||||
|
@ -490,7 +553,8 @@ PageSpaceController::PageSpaceController(int heap_growth_ratio,
|
|||
heap_growth_ratio_(heap_growth_ratio),
|
||||
desired_utilization_((100.0 - heap_growth_ratio) / 100.0),
|
||||
heap_growth_rate_(heap_growth_rate),
|
||||
garbage_collection_time_ratio_(garbage_collection_time_ratio) {
|
||||
garbage_collection_time_ratio_(garbage_collection_time_ratio),
|
||||
last_code_collection_in_us_(OS::GetCurrentTimeMicros()) {
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
|
||||
namespace dart {
|
||||
|
||||
DECLARE_FLAG(bool, collect_code);
|
||||
DECLARE_FLAG(bool, log_code_drop);
|
||||
|
||||
// Forward declarations.
|
||||
class Heap;
|
||||
class ObjectPointerVisitor;
|
||||
|
@ -121,6 +124,11 @@ class PageSpaceController {
|
|||
void EvaluateGarbageCollection(intptr_t in_use_before, intptr_t in_use_after,
|
||||
int64_t start, int64_t end);
|
||||
|
||||
int64_t last_code_collection_in_us() { return last_code_collection_in_us_; }
|
||||
void set_last_code_collection_in_us(int64_t t) {
|
||||
last_code_collection_in_us_ = t;
|
||||
}
|
||||
|
||||
void set_is_enabled(bool state) {
|
||||
is_enabled_ = state;
|
||||
}
|
||||
|
@ -149,6 +157,10 @@ class PageSpaceController {
|
|||
// garbage collection can be performed.
|
||||
int garbage_collection_time_ratio_;
|
||||
|
||||
// The time in microseconds of the last time we tried to collect unused
|
||||
// code.
|
||||
int64_t last_code_collection_in_us_;
|
||||
|
||||
PageSpaceGarbageCollectionHistory history_;
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(PageSpaceController);
|
||||
|
@ -188,6 +200,10 @@ class PageSpace {
|
|||
RawObject* FindObject(FindObjectVisitor* visitor,
|
||||
HeapPage::PageType type) const;
|
||||
|
||||
// Runs a visitor that attempts to drop references to code that has not
|
||||
// been run in awhile.
|
||||
void TryDetachingCode();
|
||||
|
||||
// Collect the garbage in the page space using mark-sweep.
|
||||
void MarkSweep(bool invoke_api_callbacks);
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ class RawCode;
|
|||
V(JumpToExceptionHandler) \
|
||||
V(UnoptimizedIdenticalWithNumberCheck) \
|
||||
V(OptimizedIdenticalWithNumberCheck) \
|
||||
V(CompileFunctionRuntimeCall) \
|
||||
|
||||
// Is it permitted for the stubs above to refer to Object::null(), which is
|
||||
// allocated in the VM isolate and shared across all isolates.
|
||||
|
|
|
@ -1589,8 +1589,25 @@ void StubCode::GenerateNArgsCheckInlineCacheStub(
|
|||
|
||||
__ Bind(&call_target_function);
|
||||
// R0: target function.
|
||||
__ ldr(R0, FieldAddress(R0, Function::code_offset()));
|
||||
__ ldr(R0, FieldAddress(R0, Code::instructions_offset()));
|
||||
__ ldr(R1, FieldAddress(R0, Function::code_offset()));
|
||||
if (FLAG_collect_code) {
|
||||
// If we are collecting code, the code object may be null.
|
||||
Label is_compiled;
|
||||
__ CompareImmediate(R1, reinterpret_cast<intptr_t>(Object::null()));
|
||||
__ b(&is_compiled, NE);
|
||||
__ EnterStubFrame();
|
||||
// Preserve arg desc. and IC data object.
|
||||
__ PushList((1 << R4) | (1 << R5));
|
||||
__ Push(R0); // Pass function.
|
||||
__ CallRuntime(kCompileFunctionRuntimeEntry, 1);
|
||||
__ Pop(R0); // Discard argument.
|
||||
__ PopList((1 << R4) | (1 << R5)); // Restore arg desc. and IC data.
|
||||
__ LeaveStubFrame();
|
||||
// R0: target function.
|
||||
__ ldr(R1, FieldAddress(R0, Function::code_offset()));
|
||||
__ Bind(&is_compiled);
|
||||
}
|
||||
__ ldr(R0, FieldAddress(R1, Code::instructions_offset()));
|
||||
__ AddImmediate(R0, Instructions::HeaderSize() - kHeapObjectTag);
|
||||
__ bx(R0);
|
||||
|
||||
|
@ -1758,6 +1775,23 @@ void StubCode::GenerateTwoArgsUnoptimizedStaticCallStub(Assembler* assembler) {
|
|||
}
|
||||
|
||||
|
||||
// Stub for calling the CompileFunction runtime call.
|
||||
// R5: IC-Data.
|
||||
// R4: Arguments descriptor.
|
||||
// R0: Function.
|
||||
void StubCode::GenerateCompileFunctionRuntimeCallStub(Assembler* assembler) {
|
||||
// Preserve arg desc. and IC data object.
|
||||
__ EnterStubFrame();
|
||||
__ PushList((1 << R4) | (1 << R5));
|
||||
__ Push(R0); // Pass function.
|
||||
__ CallRuntime(kCompileFunctionRuntimeEntry, 1);
|
||||
__ Pop(R0); // Restore argument.
|
||||
__ PopList((1 << R4) | (1 << R5)); // Restore arg desc. and IC data.
|
||||
__ LeaveStubFrame();
|
||||
__ Ret();
|
||||
}
|
||||
|
||||
|
||||
void StubCode::GenerateBreakpointRuntimeStub(Assembler* assembler) {
|
||||
__ Comment("BreakpointRuntime stub");
|
||||
__ EnterStubFrame();
|
||||
|
|
|
@ -1613,8 +1613,25 @@ void StubCode::GenerateNArgsCheckInlineCacheStub(
|
|||
|
||||
__ Bind(&call_target_function);
|
||||
// EAX: Target function.
|
||||
__ movl(EAX, FieldAddress(EAX, Function::code_offset()));
|
||||
__ movl(EAX, FieldAddress(EAX, Code::instructions_offset()));
|
||||
Label is_compiled;
|
||||
__ movl(EBX, FieldAddress(EAX, Function::code_offset()));
|
||||
if (FLAG_collect_code) {
|
||||
// If code might be GC'd, then EBX might be null. If it is, recompile.
|
||||
__ cmpl(EBX, raw_null);
|
||||
__ j(NOT_EQUAL, &is_compiled, Assembler::kNearJump);
|
||||
__ EnterStubFrame();
|
||||
__ pushl(EDX); // Preserve arguments descriptor array.
|
||||
__ pushl(ECX); // Preserve IC data object.
|
||||
__ pushl(EAX); // Pass function.
|
||||
__ CallRuntime(kCompileFunctionRuntimeEntry, 1);
|
||||
__ popl(EAX); // Restore function.
|
||||
__ popl(ECX); // Restore IC data array.
|
||||
__ popl(EDX); // Restore arguments descriptor array.
|
||||
__ LeaveFrame();
|
||||
__ movl(EBX, FieldAddress(EAX, Function::code_offset()));
|
||||
__ Bind(&is_compiled);
|
||||
}
|
||||
__ movl(EAX, FieldAddress(EBX, Code::instructions_offset()));
|
||||
__ addl(EAX, Immediate(Instructions::HeaderSize() - kHeapObjectTag));
|
||||
__ jmp(EAX);
|
||||
|
||||
|
@ -1796,6 +1813,24 @@ void StubCode::GenerateTwoArgsUnoptimizedStaticCallStub(Assembler* assembler) {
|
|||
}
|
||||
|
||||
|
||||
// Stub for calling the CompileFunction runtime call.
|
||||
// ECX: IC-Data.
|
||||
// EDX: Arguments descriptor.
|
||||
// EAX: Function.
|
||||
void StubCode::GenerateCompileFunctionRuntimeCallStub(Assembler* assembler) {
|
||||
__ EnterStubFrame();
|
||||
__ pushl(EDX); // Preserve arguments descriptor array.
|
||||
__ pushl(ECX); // Preserve IC data object.
|
||||
__ pushl(EAX); // Pass function.
|
||||
__ CallRuntime(kCompileFunctionRuntimeEntry, 1);
|
||||
__ popl(EAX); // Restore function.
|
||||
__ popl(ECX); // Restore IC data array.
|
||||
__ popl(EDX); // Restore arguments descriptor array.
|
||||
__ LeaveFrame();
|
||||
__ ret();
|
||||
}
|
||||
|
||||
|
||||
// EDX, ECX: May contain arguments to runtime stub.
|
||||
void StubCode::GenerateBreakpointRuntimeStub(Assembler* assembler) {
|
||||
__ EnterStubFrame();
|
||||
|
|
|
@ -1812,8 +1812,26 @@ void StubCode::GenerateNArgsCheckInlineCacheStub(
|
|||
|
||||
__ Bind(&call_target_function);
|
||||
// T3: Target function.
|
||||
__ lw(T3, FieldAddress(T3, Function::code_offset()));
|
||||
__ lw(T3, FieldAddress(T3, Code::instructions_offset()));
|
||||
Label is_compiled;
|
||||
__ lw(T4, FieldAddress(T3, Function::code_offset()));
|
||||
if (FLAG_collect_code) {
|
||||
__ BranchNotEqual(T4, reinterpret_cast<int32_t>(Object::null()),
|
||||
&is_compiled);
|
||||
__ EnterStubFrame();
|
||||
__ addiu(SP, SP, Immediate(-3 * kWordSize));
|
||||
__ sw(S5, Address(SP, 2 * kWordSize)); // Preserve IC data.
|
||||
__ sw(S4, Address(SP, 1 * kWordSize)); // Preserve arg desc.
|
||||
__ sw(T3, Address(SP, 0 * kWordSize)); // Function argument.
|
||||
__ CallRuntime(kCompileFunctionRuntimeEntry, 1);
|
||||
__ lw(T3, Address(SP, 0 * kWordSize)); // Restore Function.
|
||||
__ lw(S4, Address(SP, 1 * kWordSize)); // Restore arg desc.
|
||||
__ lw(S5, Address(SP, 2 * kWordSize)); // Restore IC data.
|
||||
__ addiu(SP, SP, Immediate(3 * kWordSize));
|
||||
__ LeaveStubFrame();
|
||||
__ lw(T4, FieldAddress(T3, Function::code_offset()));
|
||||
__ Bind(&is_compiled);
|
||||
}
|
||||
__ lw(T3, FieldAddress(T4, Code::instructions_offset()));
|
||||
__ AddImmediate(T3, Instructions::HeaderSize() - kHeapObjectTag);
|
||||
__ jr(T3);
|
||||
|
||||
|
@ -1993,6 +2011,24 @@ void StubCode::GenerateTwoArgsUnoptimizedStaticCallStub(Assembler* assembler) {
|
|||
}
|
||||
|
||||
|
||||
// Stub for calling the CompileFunction runtime call.
|
||||
// S5: IC-Data.
|
||||
// S4: Arguments descriptor.
|
||||
// T0: Function.
|
||||
void StubCode::GenerateCompileFunctionRuntimeCallStub(Assembler* assembler) {
|
||||
__ EnterStubFrame();
|
||||
__ addiu(SP, SP, Immediate(-3 * kWordSize));
|
||||
__ sw(S5, Address(SP, 2 * kWordSize)); // Preserve IC data object.
|
||||
__ sw(S4, Address(SP, 1 * kWordSize)); // Preserve args descriptor array.
|
||||
__ sw(T0, Address(SP, 0 * kWordSize)); // Pass function.
|
||||
__ CallRuntime(kCompileFunctionRuntimeEntry, 1);
|
||||
__ lw(T0, Address(SP, 0 * kWordSize)); // Restore function.
|
||||
__ lw(S4, Address(SP, 1 * kWordSize)); // Restore args descriptor array.
|
||||
__ lw(S5, Address(SP, 2 * kWordSize)); // Restore IC data array.
|
||||
__ LeaveStubFrameAndReturn();
|
||||
}
|
||||
|
||||
|
||||
void StubCode::GenerateBreakpointRuntimeStub(Assembler* assembler) {
|
||||
__ Comment("BreakpointRuntime stub");
|
||||
__ EnterStubFrame();
|
||||
|
|
|
@ -1600,8 +1600,25 @@ void StubCode::GenerateNArgsCheckInlineCacheStub(
|
|||
|
||||
__ Bind(&call_target_function);
|
||||
// RAX: Target function.
|
||||
__ movq(RAX, FieldAddress(RAX, Function::code_offset()));
|
||||
__ movq(RAX, FieldAddress(RAX, Code::instructions_offset()));
|
||||
Label is_compiled;
|
||||
__ movq(RCX, FieldAddress(RAX, Function::code_offset()));
|
||||
if (FLAG_collect_code) {
|
||||
// If code might be GC'd, then EBX might be null. If it is, recompile.
|
||||
__ CompareObject(RCX, Object::null_object(), PP);
|
||||
__ j(NOT_EQUAL, &is_compiled, Assembler::kNearJump);
|
||||
__ EnterStubFrame();
|
||||
__ pushq(R10); // Preserve arguments descriptor array.
|
||||
__ pushq(RBX); // Preserve IC data object.
|
||||
__ pushq(RAX); // Pass function.
|
||||
__ CallRuntime(kCompileFunctionRuntimeEntry, 1);
|
||||
__ popq(RAX); // Restore function.
|
||||
__ popq(RBX); // Restore IC data array.
|
||||
__ popq(R10); // Restore arguments descriptor array.
|
||||
__ LeaveFrame();
|
||||
__ movq(RCX, FieldAddress(RAX, Function::code_offset()));
|
||||
__ Bind(&is_compiled);
|
||||
}
|
||||
__ movq(RAX, FieldAddress(RCX, Code::instructions_offset()));
|
||||
__ addq(RAX, Immediate(Instructions::HeaderSize() - kHeapObjectTag));
|
||||
__ jmp(RAX);
|
||||
|
||||
|
@ -1782,6 +1799,24 @@ void StubCode::GenerateTwoArgsUnoptimizedStaticCallStub(Assembler* assembler) {
|
|||
}
|
||||
|
||||
|
||||
// Stub for calling the CompileFunction runtime call.
|
||||
// RCX: IC-Data.
|
||||
// RDX: Arguments descriptor.
|
||||
// RAX: Function.
|
||||
void StubCode::GenerateCompileFunctionRuntimeCallStub(Assembler* assembler) {
|
||||
__ EnterStubFrame();
|
||||
__ pushq(RDX); // Preserve arguments descriptor array.
|
||||
__ pushq(RCX); // Preserve IC data object.
|
||||
__ pushq(RAX); // Pass function.
|
||||
__ CallRuntime(kCompileFunctionRuntimeEntry, 1);
|
||||
__ popq(RAX); // Restore function.
|
||||
__ popq(RCX); // Restore IC data array.
|
||||
__ popq(RDX); // Restore arguments descriptor array.
|
||||
__ LeaveFrame();
|
||||
__ ret();
|
||||
}
|
||||
|
||||
|
||||
// RBX, R10: May contain arguments to runtime stub.
|
||||
// TOS(0): return address (Dart code).
|
||||
void StubCode::GenerateBreakpointRuntimeStub(Assembler* assembler) {
|
||||
|
|
68
tests/standalone/code_collection_test.dart
Normal file
68
tests/standalone/code_collection_test.dart
Normal file
|
@ -0,0 +1,68 @@
|
|||
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// Dart test program testing code GC.
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
import "dart:async";
|
||||
import "dart:io";
|
||||
|
||||
|
||||
int foo(int x) {
|
||||
x = x + 1;
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
List<int> bar() {
|
||||
// A couple of big allocations trigger GC.
|
||||
var l = new List.filled(700000, 7);
|
||||
return l;
|
||||
}
|
||||
|
||||
|
||||
doTest() {
|
||||
var i = 0;
|
||||
foo(1); // Initial call to compile.
|
||||
// Time passes, GC runs, foo's code is dropped.
|
||||
var ms = const Duration(milliseconds: 100);
|
||||
var t = new Timer.periodic(ms, (timer) {
|
||||
i++;
|
||||
bar();
|
||||
if (i > 1) {
|
||||
timer.cancel();
|
||||
// foo is called again to make sure we can still run it even after
|
||||
// its code has been detached.
|
||||
foo(2);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
main() {
|
||||
var opts = new Options();
|
||||
if (opts.arguments.contains("--run")) {
|
||||
doTest();
|
||||
} else {
|
||||
// Run the test and capture stdout.
|
||||
var pr = Process.runSync(Platform.executable,
|
||||
["--collect-code",
|
||||
"--code-collection-interval-in-us=100000",
|
||||
"--log-code-drop",
|
||||
"--optimization-counter-threshold=-1",
|
||||
"--package-root=${Platform.packageRoot}",
|
||||
Platform.script,
|
||||
"--run"]);
|
||||
|
||||
// Code drops are logged with --log-code-drop. Look through stdout for the
|
||||
// message that foo's code was dropped.
|
||||
var found = false;
|
||||
pr.stdout.split("\n").forEach((line) {
|
||||
if (line.contains("Detaching code") && line.contains("foo")) {
|
||||
found = true;
|
||||
}
|
||||
});
|
||||
Expect.isTrue(found);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue