bool LCodeGen::GenerateDeferredCode() {
ASSERT(is_generating());
- for (int i = 0; !is_aborted() && i < deferred_.length(); i++) {
- LDeferredCode* code = deferred_[i];
- __ bind(code->entry());
- code->Generate();
- __ jmp(code->exit());
+ Label last_jump;
+ if (deferred_.length() > 0) {
+ for (int i = 0; !is_aborted() && i < deferred_.length(); i++) {
+ LDeferredCode* code = deferred_[i];
+ __ bind(code->entry());
+ code->Generate();
+#ifdef DEBUG
+ if (i == deferred_.length() - 1) {
+ __ bind(&last_jump);
+ }
+#endif
+ __ jmp(code->exit());
+ }
+
+ // Reserve some space to ensure that the last piece of deferred code
+ // have room for lazy bailout.
+ __ nop();
+ __ nop();
+
+ int code_generated =
+ masm_->InstructionsGeneratedSince(&last_jump) * Assembler::kInstrSize;
+ ASSERT(Deoptimizer::patch_size() <= code_generated);
}
// Force constant pool emission at the end of the deferred code to make
void LCodeGen::DoDeferredStackCheck(LStackCheck* instr) {
- PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters);
- CallRuntimeFromDeferred(Runtime::kStackGuard, 0, instr);
+ {
+ PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters);
+ __ CallRuntimeSaveDoubles(Runtime::kStackGuard);
+ RegisterLazyDeoptimization(
+ instr, RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS);
+ }
+
+ // The gap code includes the restoring of the safepoint registers.
+ int pc = masm()->pc_offset();
+ safepoints_.SetPcAfterGap(pc);
}
// Bailout support.
int OsrEntryId() const { return osr_entry_id_; }
virtual int ContinueId() const = 0;
+ virtual int StackCheckId() const = 0;
// Code generation
Label* continue_target() { return &continue_target_; }
// Bailout support.
virtual int ContinueId() const { return continue_id_; }
+ virtual int StackCheckId() const { return back_edge_id_; }
int BackEdgeId() const { return back_edge_id_; }
virtual bool IsInlineable() const;
// Bailout support.
virtual int ContinueId() const { return EntryId(); }
+ virtual int StackCheckId() const { return body_id_; }
int BodyId() const { return body_id_; }
private:
// Bailout support.
virtual int ContinueId() const { return continue_id_; }
+ virtual int StackCheckId() const { return body_id_; }
int BodyId() const { return body_id_; }
bool is_fast_smi_loop() { return loop_variable_ != NULL; }
// Bailout support.
int AssignmentId() const { return assignment_id_; }
virtual int ContinueId() const { return EntryId(); }
+ virtual int StackCheckId() const { return EntryId(); }
private:
Expression* each_;
}
-void HGraphBuilder::VisitLoopBody(Statement* body,
+void HGraphBuilder::VisitLoopBody(IterationStatement* stmt,
HBasicBlock* loop_entry,
BreakAndContinueInfo* break_info) {
BreakAndContinueScope push(break_info, this);
+ AddSimulate(stmt->StackCheckId());
HValue* context = environment()->LookupContext();
HStackCheck* stack_check =
new(zone()) HStackCheck(context, HStackCheck::kBackwardsBranch);
AddInstruction(stack_check);
ASSERT(loop_entry->IsLoopHeader());
loop_entry->loop_information()->set_stack_check(stack_check);
- CHECK_BAILOUT(Visit(body));
+ CHECK_BAILOUT(Visit(stmt->body()));
}
set_current_block(loop_entry);
BreakAndContinueInfo break_info(stmt);
- CHECK_BAILOUT(VisitLoopBody(stmt->body(), loop_entry, &break_info));
+ CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info));
HBasicBlock* body_exit =
JoinContinue(stmt, current_block(), break_info.continue_block());
HBasicBlock* loop_successor = NULL;
BreakAndContinueInfo break_info(stmt);
if (current_block() != NULL) {
BreakAndContinueScope push(&break_info, this);
- CHECK_BAILOUT(VisitLoopBody(stmt->body(), loop_entry, &break_info));
+ CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info));
}
HBasicBlock* body_exit =
JoinContinue(stmt, current_block(), break_info.continue_block());
BreakAndContinueInfo break_info(stmt);
if (current_block() != NULL) {
BreakAndContinueScope push(&break_info, this);
- CHECK_BAILOUT(VisitLoopBody(stmt->body(), loop_entry, &break_info));
+ CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info));
}
HBasicBlock* body_exit =
JoinContinue(stmt, current_block(), break_info.continue_block());
void PreProcessOsrEntry(IterationStatement* statement);
// True iff. we are compiling for OSR and the statement is the entry.
bool HasOsrEntryAt(IterationStatement* statement);
- void VisitLoopBody(Statement* body,
+ void VisitLoopBody(IterationStatement* stmt,
HBasicBlock* loop_entry,
BreakAndContinueInfo* break_info);
bool LCodeGen::GenerateDeferredCode() {
ASSERT(is_generating());
- for (int i = 0; !is_aborted() && i < deferred_.length(); i++) {
- LDeferredCode* code = deferred_[i];
- __ bind(code->entry());
- code->Generate();
- __ jmp(code->exit());
+ Label last_jump;
+ if (deferred_.length() > 0) {
+ for (int i = 0; !is_aborted() && i < deferred_.length(); i++) {
+ LDeferredCode* code = deferred_[i];
+ __ bind(code->entry());
+ code->Generate();
+#ifdef DEBUG
+ if (i == deferred_.length() - 1) {
+ __ bind(&last_jump);
+ }
+#endif
+ __ jmp(code->exit());
+ }
+
+ // Reserve some space to ensure that the last piece of deferred code
+ // have room for lazy bailout.
+ __ nop();
+ __ nop();
+ __ nop();
+
+ ASSERT(Deoptimizer::patch_size() <=
+ masm_->SizeOfCodeGeneratedSince(&last_jump));
}
// Deferred code is the last part of the instruction sequence. Mark
void LCodeGen::DoDeferredStackCheck(LStackCheck* instr) {
- PushSafepointRegistersScope scope(this);
- CallRuntimeFromDeferred(Runtime::kStackGuard, 0, instr, instr->context());
+ {
+ PushSafepointRegistersScope scope(this);
+ __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
+ __ CallRuntimeSaveDoubles(Runtime::kStackGuard);
+ RegisterLazyDeoptimization(
+ instr, RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS);
+ }
+
+ // The gap code includes the restoring of the safepoint registers.
+ int pc = masm()->pc_offset();
+ safepoints_.SetPcAfterGap(pc);
}
bool LCodeGen::GenerateDeferredCode() {
ASSERT(is_generating());
- for (int i = 0; !is_aborted() && i < deferred_.length(); i++) {
- LDeferredCode* code = deferred_[i];
- __ bind(code->entry());
- code->Generate();
- __ jmp(code->exit());
+ Label last_jump;
+ if (deferred_.length() > 0) {
+ for (int i = 0; !is_aborted() && i < deferred_.length(); i++) {
+ LDeferredCode* code = deferred_[i];
+ __ bind(code->entry());
+ code->Generate();
+#ifdef DEBUG
+ if (i == (deferred_.length() - 1)) {
+ __ bind(&last_jump);
+ }
+#endif
+ __ jmp(code->exit());
+ }
+
+ // Reserve some space to ensure that the last piece of deferred code
+ // have room for lazy bailout.
+ int padding =
+ Deoptimizer::patch_size() - masm_->SizeOfCodeGeneratedSince(&last_jump);
+ if (padding > 0) {
+ while (padding > 9) {
+ __ nop(9);
+ padding -= 9;
+ }
+ __ nop(padding);
+ }
+
+ ASSERT(Deoptimizer::patch_size() <=
+ masm_->SizeOfCodeGeneratedSince(&last_jump));
}
// Deferred code is the last part of the instruction sequence. Mark
void LCodeGen::DoDeferredStackCheck(LStackCheck* instr) {
- PushSafepointRegistersScope scope(this);
- CallRuntimeFromDeferred(Runtime::kStackGuard, 0, instr);
+ {
+ PushSafepointRegistersScope scope(this);
+ __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
+ __ CallRuntimeSaveDoubles(Runtime::kStackGuard);
+ RegisterLazyDeoptimization(instr, RECORD_SAFEPOINT_WITH_REGISTERS, 0);
+ }
+
+ // The gap code includes the restoring of the safepoint registers.
+ int pc = masm()->pc_offset();
+ safepoints_.SetPcAfterGap(pc);
}
// Debug event handler which counts the break points which have been hit.
int break_point_hit_count = 0;
+int break_point_hit_count_deoptimize = 0;
static void DebugEventBreakPointHitCount(v8::DebugEvent event,
v8::Handle<v8::Object> exec_state,
v8::Handle<v8::Object> event_data,
script_data->WriteAscii(last_script_data_hit);
}
}
+
+ // Perform a full deoptimization when the specified number of
+ // breaks have been hit.
+ if (break_point_hit_count == break_point_hit_count_deoptimize) {
+ i::Deoptimizer::DeoptimizeAll();
+ }
} else if (event == v8::AfterCompile && !compiled_script_data.IsEmpty()) {
const int argc = 1;
v8::Handle<v8::Value> argv[argc] = { event_data };
// Count the number of breaks.
break_point_hit_count++;
+ // Collect the JavsScript stack height if the function frame_count is
+ // compiled.
+ if (!frame_count.IsEmpty()) {
+ static const int kArgc = 1;
+ v8::Handle<v8::Value> argv[kArgc] = { exec_state };
+ // Using exec_state as receiver is just to have a receiver.
+ v8::Handle<v8::Value> result =
+ frame_count->Call(exec_state, kArgc, argv);
+ last_js_stack_height = result->Int32Value();
+ }
+
// Set the break flag again to come back here as soon as possible.
v8::Debug::DebugBreak();
+
} else if (terminate_after_max_break_point_hit) {
// Terminate execution after the last break if requested.
v8::V8::TerminateExecution();
}
+
+ // Perform a full deoptimization when the specified number of
+ // breaks have been hit.
+ if (break_point_hit_count == break_point_hit_count_deoptimize) {
+ i::Deoptimizer::DeoptimizeAll();
+ }
}
}
const char** loop_bodies,
const char* loop_tail) {
// Receive 100 breaks for each test and then terminate JavaScript execution.
- static int count = 0;
+ static const int kBreaksPerTest = 100;
+
+ for (int i = 0; i < 1 && loop_bodies[i] != NULL; i++) {
+ // Perform a lazy deoptimization after various numbers of breaks
+ // have been hit.
+ for (int j = 0; j < 10; j++) {
+ break_point_hit_count_deoptimize = j;
+ if (j == 10) {
+ break_point_hit_count_deoptimize = kBreaksPerTest;
+ }
- for (int i = 0; loop_bodies[i] != NULL; i++) {
- count++;
- max_break_point_hit_count = count * 100;
- terminate_after_max_break_point_hit = true;
+ break_point_hit_count = 0;
+ max_break_point_hit_count = kBreaksPerTest;
+ terminate_after_max_break_point_hit = true;
- EmbeddedVector<char, 1024> buffer;
- OS::SNPrintF(buffer,
- "function f() {%s%s%s}",
- loop_head, loop_bodies[i], loop_tail);
+ EmbeddedVector<char, 1024> buffer;
+ OS::SNPrintF(buffer,
+ "function f() {%s%s%s}",
+ loop_head, loop_bodies[i], loop_tail);
- // Function with infinite loop.
- CompileRun(buffer.start());
+ // Function with infinite loop.
+ CompileRun(buffer.start());
- // Set the debug break to enter the debugger as soon as possible.
- v8::Debug::DebugBreak();
+ // Set the debug break to enter the debugger as soon as possible.
+ v8::Debug::DebugBreak();
- // Call function with infinite loop.
- CompileRun("f();");
- CHECK_EQ(count * 100, break_point_hit_count);
+ // Call function with infinite loop.
+ CompileRun("f();");
+ CHECK_EQ(kBreaksPerTest, break_point_hit_count);
- CHECK(!v8::V8::IsExecutionTerminating());
+ CHECK(!v8::V8::IsExecutionTerminating());
+ }
}
}
// Register a debug event listener which sets the break flag and counts.
v8::Debug::SetDebugEventListener(DebugEventBreakMax);
+ // Create a function for getting the frame count when hitting the break.
+ frame_count = CompileFunction(&env, frame_count_source, "frame_count");
+
CompileRun("var a = 1;");
CompileRun("function g() { }");
CompileRun("function h() { }");