}
chunk_->AddInstruction(instr, current_block_);
- if (instr->IsCall()) {
+ if (instr->IsCall() || instr->IsPrologue()) {
HValue* hydrogen_value_for_lazy_bailout = hydrogen_val;
if (hydrogen_val->HasObservableSideEffects()) {
HSimulate* sim = HSimulate::cast(hydrogen_val->next());
}
+LInstruction* LChunkBuilder::DoPrologue(HPrologue* instr) {
+ return new (zone()) LPrologue();
+}
+
+
LInstruction* LChunkBuilder::DoGoto(HGoto* instr) {
return new(zone()) LGoto(instr->FirstSuccessor());
}
V(OsrEntry) \
V(Parameter) \
V(Power) \
+ V(Prologue) \
V(PushArgument) \
V(RegExpLiteral) \
V(Return) \
};
+class LPrologue final : public LTemplateInstruction<0, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(Prologue, "prologue")
+};
+
+
class LLazyBailout final : public LTemplateInstruction<0, 0, 0> {
public:
LLazyBailout() : gap_instructions_size_(0) { }
if (info()->saves_caller_doubles()) {
SaveCallerDoubles();
}
+ return !is_aborted();
+}
+
+
+void LCodeGen::DoPrologue(LPrologue* instr) {
+ Comment(";;; Prologue begin");
// Possibly allocate a local context.
- int heap_slots = info()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
- if (heap_slots > 0) {
+ if (info()->scope()->num_heap_slots() > 0) {
Comment(";;; Allocate local context");
bool need_write_barrier = true;
// Argument to NewContext is the function, which is in r1.
- DCHECK(!info()->scope()->is_script_scope());
- if (heap_slots <= FastNewContextStub::kMaximumSlots) {
- FastNewContextStub stub(isolate(), heap_slots);
+ int slots = info()->scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
+ Safepoint::DeoptMode deopt_mode = Safepoint::kNoLazyDeopt;
+ if (info()->scope()->is_script_scope()) {
+ __ push(r1);
+ __ Push(info()->scope()->GetScopeInfo(info()->isolate()));
+ __ CallRuntime(Runtime::kNewScriptContext, 2);
+ deopt_mode = Safepoint::kLazyDeopt;
+ } else if (slots <= FastNewContextStub::kMaximumSlots) {
+ FastNewContextStub stub(isolate(), slots);
__ CallStub(&stub);
// Result of FastNewContextStub is always in new space.
need_write_barrier = false;
__ push(r1);
__ CallRuntime(Runtime::kNewFunctionContext, 1);
}
- RecordSafepoint(Safepoint::kNoLazyDeopt);
+ RecordSafepoint(deopt_mode);
+
// Context is returned in both r0 and cp. It replaces the context
// passed to us. It's saved in the stack and kept live in cp.
__ mov(cp, r0);
Comment(";;; End allocate local context");
}
- // Trace the call.
- if (FLAG_trace && info()->IsOptimizing()) {
- // We have not executed any compiled code yet, so cp still holds the
- // incoming context.
- __ CallRuntime(Runtime::kTraceEnter, 0);
- }
- return !is_aborted();
+ Comment(";;; Prologue end");
}
}
chunk_->AddInstruction(instr, current_block_);
- if (instr->IsCall()) {
+ if (instr->IsCall() || instr->IsPrologue()) {
HValue* hydrogen_value_for_lazy_bailout = hydrogen_val;
if (hydrogen_val->HasObservableSideEffects()) {
HSimulate* sim = HSimulate::cast(hydrogen_val->next());
}
+LInstruction* LChunkBuilder::DoPrologue(HPrologue* instr) {
+ return new (zone()) LPrologue();
+}
+
+
LInstruction* LChunkBuilder::DoAbnormalExit(HAbnormalExit* instr) {
// The control instruction marking the end of a block that completed
// abruptly (e.g., threw an exception). There is nothing specific to do.
V(OsrEntry) \
V(Parameter) \
V(Power) \
+ V(Prologue) \
V(PreparePushArguments) \
V(PushArguments) \
V(RegExpLiteral) \
};
+class LPrologue final : public LTemplateInstruction<0, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(Prologue, "prologue")
+};
+
+
class LLazyBailout final : public LTemplateInstruction<0, 0, 0> {
public:
LLazyBailout() : gap_instructions_size_(0) { }
if (info()->saves_caller_doubles()) {
SaveCallerDoubles();
}
+ return !is_aborted();
+}
+
+
+void LCodeGen::DoPrologue(LPrologue* instr) {
+ Comment(";;; Prologue begin");
// Allocate a local context if needed.
- int heap_slots = info()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
- if (heap_slots > 0) {
+ if (info()->num_heap_slots() > 0) {
Comment(";;; Allocate local context");
bool need_write_barrier = true;
// Argument to NewContext is the function, which is in x1.
- DCHECK(!info()->scope()->is_script_scope());
- if (heap_slots <= FastNewContextStub::kMaximumSlots) {
- FastNewContextStub stub(isolate(), heap_slots);
+ int slots = info()->scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
+ Safepoint::DeoptMode deopt_mode = Safepoint::kNoLazyDeopt;
+ if (info()->scope()->is_script_scope()) {
+ __ Mov(x10, Operand(info()->scope()->GetScopeInfo(info()->isolate())));
+ __ Push(x1, x10);
+ __ CallRuntime(Runtime::kNewScriptContext, 2);
+ deopt_mode = Safepoint::kLazyDeopt;
+ } else if (slots <= FastNewContextStub::kMaximumSlots) {
+ FastNewContextStub stub(isolate(), slots);
__ CallStub(&stub);
// Result of FastNewContextStub is always in new space.
need_write_barrier = false;
__ Push(x1);
__ CallRuntime(Runtime::kNewFunctionContext, 1);
}
- RecordSafepoint(Safepoint::kNoLazyDeopt);
+ RecordSafepoint(deopt_mode);
// Context is returned in x0. It replaces the context passed to us. It's
// saved in the stack and kept live in cp.
__ Mov(cp, x0);
Comment(";;; End allocate local context");
}
- // Trace the call.
- if (FLAG_trace && info()->IsOptimizing()) {
- // We have not executed any compiled code yet, so cp still holds the
- // incoming context.
- __ CallRuntime(Runtime::kTraceEnter, 0);
- }
-
- return !is_aborted();
+ Comment(";;; Prologue end");
}
// Check that the script context table is empty except for the 'this' binding.
// We do not need script contexts for native scripts.
- DCHECK_EQ(1, native_context()->script_context_table()->used());
+ if (!FLAG_global_var_shortcuts) {
+ DCHECK_EQ(1, native_context()->script_context_table()->used());
+ }
result_ = native_context();
}
const Operator* op = javascript()->CreateScriptContext();
Node* scope_info = jsgraph()->Constant(scope->GetScopeInfo(isolate()));
Node* local_context = NewNode(op, GetFunctionClosure(), scope_info);
- PrepareFrameState(local_context, BailoutId::FunctionEntry());
+ PrepareFrameState(local_context, BailoutId::Prologue());
return local_context;
}
int used = table->used();
int length = table->length();
CHECK(used >= 0 && length > 0 && used < length);
- if (used + 1 == length) {
+ if (used + kFirstContextSlot == length) {
CHECK(length < Smi::kMaxValue / 2);
Isolate* isolate = table->GetIsolate();
Handle<FixedArray> copy =
result->set_used(used + 1);
DCHECK(script_context->IsScriptContext());
- result->set(used + 1, *script_context);
+ result->set(used + kFirstContextSlot, *script_context);
return result;
}
};
int used() const { return Smi::cast(get(kUsedSlot))->value(); }
-
void set_used(int used) { set(kUsedSlot, Smi::FromInt(used)); }
static Handle<Context> GetContext(Handle<ScriptContextTable> table, int i) {
DCHECK(i < table->used());
- return Handle<Context>::cast(FixedArray::get(table, i + 1));
+ return Handle<Context>::cast(FixedArray::get(table, i + kFirstContextSlot));
}
// Lookup a variable `name` in a ScriptContextTable.
private:
static const int kUsedSlot = 0;
+ static const int kFirstContextSlot = kUsedSlot + 1;
static const int kFirstContextOffset =
- FixedArray::kHeaderSize + (kUsedSlot + 1) * kPointerSize;
+ FixedArray::kHeaderSize + kFirstContextSlot * kPointerSize;
DISALLOW_IMPLICIT_CONSTRUCTORS(ScriptContextTable);
};
}
}
- bool function_in_register = true;
+ bool function_in_register_r1 = true;
// Possibly allocate a local context.
if (info->scope()->num_heap_slots() > 0) {
__ push(r1);
__ CallRuntime(Runtime::kNewFunctionContext, 1);
}
- function_in_register = false;
+ function_in_register_r1 = false;
// Context is returned in r0. It replaces the context passed to us.
// It's saved in the stack and kept live in cp.
__ mov(cp, r0);
}
}
+ PrepareForBailoutForId(BailoutId::Prologue(), NO_REGISTERS);
+ // Function register is trashed in case we bailout here. But since that
+ // could happen only when we allocate a context the value of
+ // |function_in_register_r1| is correct.
+
// Possibly set up a local binding to the this function which is used in
// derived constructors with super calls.
Variable* this_function_var = scope()->this_function_var();
if (this_function_var != nullptr) {
Comment cmnt(masm_, "[ This function");
- if (!function_in_register) {
+ if (!function_in_register_r1) {
__ ldr(r1, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
- // The write barrier clobbers register again, keep is marked as such.
+ // The write barrier clobbers register again, keep it marked as such.
}
SetVar(this_function_var, r1, r0, r2);
}
__ ldr(r1, MemOperand(r2, StandardFrameConstants::kMarkerOffset));
__ cmp(r1, Operand(Smi::FromInt(StackFrame::CONSTRUCT)));
Label non_construct_frame, done;
+ function_in_register_r1 = false;
__ b(ne, &non_construct_frame);
__ ldr(r0,
__ mov(r1, Operand(Smi::FromInt(rest_index)));
__ mov(r0, Operand(Smi::FromInt(language_mode())));
__ Push(r3, r2, r1, r0);
+ function_in_register_r1 = false;
RestParamAccessStub stub(isolate());
__ CallStub(&stub);
if (arguments != NULL) {
// Function uses arguments object.
Comment cmnt(masm_, "[ Allocate arguments object");
- if (!function_in_register) {
+ if (!function_in_register_r1) {
// Load this again, if it's used by the local context below.
__ ldr(r3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
} else {
SetVar(arguments, r0, r1, r2);
}
-
if (FLAG_trace) {
__ CallRuntime(Runtime::kTraceEnter, 0);
}
}
}
+ PrepareForBailoutForId(BailoutId::Prologue(), NO_REGISTERS);
+ // Function register is trashed in case we bailout here. But since that
+ // could happen only when we allocate a context the value of
+ // |function_in_register_x1| is correct.
+
// Possibly set up a local binding to the this function which is used in
// derived constructors with super calls.
Variable* this_function_var = scope()->this_function_var();
Comment cmnt(masm_, "[ This function");
if (!function_in_register_x1) {
__ Ldr(x1, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
- // The write barrier clobbers register again, keep is marked as such.
+ // The write barrier clobbers register again, keep it marked as such.
}
SetVar(this_function_var, x1, x0, x2);
}
__ Bind(&check_frame_marker);
__ Ldr(x1, MemOperand(x2, StandardFrameConstants::kMarkerOffset));
__ Cmp(x1, Smi::FromInt(StackFrame::CONSTRUCT));
+ function_in_register_x1 = false;
Label non_construct_frame, done;
__ Mov(x1, Smi::FromInt(rest_index));
__ Mov(x0, Smi::FromInt(language_mode()));
__ Push(x3, x2, x1, x0);
+ function_in_register_x1 = false;
RestParamAccessStub stub(isolate());
__ CallStub(&stub);
}
}
+ PrepareForBailoutForId(BailoutId::Prologue(), NO_REGISTERS);
+ // Function register is trashed in case we bailout here. But since that
+ // could happen only when we allocate a context the value of
+ // |function_in_register| is correct.
+
// Possibly set up a local binding to the this function which is used in
// derived constructors with super calls.
Variable* this_function_var = scope()->this_function_var();
Comment cmnt(masm_, "[ This function");
if (!function_in_register) {
__ mov(edi, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
- // The write barrier clobbers register again, keep is marked as such.
+ // The write barrier clobbers register again, keep it marked as such.
}
SetVar(this_function_var, edi, ebx, edx);
}
}
}
- bool function_in_register = true;
+ bool function_in_register_a1 = true;
// Possibly allocate a local context.
if (info->scope()->num_heap_slots() > 0) {
__ push(a1);
__ CallRuntime(Runtime::kNewFunctionContext, 1);
}
- function_in_register = false;
+ function_in_register_a1 = false;
// Context is returned in v0. It replaces the context passed to us.
// It's saved in the stack and kept live in cp.
__ mov(cp, v0);
}
}
+ PrepareForBailoutForId(BailoutId::Prologue(), NO_REGISTERS);
+ // Function register is trashed in case we bailout here. But since that
+ // could happen only when we allocate a context the value of
+ // |function_in_register_a1| is correct.
+
// Possibly set up a local binding to the this function which is used in
// derived constructors with super calls.
Variable* this_function_var = scope()->this_function_var();
if (this_function_var != nullptr) {
Comment cmnt(masm_, "[ This function");
- if (!function_in_register) {
+ if (!function_in_register_a1) {
__ lw(a1, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
- // The write barrier clobbers register again, keep is marked as such.
+ // The write barrier clobbers register again, keep it marked as such.
}
SetVar(this_function_var, a1, a2, a3);
}
// Check the marker in the calling frame.
__ bind(&check_frame_marker);
__ lw(a1, MemOperand(a2, StandardFrameConstants::kMarkerOffset));
+ function_in_register_a1 = false;
Label non_construct_frame, done;
__ Branch(&non_construct_frame, ne, a1,
__ li(a1, Operand(Smi::FromInt(rest_index)));
__ li(a0, Operand(Smi::FromInt(language_mode())));
__ Push(a3, a2, a1, a0);
+ function_in_register_a1 = false;
RestParamAccessStub stub(isolate());
__ CallStub(&stub);
if (arguments != NULL) {
// Function uses arguments object.
Comment cmnt(masm_, "[ Allocate arguments object");
- if (!function_in_register) {
+ if (!function_in_register_a1) {
// Load this again, if it's used by the local context below.
__ lw(a3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
} else {
}
}
- bool function_in_register = true;
+ bool function_in_register_a1 = true;
// Possibly allocate a local context.
if (info->scope()->num_heap_slots() > 0) {
__ push(a1);
__ CallRuntime(Runtime::kNewFunctionContext, 1);
}
- function_in_register = false;
+ function_in_register_a1 = false;
// Context is returned in v0. It replaces the context passed to us.
// It's saved in the stack and kept live in cp.
__ mov(cp, v0);
}
}
+ PrepareForBailoutForId(BailoutId::Prologue(), NO_REGISTERS);
+ // Function register is trashed in case we bailout here. But since that
+ // could happen only when we allocate a context the value of
+ // |function_in_register_a1| is correct.
+
// Possibly set up a local binding to the this function which is used in
// derived constructors with super calls.
Variable* this_function_var = scope()->this_function_var();
if (this_function_var != nullptr) {
Comment cmnt(masm_, "[ This function");
- if (!function_in_register) {
+ if (!function_in_register_a1) {
__ ld(a1, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
- // The write barrier clobbers register again, keep is marked as such.
+ // The write barrier clobbers register again, keep it marked as such.
}
SetVar(this_function_var, a1, a2, a3);
}
// Check the marker in the calling frame.
__ bind(&check_frame_marker);
__ ld(a1, MemOperand(a2, StandardFrameConstants::kMarkerOffset));
+ function_in_register_a1 = false;
Label non_construct_frame, done;
__ Branch(&non_construct_frame, ne, a1,
__ li(a1, Operand(Smi::FromInt(rest_index)));
__ li(a0, Operand(Smi::FromInt(language_mode())));
__ Push(a3, a2, a1, a0);
+ function_in_register_a1 = false;
RestParamAccessStub stub(isolate());
__ CallStub(&stub);
if (arguments != NULL) {
// Function uses arguments object.
Comment cmnt(masm_, "[ Allocate arguments object");
- if (!function_in_register) {
+ if (!function_in_register_a1) {
// Load this again, if it's used by the local context below.
__ ld(a3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
} else {
if (FLAG_trace) {
__ CallRuntime(Runtime::kTraceEnter, 0);
}
+
// Visit the declarations and body unless there is an illegal
// redeclaration.
if (scope()->HasIllegalRedeclaration()) {
}
}
+ PrepareForBailoutForId(BailoutId::Prologue(), NO_REGISTERS);
+ // Function register is trashed in case we bailout here. But since that
+ // could happen only when we allocate a context the value of
+ // |function_in_register| is correct.
+
// Possibly set up a local binding to the this function which is used in
// derived constructors with super calls.
Variable* this_function_var = scope()->this_function_var();
Comment cmnt(masm_, "[ This function");
if (!function_in_register) {
__ movp(rdi, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
- // The write barrier clobbers register again, keep is marked as such.
+ // The write barrier clobbers register again, keep it marked as such.
}
SetVar(this_function_var, rdi, rbx, rdx);
}
case HValue::kMul:
case HValue::kOsrEntry:
case HValue::kPower:
+ case HValue::kPrologue:
case HValue::kRor:
case HValue::kSar:
case HValue::kSeqStringSetChar:
V(OsrEntry) \
V(Parameter) \
V(Power) \
+ V(Prologue) \
V(PushArguments) \
V(RegExpLiteral) \
V(Return) \
};
+class HPrologue final : public HTemplateInstruction<0> {
+ public:
+ static HPrologue* New(Zone* zone) { return new (zone) HPrologue(); }
+
+ Representation RequiredInputRepresentation(int index) override {
+ return Representation::None();
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(Prologue)
+};
+
+
class HGoto final : public HTemplateControlInstruction<1, 0> {
public:
explicit HGoto(HBasicBlock* target) {
start_environment_ =
new(zone_) HEnvironment(NULL, info->scope(), info->closure(), zone_);
}
- start_environment_->set_ast_id(BailoutId::FunctionEntry());
+ start_environment_->set_ast_id(BailoutId::Prologue());
entry_block_ = CreateBasicBlock();
entry_block_->SetInitialEnvironment(start_environment_);
}
return false;
}
- int slots = current_info()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
- if (current_info()->scope()->is_script_scope() && slots > 0) {
- Bailout(kScriptContext);
- return false;
- }
-
Scope* scope = current_info()->scope();
SetUpScope(scope);
void HOptimizedGraphBuilder::SetUpScope(Scope* scope) {
+ HEnvironment* prolog_env = environment();
+ int parameter_count = environment()->parameter_count();
+ ZoneList<HValue*> parameters(parameter_count, zone());
+ for (int i = 0; i < parameter_count; ++i) {
+ HInstruction* parameter = Add<HParameter>(static_cast<unsigned>(i));
+ parameters.Add(parameter, zone());
+ environment()->Bind(i, parameter);
+ }
+
+ HConstant* undefined_constant = graph()->GetConstantUndefined();
+ // Initialize specials and locals to undefined.
+ for (int i = parameter_count + 1; i < environment()->length(); ++i) {
+ environment()->Bind(i, undefined_constant);
+ }
+ Add<HPrologue>();
+
+ HEnvironment* initial_env = environment()->CopyWithoutHistory();
+ HBasicBlock* body_entry = CreateBasicBlock(initial_env);
+ GotoNoSimulate(body_entry);
+ set_current_block(body_entry);
+
+ // Initialize context of prolog environment to undefined.
+ prolog_env->BindContext(undefined_constant);
+
// First special is HContext.
HInstruction* context = Add<HContext>();
environment()->BindContext(context);
// Create an arguments object containing the initial parameters. Set the
// initial values of parameters including "this" having parameter index 0.
- DCHECK_EQ(scope->num_parameters() + 1, environment()->parameter_count());
- HArgumentsObject* arguments_object =
- New<HArgumentsObject>(environment()->parameter_count());
- for (int i = 0; i < environment()->parameter_count(); ++i) {
- HInstruction* parameter = Add<HParameter>(i);
+ DCHECK_EQ(scope->num_parameters() + 1, parameter_count);
+ HArgumentsObject* arguments_object = New<HArgumentsObject>(parameter_count);
+ for (int i = 0; i < parameter_count; ++i) {
+ HValue* parameter = parameters.at(i);
arguments_object->AddArgument(parameter, zone());
- environment()->Bind(i, parameter);
}
+
AddInstruction(arguments_object);
graph()->SetArgumentsObject(arguments_object);
- HConstant* undefined_constant = graph()->GetConstantUndefined();
- // Initialize specials and locals to undefined.
- for (int i = environment()->parameter_count() + 1;
- i < environment()->length();
- ++i) {
- environment()->Bind(i, undefined_constant);
- }
-
// Handle the arguments and arguments shadow variables specially (they do
// not have declarations).
if (scope->arguments() != NULL) {
- environment()->Bind(scope->arguments(),
- graph()->GetArgumentsObject());
+ environment()->Bind(scope->arguments(), graph()->GetArgumentsObject());
}
int rest_index;
scope->new_target_var() != nullptr) {
return Bailout(kSuperReference);
}
+
+ // Trace the call.
+ if (FLAG_trace && top_info()->IsOptimizing()) {
+ Add<HCallRuntime>(Runtime::FunctionForId(Runtime::kTraceEnter), 0);
+ }
}
}
+void HEnvironment::Print() const {
+ OFStream os(stdout);
+ os << *this << "\n";
+}
+
+
HEnvironment* HEnvironment::Copy() const {
return new(zone()) HEnvironment(this, zone());
}
void SetExpressionStackAt(int index_from_top, HValue* value);
HValue* RemoveExpressionStackAt(int index_from_top);
+ void Print() const;
+
HEnvironment* Copy() const;
HEnvironment* CopyWithoutHistory() const;
HEnvironment* CopyAsLoopHeader(HBasicBlock* block) const;
}
-template<>
-inline HContext* HGraphBuilder::New<HContext>() {
- return HContext::New(zone());
+template <>
+inline HParameter* HGraphBuilder::New<HParameter>(unsigned index) {
+ return HParameter::New(isolate(), zone(), nullptr, index);
}
-template<>
-inline HInstruction* HGraphBuilder::NewUncasted<HContext>() {
- return New<HContext>();
+template <>
+inline HParameter* HGraphBuilder::New<HParameter>(
+ unsigned index, HParameter::ParameterKind kind) {
+ return HParameter::New(isolate(), zone(), nullptr, index, kind);
+}
+
+
+template <>
+inline HParameter* HGraphBuilder::New<HParameter>(
+ unsigned index, HParameter::ParameterKind kind, Representation r) {
+ return HParameter::New(isolate(), zone(), nullptr, index, kind, r);
}
+
+template <>
+inline HPrologue* HGraphBuilder::New<HPrologue>() {
+ return HPrologue::New(zone());
+}
+
+
+template <>
+inline HContext* HGraphBuilder::New<HContext>() {
+ return HContext::New(zone());
+}
+
+
class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
public:
// A class encapsulating (lazily-allocated) break and continue blocks for
if (info()->saves_caller_doubles()) SaveCallerDoubles();
}
+ return !is_aborted();
+}
+
+
+void LCodeGen::DoPrologue(LPrologue* instr) {
+ Comment(";;; Prologue begin");
// Possibly allocate a local context.
- int heap_slots = info_->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
- if (heap_slots > 0) {
+ if (info_->num_heap_slots() > 0) {
Comment(";;; Allocate local context");
bool need_write_barrier = true;
// Argument to NewContext is the function, which is still in edi.
- DCHECK(!info()->scope()->is_script_scope());
- if (heap_slots <= FastNewContextStub::kMaximumSlots) {
- FastNewContextStub stub(isolate(), heap_slots);
+ int slots = info_->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
+ Safepoint::DeoptMode deopt_mode = Safepoint::kNoLazyDeopt;
+ if (info()->scope()->is_script_scope()) {
+ __ push(edi);
+ __ Push(info()->scope()->GetScopeInfo(info()->isolate()));
+ __ CallRuntime(Runtime::kNewScriptContext, 2);
+ deopt_mode = Safepoint::kLazyDeopt;
+ } else if (slots <= FastNewContextStub::kMaximumSlots) {
+ FastNewContextStub stub(isolate(), slots);
__ CallStub(&stub);
// Result of FastNewContextStub is always in new space.
need_write_barrier = false;
__ push(edi);
__ CallRuntime(Runtime::kNewFunctionContext, 1);
}
- RecordSafepoint(Safepoint::kNoLazyDeopt);
+ RecordSafepoint(deopt_mode);
+
// Context is returned in eax. It replaces the context passed to us.
// It's saved in the stack and kept live in esi.
__ mov(esi, eax);
Comment(";;; End allocate local context");
}
- // Trace the call.
- if (FLAG_trace && info()->IsOptimizing()) {
- // We have not executed any compiled code yet, so esi still holds the
- // incoming context.
- __ CallRuntime(Runtime::kTraceEnter, 0);
- }
- return !is_aborted();
+ Comment(";;; Prologue end");
}
}
chunk_->AddInstruction(instr, current_block_);
- if (instr->IsCall()) {
+ if (instr->IsCall() || instr->IsPrologue()) {
HValue* hydrogen_value_for_lazy_bailout = hydrogen_val;
if (hydrogen_val->HasObservableSideEffects()) {
HSimulate* sim = HSimulate::cast(hydrogen_val->next());
}
+LInstruction* LChunkBuilder::DoPrologue(HPrologue* instr) {
+ return new (zone()) LPrologue();
+}
+
+
LInstruction* LChunkBuilder::DoGoto(HGoto* instr) {
return new(zone()) LGoto(instr->FirstSuccessor());
}
V(OsrEntry) \
V(Parameter) \
V(Power) \
+ V(Prologue) \
V(PushArgument) \
V(RegExpLiteral) \
V(Return) \
};
+class LPrologue final : public LTemplateInstruction<0, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(Prologue, "prologue")
+};
+
+
class LLazyBailout final : public LTemplateInstruction<0, 0, 0> {
public:
DECLARE_CONCRETE_INSTRUCTION(LazyBailout, "lazy-bailout")
if (info()->saves_caller_doubles()) {
SaveCallerDoubles();
}
+ return !is_aborted();
+}
+
+
+void LCodeGen::DoPrologue(LPrologue* instr) {
+ Comment(";;; Prologue begin");
// Possibly allocate a local context.
- int heap_slots = info()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
- if (heap_slots > 0) {
+ if (info()->scope()->num_heap_slots() > 0) {
Comment(";;; Allocate local context");
bool need_write_barrier = true;
// Argument to NewContext is the function, which is in a1.
- DCHECK(!info()->scope()->is_script_scope());
- if (heap_slots <= FastNewContextStub::kMaximumSlots) {
- FastNewContextStub stub(isolate(), heap_slots);
+ int slots = info()->scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
+ Safepoint::DeoptMode deopt_mode = Safepoint::kNoLazyDeopt;
+ if (info()->scope()->is_script_scope()) {
+ __ push(a1);
+ __ Push(info()->scope()->GetScopeInfo(info()->isolate()));
+ __ CallRuntime(Runtime::kNewScriptContext, 2);
+ deopt_mode = Safepoint::kLazyDeopt;
+ } else if (slots <= FastNewContextStub::kMaximumSlots) {
+ FastNewContextStub stub(isolate(), slots);
__ CallStub(&stub);
// Result of FastNewContextStub is always in new space.
need_write_barrier = false;
__ push(a1);
__ CallRuntime(Runtime::kNewFunctionContext, 1);
}
- RecordSafepoint(Safepoint::kNoLazyDeopt);
+ RecordSafepoint(deopt_mode);
+
// Context is returned in both v0. It replaces the context passed to us.
// It's saved in the stack and kept live in cp.
__ mov(cp, v0);
Comment(";;; End allocate local context");
}
- // Trace the call.
- if (FLAG_trace && info()->IsOptimizing()) {
- // We have not executed any compiled code yet, so cp still holds the
- // incoming context.
- __ CallRuntime(Runtime::kTraceEnter, 0);
- }
- return !is_aborted();
+ Comment(";;; Prologue end");
}
}
chunk_->AddInstruction(instr, current_block_);
- if (instr->IsCall()) {
+ if (instr->IsCall() || instr->IsPrologue()) {
HValue* hydrogen_value_for_lazy_bailout = hydrogen_val;
if (hydrogen_val->HasObservableSideEffects()) {
HSimulate* sim = HSimulate::cast(hydrogen_val->next());
}
+LInstruction* LChunkBuilder::DoPrologue(HPrologue* instr) {
+ return new (zone()) LPrologue();
+}
+
+
LInstruction* LChunkBuilder::DoGoto(HGoto* instr) {
return new(zone()) LGoto(instr->FirstSuccessor());
}
V(OsrEntry) \
V(Parameter) \
V(Power) \
+ V(Prologue) \
V(PushArgument) \
V(RegExpLiteral) \
V(Return) \
};
+class LPrologue final : public LTemplateInstruction<0, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(Prologue, "prologue")
+};
+
+
class LLazyBailout final : public LTemplateInstruction<0, 0, 0> {
public:
LLazyBailout() : gap_instructions_size_(0) { }
if (info()->saves_caller_doubles()) {
SaveCallerDoubles();
}
+ return !is_aborted();
+}
+
+
+void LCodeGen::DoPrologue(LPrologue* instr) {
+ Comment(";;; Prologue begin");
// Possibly allocate a local context.
- int heap_slots = info()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
- if (heap_slots > 0) {
+ if (info()->scope()->num_heap_slots() > 0) {
Comment(";;; Allocate local context");
bool need_write_barrier = true;
// Argument to NewContext is the function, which is in a1.
- DCHECK(!info()->scope()->is_script_scope());
- if (heap_slots <= FastNewContextStub::kMaximumSlots) {
- FastNewContextStub stub(isolate(), heap_slots);
+ int slots = info()->scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
+ Safepoint::DeoptMode deopt_mode = Safepoint::kNoLazyDeopt;
+ if (info()->scope()->is_script_scope()) {
+ __ push(a1);
+ __ Push(info()->scope()->GetScopeInfo(info()->isolate()));
+ __ CallRuntime(Runtime::kNewScriptContext, 2);
+ deopt_mode = Safepoint::kLazyDeopt;
+ } else if (slots <= FastNewContextStub::kMaximumSlots) {
+ FastNewContextStub stub(isolate(), slots);
__ CallStub(&stub);
// Result of FastNewContextStub is always in new space.
need_write_barrier = false;
__ push(a1);
__ CallRuntime(Runtime::kNewFunctionContext, 1);
}
- RecordSafepoint(Safepoint::kNoLazyDeopt);
+ RecordSafepoint(deopt_mode);
+
// Context is returned in both v0. It replaces the context passed to us.
// It's saved in the stack and kept live in cp.
__ mov(cp, v0);
Comment(";;; End allocate local context");
}
- // Trace the call.
- if (FLAG_trace && info()->IsOptimizing()) {
- // We have not executed any compiled code yet, so cp still holds the
- // incoming context.
- __ CallRuntime(Runtime::kTraceEnter, 0);
- }
- return !is_aborted();
+ Comment(";;; Prologue end");
}
}
chunk_->AddInstruction(instr, current_block_);
- if (instr->IsCall()) {
+ if (instr->IsCall() || instr->IsPrologue()) {
HValue* hydrogen_value_for_lazy_bailout = hydrogen_val;
if (hydrogen_val->HasObservableSideEffects()) {
HSimulate* sim = HSimulate::cast(hydrogen_val->next());
}
+LInstruction* LChunkBuilder::DoPrologue(HPrologue* instr) {
+ return new (zone()) LPrologue();
+}
+
+
LInstruction* LChunkBuilder::DoGoto(HGoto* instr) {
return new(zone()) LGoto(instr->FirstSuccessor());
}
V(OsrEntry) \
V(Parameter) \
V(Power) \
+ V(Prologue) \
V(PushArgument) \
V(RegExpLiteral) \
V(Return) \
};
+class LPrologue final : public LTemplateInstruction<0, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(Prologue, "prologue")
+};
+
+
class LLazyBailout final : public LTemplateInstruction<0, 0, 0> {
public:
LLazyBailout() : gap_instructions_size_(0) { }
int ToInt() const { return id_; }
static BailoutId None() { return BailoutId(kNoneId); }
+ static BailoutId Prologue() { return BailoutId(kPrologueId); }
static BailoutId FunctionEntry() { return BailoutId(kFunctionEntryId); }
static BailoutId Declarations() { return BailoutId(kDeclarationsId); }
static BailoutId FirstUsable() { return BailoutId(kFirstUsableId); }
static const int kNoneId = -1;
// Using 0 could disguise errors.
+ static const int kPrologueId = 1;
static const int kFunctionEntryId = 2;
// This AST id identifies the point after the declarations have been visited.
SaveCallerDoubles();
}
}
+ return !is_aborted();
+}
+
+
+void LCodeGen::DoPrologue(LPrologue* instr) {
+ Comment(";;; Prologue begin");
// Possibly allocate a local context.
- int heap_slots = info_->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
- if (heap_slots > 0) {
+ if (info_->num_heap_slots() > 0) {
Comment(";;; Allocate local context");
bool need_write_barrier = true;
// Argument to NewContext is the function, which is still in rdi.
- DCHECK(!info()->scope()->is_script_scope());
- if (heap_slots <= FastNewContextStub::kMaximumSlots) {
- FastNewContextStub stub(isolate(), heap_slots);
+ int slots = info_->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
+ Safepoint::DeoptMode deopt_mode = Safepoint::kNoLazyDeopt;
+ if (info()->scope()->is_script_scope()) {
+ __ Push(rdi);
+ __ Push(info()->scope()->GetScopeInfo(info()->isolate()));
+ __ CallRuntime(Runtime::kNewScriptContext, 2);
+ deopt_mode = Safepoint::kLazyDeopt;
+ } else if (slots <= FastNewContextStub::kMaximumSlots) {
+ FastNewContextStub stub(isolate(), slots);
__ CallStub(&stub);
// Result of FastNewContextStub is always in new space.
need_write_barrier = false;
__ Push(rdi);
__ CallRuntime(Runtime::kNewFunctionContext, 1);
}
- RecordSafepoint(Safepoint::kNoLazyDeopt);
+ RecordSafepoint(deopt_mode);
+
// Context is returned in rax. It replaces the context passed to us.
// It's saved in the stack and kept live in rsi.
__ movp(rsi, rax);
Comment(";;; End allocate local context");
}
- // Trace the call.
- if (FLAG_trace && info()->IsOptimizing()) {
- __ CallRuntime(Runtime::kTraceEnter, 0);
- }
- return !is_aborted();
+ Comment(";;; Prologue end");
}
}
chunk_->AddInstruction(instr, current_block_);
- if (instr->IsCall()) {
+ if (instr->IsCall() || instr->IsPrologue()) {
HValue* hydrogen_value_for_lazy_bailout = hydrogen_val;
if (hydrogen_val->HasObservableSideEffects()) {
HSimulate* sim = HSimulate::cast(hydrogen_val->next());
}
+LInstruction* LChunkBuilder::DoPrologue(HPrologue* instr) {
+ return new (zone()) LPrologue();
+}
+
+
LInstruction* LChunkBuilder::DoDebugBreak(HDebugBreak* instr) {
return new(zone()) LDebugBreak();
}
V(OsrEntry) \
V(Parameter) \
V(Power) \
+ V(Prologue) \
V(PushArgument) \
V(RegExpLiteral) \
V(Return) \
};
+class LPrologue final : public LTemplateInstruction<0, 0, 0> {
+ public:
+ DECLARE_CONCRETE_INSTRUCTION(Prologue, "prologue")
+};
+
+
class LLazyBailout final : public LTemplateInstruction<0, 0, 0> {
public:
LLazyBailout() : gap_instructions_size_(0) { }