Fix lazy deopt after tagged binary ops
Also add policing code to ensure that optimized frames can in fact lazily deopt at their respective current PC when we patch them for lazy bailout. BUG=chromium:350434 LOG=y R=jarin@chromium.org Review URL: https://codereview.chromium.org/194703008 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@19834 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
25468478e3
commit
8a1812f252
@ -506,15 +506,6 @@ LInstruction* LChunkBuilder::MarkAsCall(LInstruction* instr,
|
|||||||
instr->MarkAsCall();
|
instr->MarkAsCall();
|
||||||
instr = AssignPointerMap(instr);
|
instr = AssignPointerMap(instr);
|
||||||
|
|
||||||
if (hinstr->HasObservableSideEffects()) {
|
|
||||||
ASSERT(hinstr->next()->IsSimulate());
|
|
||||||
HSimulate* sim = HSimulate::cast(hinstr->next());
|
|
||||||
ASSERT(instruction_pending_deoptimization_environment_ == NULL);
|
|
||||||
ASSERT(pending_deoptimization_ast_id_.IsNone());
|
|
||||||
instruction_pending_deoptimization_environment_ = instr;
|
|
||||||
pending_deoptimization_ast_id_ = sim->ast_id();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If instruction does not have side-effects lazy deoptimization
|
// If instruction does not have side-effects lazy deoptimization
|
||||||
// after the call will try to deoptimize to the point before the call.
|
// after the call will try to deoptimize to the point before the call.
|
||||||
// Thus we still need to attach environment to this call even if
|
// Thus we still need to attach environment to this call even if
|
||||||
@ -736,6 +727,26 @@ void LChunkBuilder::VisitInstruction(HInstruction* current) {
|
|||||||
instr = AssignEnvironment(instr);
|
instr = AssignEnvironment(instr);
|
||||||
}
|
}
|
||||||
chunk_->AddInstruction(instr, current_block_);
|
chunk_->AddInstruction(instr, current_block_);
|
||||||
|
|
||||||
|
if (instr->IsCall()) {
|
||||||
|
HValue* hydrogen_value_for_lazy_bailout = current;
|
||||||
|
LInstruction* instruction_needing_environment = NULL;
|
||||||
|
if (current->HasObservableSideEffects()) {
|
||||||
|
HSimulate* sim = HSimulate::cast(current->next());
|
||||||
|
instruction_needing_environment = instr;
|
||||||
|
sim->ReplayEnvironment(current_block_->last_environment());
|
||||||
|
hydrogen_value_for_lazy_bailout = sim;
|
||||||
|
}
|
||||||
|
LInstruction* bailout = AssignEnvironment(new(zone()) LLazyBailout());
|
||||||
|
bailout->set_hydrogen_value(hydrogen_value_for_lazy_bailout);
|
||||||
|
chunk_->AddInstruction(bailout, current_block_);
|
||||||
|
if (instruction_needing_environment != NULL) {
|
||||||
|
// Store the lazy deopt environment with the instruction if needed.
|
||||||
|
// Right now it is only used for LInstanceOfKnownGlobal.
|
||||||
|
instruction_needing_environment->
|
||||||
|
SetDeferredLazyDeoptimizationEnvironment(bailout->environment());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
current_instruction_ = old_current;
|
current_instruction_ = old_current;
|
||||||
}
|
}
|
||||||
@ -2106,21 +2117,6 @@ LInstruction* LChunkBuilder::DoShr(HShr* instr) {
|
|||||||
|
|
||||||
LInstruction* LChunkBuilder::DoSimulate(HSimulate* instr) {
|
LInstruction* LChunkBuilder::DoSimulate(HSimulate* instr) {
|
||||||
instr->ReplayEnvironment(current_block_->last_environment());
|
instr->ReplayEnvironment(current_block_->last_environment());
|
||||||
|
|
||||||
// If there is an instruction pending deoptimization environment create a
|
|
||||||
// lazy bailout instruction to capture the environment.
|
|
||||||
if (pending_deoptimization_ast_id_ == instr->ast_id()) {
|
|
||||||
LInstruction* result = new(zone()) LLazyBailout;
|
|
||||||
result = AssignEnvironment(result);
|
|
||||||
// Store the lazy deopt environment with the instruction if needed. Right
|
|
||||||
// now it is only used for LInstanceOfKnownGlobal.
|
|
||||||
instruction_pending_deoptimization_environment_->
|
|
||||||
SetDeferredLazyDeoptimizationEnvironment(result->environment());
|
|
||||||
instruction_pending_deoptimization_environment_ = NULL;
|
|
||||||
pending_deoptimization_ast_id_ = BailoutId::None();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2976,9 +2976,7 @@ class LChunkBuilder V8_FINAL : public LChunkBuilderBase {
|
|||||||
status_(UNUSED),
|
status_(UNUSED),
|
||||||
current_instruction_(NULL),
|
current_instruction_(NULL),
|
||||||
current_block_(NULL),
|
current_block_(NULL),
|
||||||
allocator_(allocator),
|
allocator_(allocator) { }
|
||||||
instruction_pending_deoptimization_environment_(NULL),
|
|
||||||
pending_deoptimization_ast_id_(BailoutId::None()) { }
|
|
||||||
|
|
||||||
// Build the sequence for the graph.
|
// Build the sequence for the graph.
|
||||||
LPlatformChunk* Build();
|
LPlatformChunk* Build();
|
||||||
@ -3115,8 +3113,6 @@ class LChunkBuilder V8_FINAL : public LChunkBuilderBase {
|
|||||||
HInstruction* current_instruction_;
|
HInstruction* current_instruction_;
|
||||||
HBasicBlock* current_block_;
|
HBasicBlock* current_block_;
|
||||||
LAllocator* allocator_;
|
LAllocator* allocator_;
|
||||||
LInstruction* instruction_pending_deoptimization_environment_;
|
|
||||||
BailoutId pending_deoptimization_ast_id_;
|
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(LChunkBuilder);
|
DISALLOW_COPY_AND_ASSIGN(LChunkBuilder);
|
||||||
};
|
};
|
||||||
|
@ -771,6 +771,13 @@ void LCodeGen::GenerateOsrPrologue() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LCodeGen::GenerateBodyInstructionPre(LInstruction* instr) {
|
||||||
|
if (!instr->IsLazyBailout() && !instr->IsGap()) {
|
||||||
|
safepoints_.BumpLastLazySafepointIndex();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool LCodeGen::GenerateDeferredCode() {
|
bool LCodeGen::GenerateDeferredCode() {
|
||||||
ASSERT(is_generating());
|
ASSERT(is_generating());
|
||||||
if (deferred_.length() > 0) {
|
if (deferred_.length() > 0) {
|
||||||
|
@ -276,6 +276,7 @@ class LCodeGen: public LCodeGenBase {
|
|||||||
void RestoreCallerDoubles();
|
void RestoreCallerDoubles();
|
||||||
|
|
||||||
// Code generation steps. Returns true if code generation should continue.
|
// Code generation steps. Returns true if code generation should continue.
|
||||||
|
void GenerateBodyInstructionPre(LInstruction* instr) V8_OVERRIDE;
|
||||||
bool GeneratePrologue();
|
bool GeneratePrologue();
|
||||||
bool GenerateDeferredCode();
|
bool GenerateDeferredCode();
|
||||||
bool GenerateDeoptJumpTable();
|
bool GenerateDeoptJumpTable();
|
||||||
|
@ -614,15 +614,6 @@ LInstruction* LChunkBuilder::MarkAsCall(LInstruction* instr,
|
|||||||
instr->MarkAsCall();
|
instr->MarkAsCall();
|
||||||
instr = AssignPointerMap(instr);
|
instr = AssignPointerMap(instr);
|
||||||
|
|
||||||
if (hinstr->HasObservableSideEffects()) {
|
|
||||||
ASSERT(hinstr->next()->IsSimulate());
|
|
||||||
HSimulate* sim = HSimulate::cast(hinstr->next());
|
|
||||||
ASSERT(instruction_pending_deoptimization_environment_ == NULL);
|
|
||||||
ASSERT(pending_deoptimization_ast_id_.IsNone());
|
|
||||||
instruction_pending_deoptimization_environment_ = instr;
|
|
||||||
pending_deoptimization_ast_id_ = sim->ast_id();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If instruction does not have side-effects lazy deoptimization
|
// If instruction does not have side-effects lazy deoptimization
|
||||||
// after the call will try to deoptimize to the point before the call.
|
// after the call will try to deoptimize to the point before the call.
|
||||||
// Thus we still need to attach environment to this call even if
|
// Thus we still need to attach environment to this call even if
|
||||||
@ -905,6 +896,26 @@ void LChunkBuilder::VisitInstruction(HInstruction* current) {
|
|||||||
instr = AssignEnvironment(instr);
|
instr = AssignEnvironment(instr);
|
||||||
}
|
}
|
||||||
chunk_->AddInstruction(instr, current_block_);
|
chunk_->AddInstruction(instr, current_block_);
|
||||||
|
|
||||||
|
if (instr->IsCall()) {
|
||||||
|
HValue* hydrogen_value_for_lazy_bailout = current;
|
||||||
|
LInstruction* instruction_needing_environment = NULL;
|
||||||
|
if (current->HasObservableSideEffects()) {
|
||||||
|
HSimulate* sim = HSimulate::cast(current->next());
|
||||||
|
instruction_needing_environment = instr;
|
||||||
|
sim->ReplayEnvironment(current_block_->last_environment());
|
||||||
|
hydrogen_value_for_lazy_bailout = sim;
|
||||||
|
}
|
||||||
|
LInstruction* bailout = AssignEnvironment(new(zone()) LLazyBailout());
|
||||||
|
bailout->set_hydrogen_value(hydrogen_value_for_lazy_bailout);
|
||||||
|
chunk_->AddInstruction(bailout, current_block_);
|
||||||
|
if (instruction_needing_environment != NULL) {
|
||||||
|
// Store the lazy deopt environment with the instruction if needed.
|
||||||
|
// Right now it is only used for LInstanceOfKnownGlobal.
|
||||||
|
instruction_needing_environment->
|
||||||
|
SetDeferredLazyDeoptimizationEnvironment(bailout->environment());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
current_instruction_ = old_current;
|
current_instruction_ = old_current;
|
||||||
}
|
}
|
||||||
@ -2445,21 +2456,6 @@ LInstruction* LChunkBuilder::DoIsConstructCallAndBranch(
|
|||||||
|
|
||||||
LInstruction* LChunkBuilder::DoSimulate(HSimulate* instr) {
|
LInstruction* LChunkBuilder::DoSimulate(HSimulate* instr) {
|
||||||
instr->ReplayEnvironment(current_block_->last_environment());
|
instr->ReplayEnvironment(current_block_->last_environment());
|
||||||
|
|
||||||
// If there is an instruction pending deoptimization environment create a
|
|
||||||
// lazy bailout instruction to capture the environment.
|
|
||||||
if (pending_deoptimization_ast_id_ == instr->ast_id()) {
|
|
||||||
LInstruction* result = new(zone()) LLazyBailout;
|
|
||||||
result = AssignEnvironment(result);
|
|
||||||
// Store the lazy deopt environment with the instruction if needed. Right
|
|
||||||
// now it is only used for LInstanceOfKnownGlobal.
|
|
||||||
instruction_pending_deoptimization_environment_->
|
|
||||||
SetDeferredLazyDeoptimizationEnvironment(result->environment());
|
|
||||||
instruction_pending_deoptimization_environment_ = NULL;
|
|
||||||
pending_deoptimization_ast_id_ = BailoutId::None();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2720,9 +2720,7 @@ class LChunkBuilder V8_FINAL : public LChunkBuilderBase {
|
|||||||
current_instruction_(NULL),
|
current_instruction_(NULL),
|
||||||
current_block_(NULL),
|
current_block_(NULL),
|
||||||
next_block_(NULL),
|
next_block_(NULL),
|
||||||
allocator_(allocator),
|
allocator_(allocator) { }
|
||||||
instruction_pending_deoptimization_environment_(NULL),
|
|
||||||
pending_deoptimization_ast_id_(BailoutId::None()) { }
|
|
||||||
|
|
||||||
// Build the sequence for the graph.
|
// Build the sequence for the graph.
|
||||||
LPlatformChunk* Build();
|
LPlatformChunk* Build();
|
||||||
@ -2866,8 +2864,6 @@ class LChunkBuilder V8_FINAL : public LChunkBuilderBase {
|
|||||||
HBasicBlock* current_block_;
|
HBasicBlock* current_block_;
|
||||||
HBasicBlock* next_block_;
|
HBasicBlock* next_block_;
|
||||||
LAllocator* allocator_;
|
LAllocator* allocator_;
|
||||||
LInstruction* instruction_pending_deoptimization_environment_;
|
|
||||||
BailoutId pending_deoptimization_ast_id_;
|
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(LChunkBuilder);
|
DISALLOW_COPY_AND_ASSIGN(LChunkBuilder);
|
||||||
};
|
};
|
||||||
|
@ -269,6 +269,13 @@ void LCodeGen::GenerateOsrPrologue() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LCodeGen::GenerateBodyInstructionPre(LInstruction* instr) {
|
||||||
|
if (!instr->IsLazyBailout() && !instr->IsGap()) {
|
||||||
|
safepoints_.BumpLastLazySafepointIndex();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool LCodeGen::GenerateDeferredCode() {
|
bool LCodeGen::GenerateDeferredCode() {
|
||||||
ASSERT(is_generating());
|
ASSERT(is_generating());
|
||||||
if (deferred_.length() > 0) {
|
if (deferred_.length() > 0) {
|
||||||
@ -2066,7 +2073,6 @@ void LCodeGen::DoArithmeticT(LArithmeticT* instr) {
|
|||||||
// is in the correct position.
|
// is in the correct position.
|
||||||
Assembler::BlockConstPoolScope block_const_pool(masm());
|
Assembler::BlockConstPoolScope block_const_pool(masm());
|
||||||
CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
|
CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
|
||||||
__ nop(); // Signals no inlined code.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -191,6 +191,7 @@ class LCodeGen: public LCodeGenBase {
|
|||||||
|
|
||||||
// Code generation passes. Returns true if code generation should
|
// Code generation passes. Returns true if code generation should
|
||||||
// continue.
|
// continue.
|
||||||
|
void GenerateBodyInstructionPre(LInstruction* instr) V8_OVERRIDE;
|
||||||
bool GeneratePrologue();
|
bool GeneratePrologue();
|
||||||
bool GenerateDeferredCode();
|
bool GenerateDeferredCode();
|
||||||
bool GenerateDeoptJumpTable();
|
bool GenerateDeoptJumpTable();
|
||||||
|
@ -392,9 +392,33 @@ void Deoptimizer::DeoptimizeMarkedCodeForContext(Context* context) {
|
|||||||
element = next;
|
element = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
// Make sure all activations of optimized code can deopt at their current PC.
|
||||||
|
for (StackFrameIterator it(isolate, isolate->thread_local_top());
|
||||||
|
!it.done(); it.Advance()) {
|
||||||
|
StackFrame::Type type = it.frame()->type();
|
||||||
|
if (type == StackFrame::OPTIMIZED) {
|
||||||
|
Code* code = it.frame()->LookupCode();
|
||||||
|
if (FLAG_trace_deopt) {
|
||||||
|
JSFunction* function =
|
||||||
|
static_cast<OptimizedFrame*>(it.frame())->function();
|
||||||
|
CodeTracer::Scope scope(isolate->GetCodeTracer());
|
||||||
|
PrintF(scope.file(), "[deoptimizer patches for lazy deopt: ");
|
||||||
|
function->PrintName(scope.file());
|
||||||
|
PrintF(scope.file(),
|
||||||
|
" / %" V8PRIxPTR "]\n", reinterpret_cast<intptr_t>(function));
|
||||||
|
}
|
||||||
|
SafepointEntry safepoint = code->GetSafepointEntry(it.frame()->pc());
|
||||||
|
int deopt_index = safepoint.deoptimization_index();
|
||||||
|
CHECK(deopt_index != Safepoint::kNoDeoptimizationIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// TODO(titzer): we need a handle scope only because of the macro assembler,
|
// TODO(titzer): we need a handle scope only because of the macro assembler,
|
||||||
// which is only used in EnsureCodeForDeoptimizationEntry.
|
// which is only used in EnsureCodeForDeoptimizationEntry.
|
||||||
HandleScope scope(isolate);
|
HandleScope scope(isolate);
|
||||||
|
|
||||||
// Now patch all the codes for deoptimization.
|
// Now patch all the codes for deoptimization.
|
||||||
for (int i = 0; i < codes.length(); i++) {
|
for (int i = 0; i < codes.length(); i++) {
|
||||||
// It is finally time to die, code object.
|
// It is finally time to die, code object.
|
||||||
|
@ -614,6 +614,9 @@ void Heap::GarbageCollectionEpilogue() {
|
|||||||
if (FLAG_code_stats) ReportCodeStatistics("After GC");
|
if (FLAG_code_stats) ReportCodeStatistics("After GC");
|
||||||
#endif
|
#endif
|
||||||
if (FLAG_deopt_every_n_garbage_collections > 0) {
|
if (FLAG_deopt_every_n_garbage_collections > 0) {
|
||||||
|
// TODO(jkummerow/ulan/jarin): This is not safe! We can't assume that
|
||||||
|
// the topmost optimized frame can be deoptimized safely, because it
|
||||||
|
// might not have a lazy bailout point right after its current PC.
|
||||||
if (++gcs_since_last_deopt_ == FLAG_deopt_every_n_garbage_collections) {
|
if (++gcs_since_last_deopt_ == FLAG_deopt_every_n_garbage_collections) {
|
||||||
Deoptimizer::DeoptimizeAll(isolate());
|
Deoptimizer::DeoptimizeAll(isolate());
|
||||||
gcs_since_last_deopt_ = 0;
|
gcs_since_last_deopt_ = 0;
|
||||||
|
@ -2470,6 +2470,7 @@ void HSimulate::PrintDataTo(StringStream* stream) {
|
|||||||
|
|
||||||
|
|
||||||
void HSimulate::ReplayEnvironment(HEnvironment* env) {
|
void HSimulate::ReplayEnvironment(HEnvironment* env) {
|
||||||
|
if (done_with_replay_) return;
|
||||||
ASSERT(env != NULL);
|
ASSERT(env != NULL);
|
||||||
env->set_ast_id(ast_id());
|
env->set_ast_id(ast_id());
|
||||||
env->Drop(pop_count());
|
env->Drop(pop_count());
|
||||||
@ -2481,6 +2482,7 @@ void HSimulate::ReplayEnvironment(HEnvironment* env) {
|
|||||||
env->Push(value);
|
env->Push(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
done_with_replay_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1877,7 +1877,8 @@ class HSimulate V8_FINAL : public HInstruction {
|
|||||||
values_(2, zone),
|
values_(2, zone),
|
||||||
assigned_indexes_(2, zone),
|
assigned_indexes_(2, zone),
|
||||||
zone_(zone),
|
zone_(zone),
|
||||||
removable_(removable) {}
|
removable_(removable),
|
||||||
|
done_with_replay_(false) {}
|
||||||
~HSimulate() {}
|
~HSimulate() {}
|
||||||
|
|
||||||
virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE;
|
virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE;
|
||||||
@ -1960,7 +1961,8 @@ class HSimulate V8_FINAL : public HInstruction {
|
|||||||
ZoneList<HValue*> values_;
|
ZoneList<HValue*> values_;
|
||||||
ZoneList<int> assigned_indexes_;
|
ZoneList<int> assigned_indexes_;
|
||||||
Zone* zone_;
|
Zone* zone_;
|
||||||
RemovableSimulate removable_;
|
RemovableSimulate removable_ : 2;
|
||||||
|
bool done_with_replay_ : 1;
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
Handle<JSFunction> closure_;
|
Handle<JSFunction> closure_;
|
||||||
|
@ -390,6 +390,9 @@ void LCodeGen::GenerateOsrPrologue() {
|
|||||||
|
|
||||||
|
|
||||||
void LCodeGen::GenerateBodyInstructionPre(LInstruction* instr) {
|
void LCodeGen::GenerateBodyInstructionPre(LInstruction* instr) {
|
||||||
|
if (!instr->IsLazyBailout() && !instr->IsGap()) {
|
||||||
|
safepoints_.BumpLastLazySafepointIndex();
|
||||||
|
}
|
||||||
if (!CpuFeatures::IsSupported(SSE2)) FlushX87StackIfNecessary(instr);
|
if (!CpuFeatures::IsSupported(SSE2)) FlushX87StackIfNecessary(instr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2285,7 +2288,6 @@ void LCodeGen::DoArithmeticT(LArithmeticT* instr) {
|
|||||||
|
|
||||||
BinaryOpICStub stub(instr->op(), NO_OVERWRITE);
|
BinaryOpICStub stub(instr->op(), NO_OVERWRITE);
|
||||||
CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
|
CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
|
||||||
__ nop(); // Signals no inlined code.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -679,15 +679,6 @@ LInstruction* LChunkBuilder::MarkAsCall(LInstruction* instr,
|
|||||||
instr->MarkAsCall();
|
instr->MarkAsCall();
|
||||||
instr = AssignPointerMap(instr);
|
instr = AssignPointerMap(instr);
|
||||||
|
|
||||||
if (hinstr->HasObservableSideEffects()) {
|
|
||||||
ASSERT(hinstr->next()->IsSimulate());
|
|
||||||
HSimulate* sim = HSimulate::cast(hinstr->next());
|
|
||||||
ASSERT(instruction_pending_deoptimization_environment_ == NULL);
|
|
||||||
ASSERT(pending_deoptimization_ast_id_.IsNone());
|
|
||||||
instruction_pending_deoptimization_environment_ = instr;
|
|
||||||
pending_deoptimization_ast_id_ = sim->ast_id();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If instruction does not have side-effects lazy deoptimization
|
// If instruction does not have side-effects lazy deoptimization
|
||||||
// after the call will try to deoptimize to the point before the call.
|
// after the call will try to deoptimize to the point before the call.
|
||||||
// Thus we still need to attach environment to this call even if
|
// Thus we still need to attach environment to this call even if
|
||||||
@ -980,6 +971,26 @@ void LChunkBuilder::VisitInstruction(HInstruction* current) {
|
|||||||
chunk_->AddInstruction(clobber, current_block_);
|
chunk_->AddInstruction(clobber, current_block_);
|
||||||
}
|
}
|
||||||
chunk_->AddInstruction(instr, current_block_);
|
chunk_->AddInstruction(instr, current_block_);
|
||||||
|
|
||||||
|
if (instr->IsCall()) {
|
||||||
|
HValue* hydrogen_value_for_lazy_bailout = current;
|
||||||
|
LInstruction* instruction_needing_environment = NULL;
|
||||||
|
if (current->HasObservableSideEffects()) {
|
||||||
|
HSimulate* sim = HSimulate::cast(current->next());
|
||||||
|
instruction_needing_environment = instr;
|
||||||
|
sim->ReplayEnvironment(current_block_->last_environment());
|
||||||
|
hydrogen_value_for_lazy_bailout = sim;
|
||||||
|
}
|
||||||
|
LInstruction* bailout = AssignEnvironment(new(zone()) LLazyBailout());
|
||||||
|
bailout->set_hydrogen_value(hydrogen_value_for_lazy_bailout);
|
||||||
|
chunk_->AddInstruction(bailout, current_block_);
|
||||||
|
if (instruction_needing_environment != NULL) {
|
||||||
|
// Store the lazy deopt environment with the instruction if needed.
|
||||||
|
// Right now it is only used for LInstanceOfKnownGlobal.
|
||||||
|
instruction_needing_environment->
|
||||||
|
SetDeferredLazyDeoptimizationEnvironment(bailout->environment());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
current_instruction_ = old_current;
|
current_instruction_ = old_current;
|
||||||
}
|
}
|
||||||
@ -2585,22 +2596,6 @@ LInstruction* LChunkBuilder::DoIsConstructCallAndBranch(
|
|||||||
|
|
||||||
LInstruction* LChunkBuilder::DoSimulate(HSimulate* instr) {
|
LInstruction* LChunkBuilder::DoSimulate(HSimulate* instr) {
|
||||||
instr->ReplayEnvironment(current_block_->last_environment());
|
instr->ReplayEnvironment(current_block_->last_environment());
|
||||||
|
|
||||||
// If there is an instruction pending deoptimization environment create a
|
|
||||||
// lazy bailout instruction to capture the environment.
|
|
||||||
if (!pending_deoptimization_ast_id_.IsNone()) {
|
|
||||||
ASSERT(pending_deoptimization_ast_id_ == instr->ast_id());
|
|
||||||
LLazyBailout* lazy_bailout = new(zone()) LLazyBailout;
|
|
||||||
LInstruction* result = AssignEnvironment(lazy_bailout);
|
|
||||||
// Store the lazy deopt environment with the instruction if needed. Right
|
|
||||||
// now it is only used for LInstanceOfKnownGlobal.
|
|
||||||
instruction_pending_deoptimization_environment_->
|
|
||||||
SetDeferredLazyDeoptimizationEnvironment(result->environment());
|
|
||||||
instruction_pending_deoptimization_environment_ = NULL;
|
|
||||||
pending_deoptimization_ast_id_ = BailoutId::None();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2752,9 +2752,7 @@ class LChunkBuilder V8_FINAL : public LChunkBuilderBase {
|
|||||||
current_instruction_(NULL),
|
current_instruction_(NULL),
|
||||||
current_block_(NULL),
|
current_block_(NULL),
|
||||||
next_block_(NULL),
|
next_block_(NULL),
|
||||||
allocator_(allocator),
|
allocator_(allocator) { }
|
||||||
instruction_pending_deoptimization_environment_(NULL),
|
|
||||||
pending_deoptimization_ast_id_(BailoutId::None()) { }
|
|
||||||
|
|
||||||
// Build the sequence for the graph.
|
// Build the sequence for the graph.
|
||||||
LPlatformChunk* Build();
|
LPlatformChunk* Build();
|
||||||
@ -2906,8 +2904,6 @@ class LChunkBuilder V8_FINAL : public LChunkBuilderBase {
|
|||||||
HBasicBlock* current_block_;
|
HBasicBlock* current_block_;
|
||||||
HBasicBlock* next_block_;
|
HBasicBlock* next_block_;
|
||||||
LAllocator* allocator_;
|
LAllocator* allocator_;
|
||||||
LInstruction* instruction_pending_deoptimization_environment_;
|
|
||||||
BailoutId pending_deoptimization_ast_id_;
|
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(LChunkBuilder);
|
DISALLOW_COPY_AND_ASSIGN(LChunkBuilder);
|
||||||
};
|
};
|
||||||
|
@ -259,6 +259,13 @@ void LCodeGen::GenerateOsrPrologue() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LCodeGen::GenerateBodyInstructionPre(LInstruction* instr) {
|
||||||
|
if (!instr->IsLazyBailout() && !instr->IsGap()) {
|
||||||
|
safepoints_.BumpLastLazySafepointIndex();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool LCodeGen::GenerateDeferredCode() {
|
bool LCodeGen::GenerateDeferredCode() {
|
||||||
ASSERT(is_generating());
|
ASSERT(is_generating());
|
||||||
if (deferred_.length() > 0) {
|
if (deferred_.length() > 0) {
|
||||||
|
@ -191,6 +191,7 @@ class LCodeGen: public LCodeGenBase {
|
|||||||
|
|
||||||
// Code generation passes. Returns true if code generation should
|
// Code generation passes. Returns true if code generation should
|
||||||
// continue.
|
// continue.
|
||||||
|
void GenerateBodyInstructionPre(LInstruction* instr) V8_OVERRIDE;
|
||||||
bool GeneratePrologue();
|
bool GeneratePrologue();
|
||||||
bool GenerateDeferredCode();
|
bool GenerateDeferredCode();
|
||||||
bool GenerateDeoptJumpTable();
|
bool GenerateDeoptJumpTable();
|
||||||
|
@ -619,15 +619,6 @@ LInstruction* LChunkBuilder::MarkAsCall(LInstruction* instr,
|
|||||||
instr->MarkAsCall();
|
instr->MarkAsCall();
|
||||||
instr = AssignPointerMap(instr);
|
instr = AssignPointerMap(instr);
|
||||||
|
|
||||||
if (hinstr->HasObservableSideEffects()) {
|
|
||||||
ASSERT(hinstr->next()->IsSimulate());
|
|
||||||
HSimulate* sim = HSimulate::cast(hinstr->next());
|
|
||||||
ASSERT(instruction_pending_deoptimization_environment_ == NULL);
|
|
||||||
ASSERT(pending_deoptimization_ast_id_.IsNone());
|
|
||||||
instruction_pending_deoptimization_environment_ = instr;
|
|
||||||
pending_deoptimization_ast_id_ = sim->ast_id();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If instruction does not have side-effects lazy deoptimization
|
// If instruction does not have side-effects lazy deoptimization
|
||||||
// after the call will try to deoptimize to the point before the call.
|
// after the call will try to deoptimize to the point before the call.
|
||||||
// Thus we still need to attach environment to this call even if
|
// Thus we still need to attach environment to this call even if
|
||||||
@ -913,6 +904,26 @@ void LChunkBuilder::VisitInstruction(HInstruction* current) {
|
|||||||
instr = AssignEnvironment(instr);
|
instr = AssignEnvironment(instr);
|
||||||
}
|
}
|
||||||
chunk_->AddInstruction(instr, current_block_);
|
chunk_->AddInstruction(instr, current_block_);
|
||||||
|
|
||||||
|
if (instr->IsCall()) {
|
||||||
|
HValue* hydrogen_value_for_lazy_bailout = current;
|
||||||
|
LInstruction* instruction_needing_environment = NULL;
|
||||||
|
if (current->HasObservableSideEffects()) {
|
||||||
|
HSimulate* sim = HSimulate::cast(current->next());
|
||||||
|
instruction_needing_environment = instr;
|
||||||
|
sim->ReplayEnvironment(current_block_->last_environment());
|
||||||
|
hydrogen_value_for_lazy_bailout = sim;
|
||||||
|
}
|
||||||
|
LInstruction* bailout = AssignEnvironment(new(zone()) LLazyBailout());
|
||||||
|
bailout->set_hydrogen_value(hydrogen_value_for_lazy_bailout);
|
||||||
|
chunk_->AddInstruction(bailout, current_block_);
|
||||||
|
if (instruction_needing_environment != NULL) {
|
||||||
|
// Store the lazy deopt environment with the instruction if needed.
|
||||||
|
// Right now it is only used for LInstanceOfKnownGlobal.
|
||||||
|
instruction_needing_environment->
|
||||||
|
SetDeferredLazyDeoptimizationEnvironment(bailout->environment());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
current_instruction_ = old_current;
|
current_instruction_ = old_current;
|
||||||
}
|
}
|
||||||
@ -2347,21 +2358,6 @@ LInstruction* LChunkBuilder::DoIsConstructCallAndBranch(
|
|||||||
|
|
||||||
LInstruction* LChunkBuilder::DoSimulate(HSimulate* instr) {
|
LInstruction* LChunkBuilder::DoSimulate(HSimulate* instr) {
|
||||||
instr->ReplayEnvironment(current_block_->last_environment());
|
instr->ReplayEnvironment(current_block_->last_environment());
|
||||||
|
|
||||||
// If there is an instruction pending deoptimization environment create a
|
|
||||||
// lazy bailout instruction to capture the environment.
|
|
||||||
if (pending_deoptimization_ast_id_ == instr->ast_id()) {
|
|
||||||
LInstruction* result = new(zone()) LLazyBailout;
|
|
||||||
result = AssignEnvironment(result);
|
|
||||||
// Store the lazy deopt environment with the instruction if needed. Right
|
|
||||||
// now it is only used for LInstanceOfKnownGlobal.
|
|
||||||
instruction_pending_deoptimization_environment_->
|
|
||||||
SetDeferredLazyDeoptimizationEnvironment(result->environment());
|
|
||||||
instruction_pending_deoptimization_environment_ = NULL;
|
|
||||||
pending_deoptimization_ast_id_ = BailoutId::None();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2642,9 +2642,7 @@ class LChunkBuilder V8_FINAL : public LChunkBuilderBase {
|
|||||||
current_instruction_(NULL),
|
current_instruction_(NULL),
|
||||||
current_block_(NULL),
|
current_block_(NULL),
|
||||||
next_block_(NULL),
|
next_block_(NULL),
|
||||||
allocator_(allocator),
|
allocator_(allocator) { }
|
||||||
instruction_pending_deoptimization_environment_(NULL),
|
|
||||||
pending_deoptimization_ast_id_(BailoutId::None()) { }
|
|
||||||
|
|
||||||
// Build the sequence for the graph.
|
// Build the sequence for the graph.
|
||||||
LPlatformChunk* Build();
|
LPlatformChunk* Build();
|
||||||
@ -2784,8 +2782,6 @@ class LChunkBuilder V8_FINAL : public LChunkBuilderBase {
|
|||||||
HBasicBlock* current_block_;
|
HBasicBlock* current_block_;
|
||||||
HBasicBlock* next_block_;
|
HBasicBlock* next_block_;
|
||||||
LAllocator* allocator_;
|
LAllocator* allocator_;
|
||||||
LInstruction* instruction_pending_deoptimization_environment_;
|
|
||||||
BailoutId pending_deoptimization_ast_id_;
|
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(LChunkBuilder);
|
DISALLOW_COPY_AND_ASSIGN(LChunkBuilder);
|
||||||
};
|
};
|
||||||
|
@ -219,6 +219,9 @@ class SafepointTableBuilder BASE_EMBEDDED {
|
|||||||
// Record deoptimization index for lazy deoptimization for the last
|
// Record deoptimization index for lazy deoptimization for the last
|
||||||
// outstanding safepoints.
|
// outstanding safepoints.
|
||||||
void RecordLazyDeoptimizationIndex(int index);
|
void RecordLazyDeoptimizationIndex(int index);
|
||||||
|
void BumpLastLazySafepointIndex() {
|
||||||
|
last_lazy_safepoint_ = deopt_index_list_.length();
|
||||||
|
}
|
||||||
|
|
||||||
// Emit the safepoint table after the body. The number of bits per
|
// Emit the safepoint table after the body. The number of bits per
|
||||||
// entry must be enough to hold all the pointer indexes.
|
// entry must be enough to hold all the pointer indexes.
|
||||||
|
@ -273,6 +273,13 @@ void LCodeGen::GenerateOsrPrologue() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LCodeGen::GenerateBodyInstructionPre(LInstruction* instr) {
|
||||||
|
if (!instr->IsLazyBailout() && !instr->IsGap()) {
|
||||||
|
safepoints_.BumpLastLazySafepointIndex();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool LCodeGen::GenerateJumpTable() {
|
bool LCodeGen::GenerateJumpTable() {
|
||||||
Label needs_frame;
|
Label needs_frame;
|
||||||
if (jump_table_.length() > 0) {
|
if (jump_table_.length() > 0) {
|
||||||
@ -1900,7 +1907,6 @@ void LCodeGen::DoArithmeticT(LArithmeticT* instr) {
|
|||||||
|
|
||||||
BinaryOpICStub stub(instr->op(), NO_OVERWRITE);
|
BinaryOpICStub stub(instr->op(), NO_OVERWRITE);
|
||||||
CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
|
CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
|
||||||
__ nop(); // Signals no inlined code.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -157,6 +157,7 @@ class LCodeGen: public LCodeGenBase {
|
|||||||
|
|
||||||
// Code generation passes. Returns true if code generation should
|
// Code generation passes. Returns true if code generation should
|
||||||
// continue.
|
// continue.
|
||||||
|
void GenerateBodyInstructionPre(LInstruction* instr) V8_OVERRIDE;
|
||||||
bool GeneratePrologue();
|
bool GeneratePrologue();
|
||||||
bool GenerateDeferredCode();
|
bool GenerateDeferredCode();
|
||||||
bool GenerateJumpTable();
|
bool GenerateJumpTable();
|
||||||
|
@ -630,15 +630,6 @@ LInstruction* LChunkBuilder::MarkAsCall(LInstruction* instr,
|
|||||||
instr->MarkAsCall();
|
instr->MarkAsCall();
|
||||||
instr = AssignPointerMap(instr);
|
instr = AssignPointerMap(instr);
|
||||||
|
|
||||||
if (hinstr->HasObservableSideEffects()) {
|
|
||||||
ASSERT(hinstr->next()->IsSimulate());
|
|
||||||
HSimulate* sim = HSimulate::cast(hinstr->next());
|
|
||||||
ASSERT(instruction_pending_deoptimization_environment_ == NULL);
|
|
||||||
ASSERT(pending_deoptimization_ast_id_.IsNone());
|
|
||||||
instruction_pending_deoptimization_environment_ = instr;
|
|
||||||
pending_deoptimization_ast_id_ = sim->ast_id();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If instruction does not have side-effects lazy deoptimization
|
// If instruction does not have side-effects lazy deoptimization
|
||||||
// after the call will try to deoptimize to the point before the call.
|
// after the call will try to deoptimize to the point before the call.
|
||||||
// Thus we still need to attach environment to this call even if
|
// Thus we still need to attach environment to this call even if
|
||||||
@ -916,6 +907,26 @@ void LChunkBuilder::VisitInstruction(HInstruction* current) {
|
|||||||
instr = AssignEnvironment(instr);
|
instr = AssignEnvironment(instr);
|
||||||
}
|
}
|
||||||
chunk_->AddInstruction(instr, current_block_);
|
chunk_->AddInstruction(instr, current_block_);
|
||||||
|
|
||||||
|
if (instr->IsCall()) {
|
||||||
|
HValue* hydrogen_value_for_lazy_bailout = current;
|
||||||
|
LInstruction* instruction_needing_environment = NULL;
|
||||||
|
if (current->HasObservableSideEffects()) {
|
||||||
|
HSimulate* sim = HSimulate::cast(current->next());
|
||||||
|
instruction_needing_environment = instr;
|
||||||
|
sim->ReplayEnvironment(current_block_->last_environment());
|
||||||
|
hydrogen_value_for_lazy_bailout = sim;
|
||||||
|
}
|
||||||
|
LInstruction* bailout = AssignEnvironment(new(zone()) LLazyBailout());
|
||||||
|
bailout->set_hydrogen_value(hydrogen_value_for_lazy_bailout);
|
||||||
|
chunk_->AddInstruction(bailout, current_block_);
|
||||||
|
if (instruction_needing_environment != NULL) {
|
||||||
|
// Store the lazy deopt environment with the instruction if needed.
|
||||||
|
// Right now it is only used for LInstanceOfKnownGlobal.
|
||||||
|
instruction_needing_environment->
|
||||||
|
SetDeferredLazyDeoptimizationEnvironment(bailout->environment());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
current_instruction_ = old_current;
|
current_instruction_ = old_current;
|
||||||
}
|
}
|
||||||
@ -2435,21 +2446,6 @@ LInstruction* LChunkBuilder::DoIsConstructCallAndBranch(
|
|||||||
|
|
||||||
LInstruction* LChunkBuilder::DoSimulate(HSimulate* instr) {
|
LInstruction* LChunkBuilder::DoSimulate(HSimulate* instr) {
|
||||||
instr->ReplayEnvironment(current_block_->last_environment());
|
instr->ReplayEnvironment(current_block_->last_environment());
|
||||||
|
|
||||||
// If there is an instruction pending deoptimization environment create a
|
|
||||||
// lazy bailout instruction to capture the environment.
|
|
||||||
if (pending_deoptimization_ast_id_ == instr->ast_id()) {
|
|
||||||
LLazyBailout* lazy_bailout = new(zone()) LLazyBailout;
|
|
||||||
LInstruction* result = AssignEnvironment(lazy_bailout);
|
|
||||||
// Store the lazy deopt environment with the instruction if needed. Right
|
|
||||||
// now it is only used for LInstanceOfKnownGlobal.
|
|
||||||
instruction_pending_deoptimization_environment_->
|
|
||||||
SetDeferredLazyDeoptimizationEnvironment(result->environment());
|
|
||||||
instruction_pending_deoptimization_environment_ = NULL;
|
|
||||||
pending_deoptimization_ast_id_ = BailoutId::None();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2666,9 +2666,7 @@ class LChunkBuilder V8_FINAL : public LChunkBuilderBase {
|
|||||||
current_instruction_(NULL),
|
current_instruction_(NULL),
|
||||||
current_block_(NULL),
|
current_block_(NULL),
|
||||||
next_block_(NULL),
|
next_block_(NULL),
|
||||||
allocator_(allocator),
|
allocator_(allocator) { }
|
||||||
instruction_pending_deoptimization_environment_(NULL),
|
|
||||||
pending_deoptimization_ast_id_(BailoutId::None()) { }
|
|
||||||
|
|
||||||
// Build the sequence for the graph.
|
// Build the sequence for the graph.
|
||||||
LPlatformChunk* Build();
|
LPlatformChunk* Build();
|
||||||
@ -2813,8 +2811,6 @@ class LChunkBuilder V8_FINAL : public LChunkBuilderBase {
|
|||||||
HBasicBlock* current_block_;
|
HBasicBlock* current_block_;
|
||||||
HBasicBlock* next_block_;
|
HBasicBlock* next_block_;
|
||||||
LAllocator* allocator_;
|
LAllocator* allocator_;
|
||||||
LInstruction* instruction_pending_deoptimization_environment_;
|
|
||||||
BailoutId pending_deoptimization_ast_id_;
|
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(LChunkBuilder);
|
DISALLOW_COPY_AND_ASSIGN(LChunkBuilder);
|
||||||
};
|
};
|
||||||
|
33
test/mjsunit/regress/regress-crbug-350434.js
Normal file
33
test/mjsunit/regress/regress-crbug-350434.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// Copyright 2014 the V8 project authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
// Flags: --gc-global --noincremental-marking --allow-natives-syntax
|
||||||
|
|
||||||
|
function Ctor() {
|
||||||
|
this.foo = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var o = new Ctor();
|
||||||
|
var p = new Ctor();
|
||||||
|
|
||||||
|
|
||||||
|
function crash(o, timeout) {
|
||||||
|
var s = "4000111222"; // Outside Smi range.
|
||||||
|
%SetAllocationTimeout(100000, timeout);
|
||||||
|
// This allocates a heap number, causing a GC, triggering lazy deopt.
|
||||||
|
var end = s >>> 0;
|
||||||
|
s = s.substring(0, end);
|
||||||
|
// This creates a map dependency, which gives the GC a reason to trigger
|
||||||
|
// a lazy deopt when that map dies.
|
||||||
|
o.bar = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
crash(o, 100000);
|
||||||
|
crash(o, 100000);
|
||||||
|
crash(p, 100000);
|
||||||
|
%OptimizeFunctionOnNextCall(crash);
|
||||||
|
crash(o, 100000);
|
||||||
|
o = null;
|
||||||
|
p = null;
|
||||||
|
crash({}, 0);
|
Loading…
Reference in New Issue
Block a user