Commit acbb968d authored by ishell's avatar ishell Committed by Commit bot

[crankshaft] Fixing ES6 tail call elimination.

In case when F inlined normal call to G which tail calls H we should not write translation for G for the tail call site.
Otherwise we will see G in a stack trace inside H.

This CL also enables all existing tests related to ES6 tail call elimination and adds more combinations.

TBR=bmeurer@chromium.org
BUG=v8:4698
LOG=N

Committed: https://crrev.com/689980f7d4dfd4c29492f616d7b616b86ec9af91
Cr-Commit-Position: refs/heads/master@{#34830}

Review URL: https://codereview.chromium.org/1780043004

Cr-Commit-Position: refs/heads/master@{#34920}
parent 2a1570ef
...@@ -546,12 +546,7 @@ LInstruction* LChunkBuilder::DefineFixedDouble( ...@@ -546,12 +546,7 @@ LInstruction* LChunkBuilder::DefineFixedDouble(
LInstruction* LChunkBuilder::AssignEnvironment(LInstruction* instr) { LInstruction* LChunkBuilder::AssignEnvironment(LInstruction* instr) {
HEnvironment* hydrogen_env = current_block_->last_environment(); HEnvironment* hydrogen_env = current_block_->last_environment();
int argument_index_accumulator = 0; return LChunkBuilderBase::AssignEnvironment(instr, hydrogen_env);
ZoneList<HValue*> objects_to_materialize(0, zone());
instr->set_environment(CreateEnvironment(hydrogen_env,
&argument_index_accumulator,
&objects_to_materialize));
return instr;
} }
...@@ -877,15 +872,39 @@ void LChunkBuilder::AddInstruction(LInstruction* instr, ...@@ -877,15 +872,39 @@ void LChunkBuilder::AddInstruction(LInstruction* instr,
chunk_->AddInstruction(instr, current_block_); chunk_->AddInstruction(instr, current_block_);
if (instr->IsCall()) { if (instr->IsCall()) {
HEnvironment* hydrogen_env = current_block_->last_environment();
HValue* hydrogen_value_for_lazy_bailout = hydrogen_val; HValue* hydrogen_value_for_lazy_bailout = hydrogen_val;
if (hydrogen_val->HasObservableSideEffects()) { DCHECK_NOT_NULL(hydrogen_env);
HSimulate* sim = HSimulate::cast(hydrogen_val->next()); if (instr->IsSyntacticTailCall()) {
sim->ReplayEnvironment(current_block_->last_environment()); // If it was a syntactic tail call we need to drop the current frame and
hydrogen_value_for_lazy_bailout = sim; // an arguments adaptor frame on top of it (if the latter is present).
hydrogen_env = hydrogen_env->outer();
if (hydrogen_env != nullptr &&
hydrogen_env->frame_type() == ARGUMENTS_ADAPTOR) {
hydrogen_env = hydrogen_env->outer();
}
if (hydrogen_env != nullptr) {
// Push return value on top of outer environment.
hydrogen_env = hydrogen_env->Copy();
hydrogen_env->Push(hydrogen_val);
}
} else {
if (hydrogen_val->HasObservableSideEffects()) {
HSimulate* sim = HSimulate::cast(hydrogen_val->next());
sim->ReplayEnvironment(hydrogen_env);
hydrogen_value_for_lazy_bailout = sim;
}
}
if (hydrogen_env != nullptr) {
// The |hydrogen_env| can be null at this point only if we are generating
// a syntactic tail call from the outermost function but in this case
// it would be a real tail call which will pop function's frame and
// therefore this lazy bailout can be skipped.
LInstruction* bailout = LChunkBuilderBase::AssignEnvironment(
new (zone()) LLazyBailout(), hydrogen_env);
bailout->set_hydrogen_value(hydrogen_value_for_lazy_bailout);
chunk_->AddInstruction(bailout, current_block_);
} }
LInstruction* bailout = AssignEnvironment(new(zone()) LLazyBailout());
bailout->set_hydrogen_value(hydrogen_value_for_lazy_bailout);
chunk_->AddInstruction(bailout, current_block_);
} }
} }
...@@ -1065,6 +1084,9 @@ LInstruction* LChunkBuilder::DoCallWithDescriptor( ...@@ -1065,6 +1084,9 @@ LInstruction* LChunkBuilder::DoCallWithDescriptor(
LCallWithDescriptor* result = new(zone()) LCallWithDescriptor( LCallWithDescriptor* result = new(zone()) LCallWithDescriptor(
descriptor, ops, zone()); descriptor, ops, zone());
if (instr->syntactic_tail_call_mode() == TailCallMode::kAllow) {
result->MarkAsSyntacticTailCall();
}
return MarkAsCall(DefineFixed(result, r0), instr); return MarkAsCall(DefineFixed(result, r0), instr);
} }
...@@ -1073,6 +1095,9 @@ LInstruction* LChunkBuilder::DoInvokeFunction(HInvokeFunction* instr) { ...@@ -1073,6 +1095,9 @@ LInstruction* LChunkBuilder::DoInvokeFunction(HInvokeFunction* instr) {
LOperand* context = UseFixed(instr->context(), cp); LOperand* context = UseFixed(instr->context(), cp);
LOperand* function = UseFixed(instr->function(), r1); LOperand* function = UseFixed(instr->function(), r1);
LInvokeFunction* result = new(zone()) LInvokeFunction(context, function); LInvokeFunction* result = new(zone()) LInvokeFunction(context, function);
if (instr->syntactic_tail_call_mode() == TailCallMode::kAllow) {
result->MarkAsSyntacticTailCall();
}
return MarkAsCall(DefineFixed(result, r0), instr, CANNOT_DEOPTIMIZE_EAGERLY); return MarkAsCall(DefineFixed(result, r0), instr, CANNOT_DEOPTIMIZE_EAGERLY);
} }
......
...@@ -224,6 +224,13 @@ class LInstruction : public ZoneObject { ...@@ -224,6 +224,13 @@ class LInstruction : public ZoneObject {
void MarkAsCall() { bit_field_ = IsCallBits::update(bit_field_, true); } void MarkAsCall() { bit_field_ = IsCallBits::update(bit_field_, true); }
bool IsCall() const { return IsCallBits::decode(bit_field_); } bool IsCall() const { return IsCallBits::decode(bit_field_); }
void MarkAsSyntacticTailCall() {
bit_field_ = IsSyntacticTailCallBits::update(bit_field_, true);
}
bool IsSyntacticTailCall() const {
return IsSyntacticTailCallBits::decode(bit_field_);
}
// Interface to the register allocator and iterators. // Interface to the register allocator and iterators.
bool ClobbersTemps() const { return IsCall(); } bool ClobbersTemps() const { return IsCall(); }
bool ClobbersRegisters() const { return IsCall(); } bool ClobbersRegisters() const { return IsCall(); }
...@@ -258,6 +265,8 @@ class LInstruction : public ZoneObject { ...@@ -258,6 +265,8 @@ class LInstruction : public ZoneObject {
virtual LOperand* TempAt(int i) = 0; virtual LOperand* TempAt(int i) = 0;
class IsCallBits: public BitField<bool, 0, 1> {}; class IsCallBits: public BitField<bool, 0, 1> {};
class IsSyntacticTailCallBits : public BitField<bool, IsCallBits::kNext, 1> {
};
LEnvironment* environment_; LEnvironment* environment_;
SetOncePointer<LPointerMap> pointer_map_; SetOncePointer<LPointerMap> pointer_map_;
......
...@@ -715,27 +715,46 @@ void LChunkBuilder::AddInstruction(LInstruction* instr, ...@@ -715,27 +715,46 @@ void LChunkBuilder::AddInstruction(LInstruction* instr,
chunk_->AddInstruction(instr, current_block_); chunk_->AddInstruction(instr, current_block_);
if (instr->IsCall()) { if (instr->IsCall()) {
HEnvironment* hydrogen_env = current_block_->last_environment();
HValue* hydrogen_value_for_lazy_bailout = hydrogen_val; HValue* hydrogen_value_for_lazy_bailout = hydrogen_val;
if (hydrogen_val->HasObservableSideEffects()) { DCHECK_NOT_NULL(hydrogen_env);
HSimulate* sim = HSimulate::cast(hydrogen_val->next()); if (instr->IsSyntacticTailCall()) {
sim->ReplayEnvironment(current_block_->last_environment()); // If it was a syntactic tail call we need to drop the current frame and
hydrogen_value_for_lazy_bailout = sim; // an arguments adaptor frame on top of it (if the latter is present).
hydrogen_env = hydrogen_env->outer();
if (hydrogen_env != nullptr &&
hydrogen_env->frame_type() == ARGUMENTS_ADAPTOR) {
hydrogen_env = hydrogen_env->outer();
}
if (hydrogen_env != nullptr) {
// Push return value on top of outer environment.
hydrogen_env = hydrogen_env->Copy();
hydrogen_env->Push(hydrogen_val);
}
} else {
if (hydrogen_val->HasObservableSideEffects()) {
HSimulate* sim = HSimulate::cast(hydrogen_val->next());
sim->ReplayEnvironment(hydrogen_env);
hydrogen_value_for_lazy_bailout = sim;
}
}
if (hydrogen_env != nullptr) {
// The |hydrogen_env| can be null at this point only if we are generating
// a syntactic tail call from the outermost function but in this case
// it would be a real tail call which will pop function's frame and
// therefore this lazy bailout can be skipped.
LInstruction* bailout = LChunkBuilderBase::AssignEnvironment(
new (zone()) LLazyBailout(), hydrogen_env);
bailout->set_hydrogen_value(hydrogen_value_for_lazy_bailout);
chunk_->AddInstruction(bailout, current_block_);
} }
LInstruction* bailout = AssignEnvironment(new(zone()) LLazyBailout());
bailout->set_hydrogen_value(hydrogen_value_for_lazy_bailout);
chunk_->AddInstruction(bailout, current_block_);
} }
} }
LInstruction* LChunkBuilder::AssignEnvironment(LInstruction* instr) { LInstruction* LChunkBuilder::AssignEnvironment(LInstruction* instr) {
HEnvironment* hydrogen_env = current_block_->last_environment(); HEnvironment* hydrogen_env = current_block_->last_environment();
int argument_index_accumulator = 0; return LChunkBuilderBase::AssignEnvironment(instr, hydrogen_env);
ZoneList<HValue*> objects_to_materialize(0, zone());
instr->set_environment(CreateEnvironment(hydrogen_env,
&argument_index_accumulator,
&objects_to_materialize));
return instr;
} }
...@@ -1018,6 +1037,9 @@ LInstruction* LChunkBuilder::DoCallWithDescriptor( ...@@ -1018,6 +1037,9 @@ LInstruction* LChunkBuilder::DoCallWithDescriptor(
LCallWithDescriptor* result = new(zone()) LCallWithDescriptor(descriptor, LCallWithDescriptor* result = new(zone()) LCallWithDescriptor(descriptor,
ops, ops,
zone()); zone());
if (instr->syntactic_tail_call_mode() == TailCallMode::kAllow) {
result->MarkAsSyntacticTailCall();
}
return MarkAsCall(DefineFixed(result, x0), instr); return MarkAsCall(DefineFixed(result, x0), instr);
} }
...@@ -1510,6 +1532,9 @@ LInstruction* LChunkBuilder::DoInvokeFunction(HInvokeFunction* instr) { ...@@ -1510,6 +1532,9 @@ LInstruction* LChunkBuilder::DoInvokeFunction(HInvokeFunction* instr) {
// The function is required (by MacroAssembler::InvokeFunction) to be in x1. // The function is required (by MacroAssembler::InvokeFunction) to be in x1.
LOperand* function = UseFixed(instr->function(), x1); LOperand* function = UseFixed(instr->function(), x1);
LInvokeFunction* result = new(zone()) LInvokeFunction(context, function); LInvokeFunction* result = new(zone()) LInvokeFunction(context, function);
if (instr->syntactic_tail_call_mode() == TailCallMode::kAllow) {
result->MarkAsSyntacticTailCall();
}
return MarkAsCall(DefineFixed(result, x0), instr, CANNOT_DEOPTIMIZE_EAGERLY); return MarkAsCall(DefineFixed(result, x0), instr, CANNOT_DEOPTIMIZE_EAGERLY);
} }
......
...@@ -235,6 +235,13 @@ class LInstruction : public ZoneObject { ...@@ -235,6 +235,13 @@ class LInstruction : public ZoneObject {
void MarkAsCall() { bit_field_ = IsCallBits::update(bit_field_, true); } void MarkAsCall() { bit_field_ = IsCallBits::update(bit_field_, true); }
bool IsCall() const { return IsCallBits::decode(bit_field_); } bool IsCall() const { return IsCallBits::decode(bit_field_); }
void MarkAsSyntacticTailCall() {
bit_field_ = IsSyntacticTailCallBits::update(bit_field_, true);
}
bool IsSyntacticTailCall() const {
return IsSyntacticTailCallBits::decode(bit_field_);
}
// Interface to the register allocator and iterators. // Interface to the register allocator and iterators.
bool ClobbersTemps() const { return IsCall(); } bool ClobbersTemps() const { return IsCall(); }
bool ClobbersRegisters() const { return IsCall(); } bool ClobbersRegisters() const { return IsCall(); }
...@@ -262,6 +269,8 @@ class LInstruction : public ZoneObject { ...@@ -262,6 +269,8 @@ class LInstruction : public ZoneObject {
private: private:
class IsCallBits: public BitField<bool, 0, 1> {}; class IsCallBits: public BitField<bool, 0, 1> {};
class IsSyntacticTailCallBits : public BitField<bool, IsCallBits::kNext, 1> {
};
LEnvironment* environment_; LEnvironment* environment_;
SetOncePointer<LPointerMap> pointer_map_; SetOncePointer<LPointerMap> pointer_map_;
......
...@@ -911,6 +911,18 @@ std::ostream& HBinaryCall::PrintDataTo(std::ostream& os) const { // NOLINT ...@@ -911,6 +911,18 @@ std::ostream& HBinaryCall::PrintDataTo(std::ostream& os) const { // NOLINT
<< argument_count(); << argument_count();
} }
std::ostream& HInvokeFunction::PrintTo(std::ostream& os) const { // NOLINT
if (tail_call_mode() == TailCallMode::kAllow) os << "Tail";
return HBinaryCall::PrintTo(os);
}
std::ostream& HInvokeFunction::PrintDataTo(std::ostream& os) const { // NOLINT
HBinaryCall::PrintDataTo(os);
if (syntactic_tail_call_mode() == TailCallMode::kAllow) {
os << ", JSTailCall";
}
return os;
}
void HBoundsCheck::ApplyIndexChange() { void HBoundsCheck::ApplyIndexChange() {
if (skip_check()) return; if (skip_check()) return;
...@@ -1033,7 +1045,11 @@ std::ostream& HCallWithDescriptor::PrintDataTo( ...@@ -1033,7 +1045,11 @@ std::ostream& HCallWithDescriptor::PrintDataTo(
for (int i = 0; i < OperandCount(); i++) { for (int i = 0; i < OperandCount(); i++) {
os << NameOf(OperandAt(i)) << " "; os << NameOf(OperandAt(i)) << " ";
} }
return os << "#" << argument_count(); os << "#" << argument_count();
if (syntactic_tail_call_mode() == TailCallMode::kAllow) {
os << ", JSTailCall";
}
return os;
} }
......
...@@ -2214,19 +2214,17 @@ class HBinaryCall : public HCall<2> { ...@@ -2214,19 +2214,17 @@ class HBinaryCall : public HCall<2> {
}; };
enum CallMode { NORMAL_CALL, TAIL_CALL };
class HCallWithDescriptor final : public HInstruction { class HCallWithDescriptor final : public HInstruction {
public: public:
static HCallWithDescriptor* New(Isolate* isolate, Zone* zone, HValue* context, static HCallWithDescriptor* New(
HValue* target, int argument_count, Isolate* isolate, Zone* zone, HValue* context, HValue* target,
CallInterfaceDescriptor descriptor, int argument_count, CallInterfaceDescriptor descriptor,
const Vector<HValue*>& operands, const Vector<HValue*>& operands,
CallMode call_mode = NORMAL_CALL) { TailCallMode syntactic_tail_call_mode = TailCallMode::kDisallow,
HCallWithDescriptor* res = new (zone) HCallWithDescriptor( TailCallMode tail_call_mode = TailCallMode::kDisallow) {
target, argument_count, descriptor, operands, call_mode, zone); HCallWithDescriptor* res = new (zone)
DCHECK_EQ(operands.length(), res->GetParameterCount()); HCallWithDescriptor(target, argument_count, descriptor, operands,
syntactic_tail_call_mode, tail_call_mode, zone);
return res; return res;
} }
...@@ -2248,7 +2246,16 @@ class HCallWithDescriptor final : public HInstruction { ...@@ -2248,7 +2246,16 @@ class HCallWithDescriptor final : public HInstruction {
HType CalculateInferredType() final { return HType::Tagged(); } HType CalculateInferredType() final { return HType::Tagged(); }
bool IsTailCall() const { return call_mode_ == TAIL_CALL; } // Defines whether this instruction corresponds to a JS call at tail position.
TailCallMode syntactic_tail_call_mode() const {
return SyntacticTailCallModeField::decode(bit_field_);
}
// Defines whether this call should be generated as a tail call.
TailCallMode tail_call_mode() const {
return TailCallModeField::decode(bit_field_);
}
bool IsTailCall() const { return tail_call_mode() == TailCallMode::kAllow; }
virtual int argument_count() const { virtual int argument_count() const {
return argument_count_; return argument_count_;
...@@ -2268,14 +2275,18 @@ class HCallWithDescriptor final : public HInstruction { ...@@ -2268,14 +2275,18 @@ class HCallWithDescriptor final : public HInstruction {
// The argument count includes the receiver. // The argument count includes the receiver.
HCallWithDescriptor(HValue* target, int argument_count, HCallWithDescriptor(HValue* target, int argument_count,
CallInterfaceDescriptor descriptor, CallInterfaceDescriptor descriptor,
const Vector<HValue*>& operands, CallMode call_mode, const Vector<HValue*>& operands,
Zone* zone) TailCallMode syntactic_tail_call_mode,
TailCallMode tail_call_mode, Zone* zone)
: descriptor_(descriptor), : descriptor_(descriptor),
values_(GetParameterCount() + 1, zone), values_(GetParameterCount() + 1, zone),
argument_count_(argument_count), argument_count_(argument_count),
call_mode_(call_mode) { bit_field_(
TailCallModeField::encode(tail_call_mode) |
SyntacticTailCallModeField::encode(syntactic_tail_call_mode)) {
DCHECK_EQ(operands.length(), GetParameterCount());
// We can only tail call without any stack arguments. // We can only tail call without any stack arguments.
DCHECK(call_mode != TAIL_CALL || argument_count == 0); DCHECK(tail_call_mode != TailCallMode::kAllow || argument_count == 0);
AddOperand(target, zone); AddOperand(target, zone);
for (int i = 0; i < operands.length(); i++) { for (int i = 0; i < operands.length(); i++) {
AddOperand(operands[i], zone); AddOperand(operands[i], zone);
...@@ -2300,15 +2311,18 @@ class HCallWithDescriptor final : public HInstruction { ...@@ -2300,15 +2311,18 @@ class HCallWithDescriptor final : public HInstruction {
CallInterfaceDescriptor descriptor_; CallInterfaceDescriptor descriptor_;
ZoneList<HValue*> values_; ZoneList<HValue*> values_;
int argument_count_; int argument_count_;
CallMode call_mode_; class TailCallModeField : public BitField<TailCallMode, 0, 1> {};
class SyntacticTailCallModeField
: public BitField<TailCallMode, TailCallModeField::kNext, 1> {};
uint32_t bit_field_;
}; };
class HInvokeFunction final : public HBinaryCall { class HInvokeFunction final : public HBinaryCall {
public: public:
DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P4(HInvokeFunction, HValue*, DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P5(HInvokeFunction, HValue*,
Handle<JSFunction>, int, Handle<JSFunction>, int,
TailCallMode); TailCallMode, TailCallMode);
HValue* context() { return first(); } HValue* context() { return first(); }
HValue* function() { return second(); } HValue* function() { return second(); }
...@@ -2316,12 +2330,22 @@ class HInvokeFunction final : public HBinaryCall { ...@@ -2316,12 +2330,22 @@ class HInvokeFunction final : public HBinaryCall {
int formal_parameter_count() const { return formal_parameter_count_; } int formal_parameter_count() const { return formal_parameter_count_; }
bool HasStackCheck() final { return HasStackCheckField::decode(bit_field_); } bool HasStackCheck() final { return HasStackCheckField::decode(bit_field_); }
// Defines whether this instruction corresponds to a JS call at tail position.
TailCallMode syntactic_tail_call_mode() const {
return SyntacticTailCallModeField::decode(bit_field_);
}
// Defines whether this call should be generated as a tail call.
TailCallMode tail_call_mode() const { TailCallMode tail_call_mode() const {
return TailCallModeField::decode(bit_field_); return TailCallModeField::decode(bit_field_);
} }
DECLARE_CONCRETE_INSTRUCTION(InvokeFunction) DECLARE_CONCRETE_INSTRUCTION(InvokeFunction)
std::ostream& PrintTo(std::ostream& os) const override; // NOLINT
std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT
private: private:
void set_has_stack_check(bool has_stack_check) { void set_has_stack_check(bool has_stack_check) {
bit_field_ = HasStackCheckField::update(bit_field_, has_stack_check); bit_field_ = HasStackCheckField::update(bit_field_, has_stack_check);
...@@ -2329,10 +2353,15 @@ class HInvokeFunction final : public HBinaryCall { ...@@ -2329,10 +2353,15 @@ class HInvokeFunction final : public HBinaryCall {
HInvokeFunction(HValue* context, HValue* function, HInvokeFunction(HValue* context, HValue* function,
Handle<JSFunction> known_function, int argument_count, Handle<JSFunction> known_function, int argument_count,
TailCallMode syntactic_tail_call_mode,
TailCallMode tail_call_mode) TailCallMode tail_call_mode)
: HBinaryCall(context, function, argument_count), : HBinaryCall(context, function, argument_count),
known_function_(known_function), known_function_(known_function),
bit_field_(TailCallModeField::encode(tail_call_mode)) { bit_field_(
TailCallModeField::encode(tail_call_mode) |
SyntacticTailCallModeField::encode(syntactic_tail_call_mode)) {
DCHECK(tail_call_mode != TailCallMode::kAllow ||
syntactic_tail_call_mode == TailCallMode::kAllow);
formal_parameter_count_ = formal_parameter_count_ =
known_function.is_null() known_function.is_null()
? 0 ? 0
...@@ -2349,6 +2378,8 @@ class HInvokeFunction final : public HBinaryCall { ...@@ -2349,6 +2378,8 @@ class HInvokeFunction final : public HBinaryCall {
class HasStackCheckField : public BitField<bool, 0, 1> {}; class HasStackCheckField : public BitField<bool, 0, 1> {};
class TailCallModeField class TailCallModeField
: public BitField<TailCallMode, HasStackCheckField::kNext, 1> {}; : public BitField<TailCallMode, HasStackCheckField::kNext, 1> {};
class SyntacticTailCallModeField
: public BitField<TailCallMode, TailCallModeField::kNext, 1> {};
uint32_t bit_field_; uint32_t bit_field_;
}; };
......
This diff is collapsed.
...@@ -2838,16 +2838,19 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor { ...@@ -2838,16 +2838,19 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
Handle<Map> receiver_map); Handle<Map> receiver_map);
HInstruction* NewCallFunction(HValue* function, int argument_count, HInstruction* NewCallFunction(HValue* function, int argument_count,
TailCallMode syntactic_tail_call_mode,
ConvertReceiverMode convert_mode, ConvertReceiverMode convert_mode,
TailCallMode tail_call_mode); TailCallMode tail_call_mode);
HInstruction* NewCallFunctionViaIC(HValue* function, int argument_count, HInstruction* NewCallFunctionViaIC(HValue* function, int argument_count,
TailCallMode syntactic_tail_call_mode,
ConvertReceiverMode convert_mode, ConvertReceiverMode convert_mode,
TailCallMode tail_call_mode, TailCallMode tail_call_mode,
FeedbackVectorSlot slot); FeedbackVectorSlot slot);
HInstruction* NewCallConstantFunction(Handle<JSFunction> target, HInstruction* NewCallConstantFunction(Handle<JSFunction> target,
int argument_count, int argument_count,
TailCallMode syntactic_tail_call_mode,
TailCallMode tail_call_mode); TailCallMode tail_call_mode);
bool CanBeFunctionApplyArguments(Call* expr); bool CanBeFunctionApplyArguments(Call* expr);
......
...@@ -590,11 +590,7 @@ LInstruction* LChunkBuilder::DefineFixedDouble( ...@@ -590,11 +590,7 @@ LInstruction* LChunkBuilder::DefineFixedDouble(
LInstruction* LChunkBuilder::AssignEnvironment(LInstruction* instr) { LInstruction* LChunkBuilder::AssignEnvironment(LInstruction* instr) {
HEnvironment* hydrogen_env = current_block_->last_environment(); HEnvironment* hydrogen_env = current_block_->last_environment();
int argument_index_accumulator = 0; return LChunkBuilderBase::AssignEnvironment(instr, hydrogen_env);
ZoneList<HValue*> objects_to_materialize(0, zone());
instr->set_environment(CreateEnvironment(
hydrogen_env, &argument_index_accumulator, &objects_to_materialize));
return instr;
} }
...@@ -909,15 +905,39 @@ void LChunkBuilder::AddInstruction(LInstruction* instr, ...@@ -909,15 +905,39 @@ void LChunkBuilder::AddInstruction(LInstruction* instr,
chunk_->AddInstruction(instr, current_block_); chunk_->AddInstruction(instr, current_block_);
if (instr->IsCall()) { if (instr->IsCall()) {
HEnvironment* hydrogen_env = current_block_->last_environment();
HValue* hydrogen_value_for_lazy_bailout = hydrogen_val; HValue* hydrogen_value_for_lazy_bailout = hydrogen_val;
if (hydrogen_val->HasObservableSideEffects()) { DCHECK_NOT_NULL(hydrogen_env);
HSimulate* sim = HSimulate::cast(hydrogen_val->next()); if (instr->IsSyntacticTailCall()) {
sim->ReplayEnvironment(current_block_->last_environment()); // If it was a syntactic tail call we need to drop the current frame and
hydrogen_value_for_lazy_bailout = sim; // an arguments adaptor frame on top of it (if the latter is present).
hydrogen_env = hydrogen_env->outer();
if (hydrogen_env != nullptr &&
hydrogen_env->frame_type() == ARGUMENTS_ADAPTOR) {
hydrogen_env = hydrogen_env->outer();
}
if (hydrogen_env != nullptr) {
// Push return value on top of outer environment.
hydrogen_env = hydrogen_env->Copy();
hydrogen_env->Push(hydrogen_val);
}
} else {
if (hydrogen_val->HasObservableSideEffects()) {
HSimulate* sim = HSimulate::cast(hydrogen_val->next());
sim->ReplayEnvironment(hydrogen_env);
hydrogen_value_for_lazy_bailout = sim;
}
}
if (hydrogen_env != nullptr) {
// The |hydrogen_env| can be null at this point only if we are generating
// a syntactic tail call from the outermost function but in this case
// it would be a real tail call which will pop function's frame and
// therefore this lazy bailout can be skipped.
LInstruction* bailout = LChunkBuilderBase::AssignEnvironment(
new (zone()) LLazyBailout(), hydrogen_env);
bailout->set_hydrogen_value(hydrogen_value_for_lazy_bailout);
chunk_->AddInstruction(bailout, current_block_);
} }
LInstruction* bailout = AssignEnvironment(new(zone()) LLazyBailout());
bailout->set_hydrogen_value(hydrogen_value_for_lazy_bailout);
chunk_->AddInstruction(bailout, current_block_);
} }
} }
...@@ -1098,6 +1118,9 @@ LInstruction* LChunkBuilder::DoCallWithDescriptor( ...@@ -1098,6 +1118,9 @@ LInstruction* LChunkBuilder::DoCallWithDescriptor(
LCallWithDescriptor* result = new(zone()) LCallWithDescriptor( LCallWithDescriptor* result = new(zone()) LCallWithDescriptor(
descriptor, ops, zone()); descriptor, ops, zone());
if (instr->syntactic_tail_call_mode() == TailCallMode::kAllow) {
result->MarkAsSyntacticTailCall();
}
return MarkAsCall(DefineFixed(result, eax), instr, CANNOT_DEOPTIMIZE_EAGERLY); return MarkAsCall(DefineFixed(result, eax), instr, CANNOT_DEOPTIMIZE_EAGERLY);
} }
...@@ -1106,6 +1129,9 @@ LInstruction* LChunkBuilder::DoInvokeFunction(HInvokeFunction* instr) { ...@@ -1106,6 +1129,9 @@ LInstruction* LChunkBuilder::DoInvokeFunction(HInvokeFunction* instr) {
LOperand* context = UseFixed(instr->context(), esi); LOperand* context = UseFixed(instr->context(), esi);
LOperand* function = UseFixed(instr->function(), edi); LOperand* function = UseFixed(instr->function(), edi);
LInvokeFunction* result = new(zone()) LInvokeFunction(context, function); LInvokeFunction* result = new(zone()) LInvokeFunction(context, function);
if (instr->syntactic_tail_call_mode() == TailCallMode::kAllow) {
result->MarkAsSyntacticTailCall();
}
return MarkAsCall(DefineFixed(result, eax), instr, CANNOT_DEOPTIMIZE_EAGERLY); return MarkAsCall(DefineFixed(result, eax), instr, CANNOT_DEOPTIMIZE_EAGERLY);
} }
......
...@@ -225,6 +225,13 @@ class LInstruction : public ZoneObject { ...@@ -225,6 +225,13 @@ class LInstruction : public ZoneObject {
void MarkAsCall() { bit_field_ = IsCallBits::update(bit_field_, true); } void MarkAsCall() { bit_field_ = IsCallBits::update(bit_field_, true); }
bool IsCall() const { return IsCallBits::decode(bit_field_); } bool IsCall() const { return IsCallBits::decode(bit_field_); }
void MarkAsSyntacticTailCall() {
bit_field_ = IsSyntacticTailCallBits::update(bit_field_, true);
}
bool IsSyntacticTailCall() const {
return IsSyntacticTailCallBits::decode(bit_field_);
}
// Interface to the register allocator and iterators. // Interface to the register allocator and iterators.
bool ClobbersTemps() const { return IsCall(); } bool ClobbersTemps() const { return IsCall(); }
bool ClobbersRegisters() const { return IsCall(); } bool ClobbersRegisters() const { return IsCall(); }
...@@ -259,6 +266,8 @@ class LInstruction : public ZoneObject { ...@@ -259,6 +266,8 @@ class LInstruction : public ZoneObject {
virtual LOperand* TempAt(int i) = 0; virtual LOperand* TempAt(int i) = 0;
class IsCallBits: public BitField<bool, 0, 1> {}; class IsCallBits: public BitField<bool, 0, 1> {};
class IsSyntacticTailCallBits : public BitField<bool, IsCallBits::kNext, 1> {
};
LEnvironment* environment_; LEnvironment* environment_;
SetOncePointer<LPointerMap> pointer_map_; SetOncePointer<LPointerMap> pointer_map_;
......
...@@ -508,6 +508,14 @@ void LChunkBuilderBase::Retry(BailoutReason reason) { ...@@ -508,6 +508,14 @@ void LChunkBuilderBase::Retry(BailoutReason reason) {
status_ = ABORTED; status_ = ABORTED;
} }
LInstruction* LChunkBuilderBase::AssignEnvironment(LInstruction* instr,
HEnvironment* hydrogen_env) {
int argument_index_accumulator = 0;
ZoneList<HValue*> objects_to_materialize(0, zone());
instr->set_environment(CreateEnvironment(
hydrogen_env, &argument_index_accumulator, &objects_to_materialize));
return instr;
}
LEnvironment* LChunkBuilderBase::CreateEnvironment( LEnvironment* LChunkBuilderBase::CreateEnvironment(
HEnvironment* hydrogen_env, int* argument_index_accumulator, HEnvironment* hydrogen_env, int* argument_index_accumulator,
......
...@@ -745,6 +745,11 @@ class LChunkBuilderBase BASE_EMBEDDED { ...@@ -745,6 +745,11 @@ class LChunkBuilderBase BASE_EMBEDDED {
// Will not be moved to a register even if one is freely available. // Will not be moved to a register even if one is freely available.
virtual MUST_USE_RESULT LOperand* UseAny(HValue* value) = 0; virtual MUST_USE_RESULT LOperand* UseAny(HValue* value) = 0;
// Assigns given environment to an instruction. An instruction which can
// deoptimize must have an environment.
LInstruction* AssignEnvironment(LInstruction* instr,
HEnvironment* hydrogen_env);
LEnvironment* CreateEnvironment(HEnvironment* hydrogen_env, LEnvironment* CreateEnvironment(HEnvironment* hydrogen_env,
int* argument_index_accumulator, int* argument_index_accumulator,
ZoneList<HValue*>* objects_to_materialize); ZoneList<HValue*>* objects_to_materialize);
......
...@@ -553,12 +553,7 @@ LInstruction* LChunkBuilder::DefineFixedDouble( ...@@ -553,12 +553,7 @@ LInstruction* LChunkBuilder::DefineFixedDouble(
LInstruction* LChunkBuilder::AssignEnvironment(LInstruction* instr) { LInstruction* LChunkBuilder::AssignEnvironment(LInstruction* instr) {
HEnvironment* hydrogen_env = current_block_->last_environment(); HEnvironment* hydrogen_env = current_block_->last_environment();
int argument_index_accumulator = 0; return LChunkBuilderBase::AssignEnvironment(instr, hydrogen_env);
ZoneList<HValue*> objects_to_materialize(0, zone());
instr->set_environment(CreateEnvironment(hydrogen_env,
&argument_index_accumulator,
&objects_to_materialize));
return instr;
} }
...@@ -887,15 +882,39 @@ void LChunkBuilder::AddInstruction(LInstruction* instr, ...@@ -887,15 +882,39 @@ void LChunkBuilder::AddInstruction(LInstruction* instr,
chunk_->AddInstruction(instr, current_block_); chunk_->AddInstruction(instr, current_block_);
if (instr->IsCall()) { if (instr->IsCall()) {
HEnvironment* hydrogen_env = current_block_->last_environment();
HValue* hydrogen_value_for_lazy_bailout = hydrogen_val; HValue* hydrogen_value_for_lazy_bailout = hydrogen_val;
if (hydrogen_val->HasObservableSideEffects()) { DCHECK_NOT_NULL(hydrogen_env);
HSimulate* sim = HSimulate::cast(hydrogen_val->next()); if (instr->IsSyntacticTailCall()) {
sim->ReplayEnvironment(current_block_->last_environment()); // If it was a syntactic tail call we need to drop the current frame and
hydrogen_value_for_lazy_bailout = sim; // an arguments adaptor frame on top of it (if the latter is present).
hydrogen_env = hydrogen_env->outer();
if (hydrogen_env != nullptr &&
hydrogen_env->frame_type() == ARGUMENTS_ADAPTOR) {
hydrogen_env = hydrogen_env->outer();
}
if (hydrogen_env != nullptr) {
// Push return value on top of outer environment.
hydrogen_env = hydrogen_env->Copy();
hydrogen_env->Push(hydrogen_val);
}
} else {
if (hydrogen_val->HasObservableSideEffects()) {
HSimulate* sim = HSimulate::cast(hydrogen_val->next());
sim->ReplayEnvironment(hydrogen_env);
hydrogen_value_for_lazy_bailout = sim;
}
}
if (hydrogen_env != nullptr) {
// The |hydrogen_env| can be null at this point only if we are generating
// a syntactic tail call from the outermost function but in this case
// it would be a real tail call which will pop function's frame and
// therefore this lazy bailout can be skipped.
LInstruction* bailout = LChunkBuilderBase::AssignEnvironment(
new (zone()) LLazyBailout(), hydrogen_env);
bailout->set_hydrogen_value(hydrogen_value_for_lazy_bailout);
chunk_->AddInstruction(bailout, current_block_);
} }
LInstruction* bailout = AssignEnvironment(new(zone()) LLazyBailout());
bailout->set_hydrogen_value(hydrogen_value_for_lazy_bailout);
chunk_->AddInstruction(bailout, current_block_);
} }
} }
...@@ -1070,6 +1089,9 @@ LInstruction* LChunkBuilder::DoCallWithDescriptor( ...@@ -1070,6 +1089,9 @@ LInstruction* LChunkBuilder::DoCallWithDescriptor(
LCallWithDescriptor* result = new(zone()) LCallWithDescriptor( LCallWithDescriptor* result = new(zone()) LCallWithDescriptor(
descriptor, ops, zone()); descriptor, ops, zone());
if (instr->syntactic_tail_call_mode() == TailCallMode::kAllow) {
result->MarkAsSyntacticTailCall();
}
return MarkAsCall(DefineFixed(result, v0), instr); return MarkAsCall(DefineFixed(result, v0), instr);
} }
...@@ -1078,6 +1100,9 @@ LInstruction* LChunkBuilder::DoInvokeFunction(HInvokeFunction* instr) { ...@@ -1078,6 +1100,9 @@ LInstruction* LChunkBuilder::DoInvokeFunction(HInvokeFunction* instr) {
LOperand* context = UseFixed(instr->context(), cp); LOperand* context = UseFixed(instr->context(), cp);
LOperand* function = UseFixed(instr->function(), a1); LOperand* function = UseFixed(instr->function(), a1);
LInvokeFunction* result = new(zone()) LInvokeFunction(context, function); LInvokeFunction* result = new(zone()) LInvokeFunction(context, function);
if (instr->syntactic_tail_call_mode() == TailCallMode::kAllow) {
result->MarkAsSyntacticTailCall();
}
return MarkAsCall(DefineFixed(result, v0), instr, CANNOT_DEOPTIMIZE_EAGERLY); return MarkAsCall(DefineFixed(result, v0), instr, CANNOT_DEOPTIMIZE_EAGERLY);
} }
......
...@@ -221,6 +221,13 @@ class LInstruction : public ZoneObject { ...@@ -221,6 +221,13 @@ class LInstruction : public ZoneObject {
void MarkAsCall() { bit_field_ = IsCallBits::update(bit_field_, true); } void MarkAsCall() { bit_field_ = IsCallBits::update(bit_field_, true); }
bool IsCall() const { return IsCallBits::decode(bit_field_); } bool IsCall() const { return IsCallBits::decode(bit_field_); }
void MarkAsSyntacticTailCall() {
bit_field_ = IsSyntacticTailCallBits::update(bit_field_, true);
}
bool IsSyntacticTailCall() const {
return IsSyntacticTailCallBits::decode(bit_field_);
}
// Interface to the register allocator and iterators. // Interface to the register allocator and iterators.
bool ClobbersTemps() const { return IsCall(); } bool ClobbersTemps() const { return IsCall(); }
bool ClobbersRegisters() const { return IsCall(); } bool ClobbersRegisters() const { return IsCall(); }
...@@ -255,6 +262,8 @@ class LInstruction : public ZoneObject { ...@@ -255,6 +262,8 @@ class LInstruction : public ZoneObject {
virtual LOperand* TempAt(int i) = 0; virtual LOperand* TempAt(int i) = 0;
class IsCallBits: public BitField<bool, 0, 1> {}; class IsCallBits: public BitField<bool, 0, 1> {};
class IsSyntacticTailCallBits : public BitField<bool, IsCallBits::kNext, 1> {
};
LEnvironment* environment_; LEnvironment* environment_;
SetOncePointer<LPointerMap> pointer_map_; SetOncePointer<LPointerMap> pointer_map_;
......
...@@ -553,12 +553,7 @@ LInstruction* LChunkBuilder::DefineFixedDouble( ...@@ -553,12 +553,7 @@ LInstruction* LChunkBuilder::DefineFixedDouble(
LInstruction* LChunkBuilder::AssignEnvironment(LInstruction* instr) { LInstruction* LChunkBuilder::AssignEnvironment(LInstruction* instr) {
HEnvironment* hydrogen_env = current_block_->last_environment(); HEnvironment* hydrogen_env = current_block_->last_environment();
int argument_index_accumulator = 0; return LChunkBuilderBase::AssignEnvironment(instr, hydrogen_env);
ZoneList<HValue*> objects_to_materialize(0, zone());
instr->set_environment(CreateEnvironment(hydrogen_env,
&argument_index_accumulator,
&objects_to_materialize));
return instr;
} }
...@@ -887,15 +882,39 @@ void LChunkBuilder::AddInstruction(LInstruction* instr, ...@@ -887,15 +882,39 @@ void LChunkBuilder::AddInstruction(LInstruction* instr,
chunk_->AddInstruction(instr, current_block_); chunk_->AddInstruction(instr, current_block_);
if (instr->IsCall()) { if (instr->IsCall()) {
HEnvironment* hydrogen_env = current_block_->last_environment();
HValue* hydrogen_value_for_lazy_bailout = hydrogen_val; HValue* hydrogen_value_for_lazy_bailout = hydrogen_val;
if (hydrogen_val->HasObservableSideEffects()) { DCHECK_NOT_NULL(hydrogen_env);
HSimulate* sim = HSimulate::cast(hydrogen_val->next()); if (instr->IsSyntacticTailCall()) {
sim->ReplayEnvironment(current_block_->last_environment()); // If it was a syntactic tail call we need to drop the current frame and
hydrogen_value_for_lazy_bailout = sim; // an arguments adaptor frame on top of it (if the latter is present).
hydrogen_env = hydrogen_env->outer();
if (hydrogen_env != nullptr &&
hydrogen_env->frame_type() == ARGUMENTS_ADAPTOR) {
hydrogen_env = hydrogen_env->outer();
}
if (hydrogen_env != nullptr) {
// Push return value on top of outer environment.
hydrogen_env = hydrogen_env->Copy();
hydrogen_env->Push(hydrogen_val);
}
} else {
if (hydrogen_val->HasObservableSideEffects()) {
HSimulate* sim = HSimulate::cast(hydrogen_val->next());
sim->ReplayEnvironment(hydrogen_env);
hydrogen_value_for_lazy_bailout = sim;
}
}
if (hydrogen_env != nullptr) {
// The |hydrogen_env| can be null at this point only if we are generating
// a syntactic tail call from the outermost function but in this case
// it would be a real tail call which will pop function's frame and
// therefore this lazy bailout can be skipped.
LInstruction* bailout = LChunkBuilderBase::AssignEnvironment(
new (zone()) LLazyBailout(), hydrogen_env);
bailout->set_hydrogen_value(hydrogen_value_for_lazy_bailout);
chunk_->AddInstruction(bailout, current_block_);
} }
LInstruction* bailout = AssignEnvironment(new(zone()) LLazyBailout());
bailout->set_hydrogen_value(hydrogen_value_for_lazy_bailout);
chunk_->AddInstruction(bailout, current_block_);
} }
} }
...@@ -1070,6 +1089,9 @@ LInstruction* LChunkBuilder::DoCallWithDescriptor( ...@@ -1070,6 +1089,9 @@ LInstruction* LChunkBuilder::DoCallWithDescriptor(
LCallWithDescriptor* result = new(zone()) LCallWithDescriptor( LCallWithDescriptor* result = new(zone()) LCallWithDescriptor(
descriptor, ops, zone()); descriptor, ops, zone());
if (instr->syntactic_tail_call_mode() == TailCallMode::kAllow) {
result->MarkAsSyntacticTailCall();
}
return MarkAsCall(DefineFixed(result, v0), instr); return MarkAsCall(DefineFixed(result, v0), instr);
} }
...@@ -1078,6 +1100,9 @@ LInstruction* LChunkBuilder::DoInvokeFunction(HInvokeFunction* instr) { ...@@ -1078,6 +1100,9 @@ LInstruction* LChunkBuilder::DoInvokeFunction(HInvokeFunction* instr) {
LOperand* context = UseFixed(instr->context(), cp); LOperand* context = UseFixed(instr->context(), cp);
LOperand* function = UseFixed(instr->function(), a1); LOperand* function = UseFixed(instr->function(), a1);
LInvokeFunction* result = new(zone()) LInvokeFunction(context, function); LInvokeFunction* result = new(zone()) LInvokeFunction(context, function);
if (instr->syntactic_tail_call_mode() == TailCallMode::kAllow) {
result->MarkAsSyntacticTailCall();
}
return MarkAsCall(DefineFixed(result, v0), instr, CANNOT_DEOPTIMIZE_EAGERLY); return MarkAsCall(DefineFixed(result, v0), instr, CANNOT_DEOPTIMIZE_EAGERLY);
} }
......
...@@ -224,6 +224,13 @@ class LInstruction : public ZoneObject { ...@@ -224,6 +224,13 @@ class LInstruction : public ZoneObject {
void MarkAsCall() { bit_field_ = IsCallBits::update(bit_field_, true); } void MarkAsCall() { bit_field_ = IsCallBits::update(bit_field_, true); }
bool IsCall() const { return IsCallBits::decode(bit_field_); } bool IsCall() const { return IsCallBits::decode(bit_field_); }
void MarkAsSyntacticTailCall() {
bit_field_ = IsSyntacticTailCallBits::update(bit_field_, true);
}
bool IsSyntacticTailCall() const {
return IsSyntacticTailCallBits::decode(bit_field_);
}
// Interface to the register allocator and iterators. // Interface to the register allocator and iterators.
bool ClobbersTemps() const { return IsCall(); } bool ClobbersTemps() const { return IsCall(); }
bool ClobbersRegisters() const { return IsCall(); } bool ClobbersRegisters() const { return IsCall(); }
...@@ -258,6 +265,8 @@ class LInstruction : public ZoneObject { ...@@ -258,6 +265,8 @@ class LInstruction : public ZoneObject {
virtual LOperand* TempAt(int i) = 0; virtual LOperand* TempAt(int i) = 0;
class IsCallBits: public BitField<bool, 0, 1> {}; class IsCallBits: public BitField<bool, 0, 1> {};
class IsSyntacticTailCallBits : public BitField<bool, IsCallBits::kNext, 1> {
};
LEnvironment* environment_; LEnvironment* environment_;
SetOncePointer<LPointerMap> pointer_map_; SetOncePointer<LPointerMap> pointer_map_;
......
...@@ -581,11 +581,7 @@ LInstruction* LChunkBuilder::DefineFixedDouble( ...@@ -581,11 +581,7 @@ LInstruction* LChunkBuilder::DefineFixedDouble(
LInstruction* LChunkBuilder::AssignEnvironment(LInstruction* instr) { LInstruction* LChunkBuilder::AssignEnvironment(LInstruction* instr) {
HEnvironment* hydrogen_env = current_block_->last_environment(); HEnvironment* hydrogen_env = current_block_->last_environment();
int argument_index_accumulator = 0; return LChunkBuilderBase::AssignEnvironment(instr, hydrogen_env);
ZoneList<HValue*> objects_to_materialize(0, zone());
instr->set_environment(CreateEnvironment(
hydrogen_env, &argument_index_accumulator, &objects_to_materialize));
return instr;
} }
...@@ -901,15 +897,39 @@ void LChunkBuilder::AddInstruction(LInstruction* instr, ...@@ -901,15 +897,39 @@ void LChunkBuilder::AddInstruction(LInstruction* instr,
chunk_->AddInstruction(instr, current_block_); chunk_->AddInstruction(instr, current_block_);
if (instr->IsCall()) { if (instr->IsCall()) {
HEnvironment* hydrogen_env = current_block_->last_environment();
HValue* hydrogen_value_for_lazy_bailout = hydrogen_val; HValue* hydrogen_value_for_lazy_bailout = hydrogen_val;
if (hydrogen_val->HasObservableSideEffects()) { DCHECK_NOT_NULL(hydrogen_env);
HSimulate* sim = HSimulate::cast(hydrogen_val->next()); if (instr->IsSyntacticTailCall()) {
sim->ReplayEnvironment(current_block_->last_environment()); // If it was a syntactic tail call we need to drop the current frame and
hydrogen_value_for_lazy_bailout = sim; // an arguments adaptor frame on top of it (if the latter is present).
hydrogen_env = hydrogen_env->outer();
if (hydrogen_env != nullptr &&
hydrogen_env->frame_type() == ARGUMENTS_ADAPTOR) {
hydrogen_env = hydrogen_env->outer();
}
if (hydrogen_env != nullptr) {
// Push return value on top of outer environment.
hydrogen_env = hydrogen_env->Copy();
hydrogen_env->Push(hydrogen_val);
}
} else {
if (hydrogen_val->HasObservableSideEffects()) {
HSimulate* sim = HSimulate::cast(hydrogen_val->next());
sim->ReplayEnvironment(hydrogen_env);
hydrogen_value_for_lazy_bailout = sim;
}
}
if (hydrogen_env != nullptr) {
// The |hydrogen_env| can be null at this point only if we are generating
// a syntactic tail call from the outermost function but in this case
// it would be a real tail call which will pop function's frame and
// therefore this lazy bailout can be skipped.
LInstruction* bailout = LChunkBuilderBase::AssignEnvironment(
new (zone()) LLazyBailout(), hydrogen_env);
bailout->set_hydrogen_value(hydrogen_value_for_lazy_bailout);
chunk_->AddInstruction(bailout, current_block_);
} }
LInstruction* bailout = AssignEnvironment(new(zone()) LLazyBailout());
bailout->set_hydrogen_value(hydrogen_value_for_lazy_bailout);
chunk_->AddInstruction(bailout, current_block_);
} }
} }
...@@ -1087,6 +1107,9 @@ LInstruction* LChunkBuilder::DoCallWithDescriptor( ...@@ -1087,6 +1107,9 @@ LInstruction* LChunkBuilder::DoCallWithDescriptor(
LCallWithDescriptor* result = new(zone()) LCallWithDescriptor( LCallWithDescriptor* result = new(zone()) LCallWithDescriptor(
descriptor, ops, zone()); descriptor, ops, zone());
if (instr->syntactic_tail_call_mode() == TailCallMode::kAllow) {
result->MarkAsSyntacticTailCall();
}
return MarkAsCall(DefineFixed(result, rax), instr); return MarkAsCall(DefineFixed(result, rax), instr);
} }
...@@ -1095,6 +1118,9 @@ LInstruction* LChunkBuilder::DoInvokeFunction(HInvokeFunction* instr) { ...@@ -1095,6 +1118,9 @@ LInstruction* LChunkBuilder::DoInvokeFunction(HInvokeFunction* instr) {
LOperand* context = UseFixed(instr->context(), rsi); LOperand* context = UseFixed(instr->context(), rsi);
LOperand* function = UseFixed(instr->function(), rdi); LOperand* function = UseFixed(instr->function(), rdi);
LInvokeFunction* result = new(zone()) LInvokeFunction(context, function); LInvokeFunction* result = new(zone()) LInvokeFunction(context, function);
if (instr->syntactic_tail_call_mode() == TailCallMode::kAllow) {
result->MarkAsSyntacticTailCall();
}
return MarkAsCall(DefineFixed(result, rax), instr, CANNOT_DEOPTIMIZE_EAGERLY); return MarkAsCall(DefineFixed(result, rax), instr, CANNOT_DEOPTIMIZE_EAGERLY);
} }
......
...@@ -221,6 +221,13 @@ class LInstruction : public ZoneObject { ...@@ -221,6 +221,13 @@ class LInstruction : public ZoneObject {
void MarkAsCall() { bit_field_ = IsCallBits::update(bit_field_, true); } void MarkAsCall() { bit_field_ = IsCallBits::update(bit_field_, true); }
bool IsCall() const { return IsCallBits::decode(bit_field_); } bool IsCall() const { return IsCallBits::decode(bit_field_); }
void MarkAsSyntacticTailCall() {
bit_field_ = IsSyntacticTailCallBits::update(bit_field_, true);
}
bool IsSyntacticTailCall() const {
return IsSyntacticTailCallBits::decode(bit_field_);
}
// Interface to the register allocator and iterators. // Interface to the register allocator and iterators.
bool ClobbersTemps() const { return IsCall(); } bool ClobbersTemps() const { return IsCall(); }
bool ClobbersRegisters() const { return IsCall(); } bool ClobbersRegisters() const { return IsCall(); }
...@@ -259,6 +266,8 @@ class LInstruction : public ZoneObject { ...@@ -259,6 +266,8 @@ class LInstruction : public ZoneObject {
virtual LOperand* TempAt(int i) = 0; virtual LOperand* TempAt(int i) = 0;
class IsCallBits: public BitField<bool, 0, 1> {}; class IsCallBits: public BitField<bool, 0, 1> {};
class IsSyntacticTailCallBits : public BitField<bool, IsCallBits::kNext, 1> {
};
LEnvironment* environment_; LEnvironment* environment_;
SetOncePointer<LPointerMap> pointer_map_; SetOncePointer<LPointerMap> pointer_map_;
......
...@@ -3,8 +3,6 @@ ...@@ -3,8 +3,6 @@
// found in the LICENSE file. // found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-tailcalls // Flags: --allow-natives-syntax --harmony-tailcalls
// TODO(v8:4698), TODO(ishell): support these cases.
// Flags: --turbo --nostress-opt
try { try {
load("mjsunit/es6/tail-call-megatest.js"); load("mjsunit/es6/tail-call-megatest.js");
......
...@@ -3,8 +3,6 @@ ...@@ -3,8 +3,6 @@
// found in the LICENSE file. // found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-tailcalls // Flags: --allow-natives-syntax --harmony-tailcalls
// TODO(v8:4698), TODO(ishell): support these cases.
// Flags: --turbo --nostress-opt
try { try {
load("mjsunit/es6/tail-call-megatest.js"); load("mjsunit/es6/tail-call-megatest.js");
......
...@@ -3,8 +3,6 @@ ...@@ -3,8 +3,6 @@
// found in the LICENSE file. // found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-tailcalls // Flags: --allow-natives-syntax --harmony-tailcalls
// TODO(v8:4698), TODO(ishell): support these cases.
// Flags: --turbo --nostress-opt
try { try {
load("mjsunit/es6/tail-call-megatest.js"); load("mjsunit/es6/tail-call-megatest.js");
......
...@@ -3,8 +3,6 @@ ...@@ -3,8 +3,6 @@
// found in the LICENSE file. // found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-tailcalls // Flags: --allow-natives-syntax --harmony-tailcalls
// TODO(v8:4698), TODO(ishell): support these cases.
// Flags: --turbo --nostress-opt
try { try {
load("mjsunit/es6/tail-call-megatest.js"); load("mjsunit/es6/tail-call-megatest.js");
......
...@@ -3,8 +3,6 @@ ...@@ -3,8 +3,6 @@
// found in the LICENSE file. // found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-tailcalls // Flags: --allow-natives-syntax --harmony-tailcalls
// TODO(v8:4698), TODO(ishell): support these cases.
// Flags: --turbo --nostress-opt
try { try {
load("mjsunit/es6/tail-call-megatest.js"); load("mjsunit/es6/tail-call-megatest.js");
......
...@@ -3,8 +3,6 @@ ...@@ -3,8 +3,6 @@
// found in the LICENSE file. // found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-tailcalls // Flags: --allow-natives-syntax --harmony-tailcalls
// TODO(v8:4698), TODO(ishell): support these cases.
// Flags: --turbo --nostress-opt
try { try {
load("mjsunit/es6/tail-call-megatest.js"); load("mjsunit/es6/tail-call-megatest.js");
......
...@@ -3,8 +3,6 @@ ...@@ -3,8 +3,6 @@
// found in the LICENSE file. // found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-tailcalls // Flags: --allow-natives-syntax --harmony-tailcalls
// TODO(v8:4698), TODO(ishell): support these cases.
// Flags: --turbo --nostress-opt
try { try {
load("mjsunit/es6/tail-call-megatest.js"); load("mjsunit/es6/tail-call-megatest.js");
......
...@@ -3,8 +3,6 @@ ...@@ -3,8 +3,6 @@
// found in the LICENSE file. // found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-tailcalls // Flags: --allow-natives-syntax --harmony-tailcalls
// TODO(v8:4698), TODO(ishell): support these cases.
// Flags: --turbo --nostress-opt
try { try {
load("mjsunit/es6/tail-call-megatest.js"); load("mjsunit/es6/tail-call-megatest.js");
......
...@@ -3,8 +3,6 @@ ...@@ -3,8 +3,6 @@
// found in the LICENSE file. // found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-tailcalls // Flags: --allow-natives-syntax --harmony-tailcalls
// TODO(v8:4698), TODO(ishell): support these cases.
// Flags: --turbo --nostress-opt
try { try {
load("mjsunit/es6/tail-call-megatest.js"); load("mjsunit/es6/tail-call-megatest.js");
......
...@@ -3,8 +3,6 @@ ...@@ -3,8 +3,6 @@
// found in the LICENSE file. // found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-tailcalls // Flags: --allow-natives-syntax --harmony-tailcalls
// TODO(v8:4698), TODO(ishell): support these cases.
// Flags: --turbo --nostress-opt
try { try {
load("mjsunit/es6/tail-call-megatest.js"); load("mjsunit/es6/tail-call-megatest.js");
......
...@@ -3,8 +3,6 @@ ...@@ -3,8 +3,6 @@
// found in the LICENSE file. // found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-tailcalls // Flags: --allow-natives-syntax --harmony-tailcalls
// TODO(v8:4698), TODO(ishell): support these cases.
// Flags: --turbo --nostress-opt
Error.prepareStackTrace = (error,stack) => { Error.prepareStackTrace = (error,stack) => {
...@@ -59,6 +57,19 @@ function run_tests(shard) { ...@@ -59,6 +57,19 @@ function run_tests(shard) {
} }
var check_arguments = check_arguments_template("expected_args"); var check_arguments = check_arguments_template("expected_args");
function deopt_template(deopt_mode) {
switch(deopt_mode) {
case "none":
return " // Don't deoptimize";
case "f":
case "g":
case "test":
return ` %DeoptimizeFunction(${deopt_mode});`;
default:
assertUnreachable();
}
}
var f_cfg_sloppy = { var f_cfg_sloppy = {
func_name: 'f', func_name: 'f',
source_template: function(cfg) { source_template: function(cfg) {
...@@ -66,7 +77,7 @@ function run_tests(shard) { ...@@ -66,7 +77,7 @@ function run_tests(shard) {
: "global"; : "global";
var do_checks = [ var do_checks = [
` assertEquals_(${receiver}, this);`, ` assertEquals_(${receiver}, this);`,
` assertEquals_(undefined, new.target);`, ` ${!cfg.check_new_target ? "// " : ""}assertEquals_(undefined, new.target);`,
check_arguments, check_arguments,
` checkStackTrace_([f, test]);`, ` checkStackTrace_([f, test]);`,
].join("\n"); ].join("\n");
...@@ -74,9 +85,10 @@ function run_tests(shard) { ...@@ -74,9 +85,10 @@ function run_tests(shard) {
var lines = [ var lines = [
`function f(a) {`, `function f(a) {`,
` ${inlinable_comment(cfg.f_inlinable)}`, ` ${inlinable_comment(cfg.f_inlinable)}`,
` counter++;`,
` var expected_args = [${cfg.f_args}];`, ` var expected_args = [${cfg.f_args}];`,
do_checks, do_checks,
` %DeoptimizeNow();`, deopt_template(cfg.deopt_mode),
do_checks, do_checks,
` return 42;`, ` return 42;`,
`}`, `}`,
...@@ -92,7 +104,7 @@ function run_tests(shard) { ...@@ -92,7 +104,7 @@ function run_tests(shard) {
: "undefined"; : "undefined";
var do_checks = [ var do_checks = [
` assertEquals_(${receiver}, this);`, ` assertEquals_(${receiver}, this);`,
` assertEquals_(undefined, new.target);`, ` ${!cfg.check_new_target ? "// " : ""}assertEquals_(undefined, new.target);`,
check_arguments, check_arguments,
` checkStackTrace_([f, test]);`, ` checkStackTrace_([f, test]);`,
].join("\n"); ].join("\n");
...@@ -101,9 +113,10 @@ function run_tests(shard) { ...@@ -101,9 +113,10 @@ function run_tests(shard) {
`function f(a) {`, `function f(a) {`,
` "use strict";`, ` "use strict";`,
` ${inlinable_comment(cfg.f_inlinable)}`, ` ${inlinable_comment(cfg.f_inlinable)}`,
` counter++;`,
` var expected_args = [${cfg.f_args}];`, ` var expected_args = [${cfg.f_args}];`,
do_checks, do_checks,
` %DeoptimizeNow();`, deopt_template(cfg.deopt_mode),
do_checks, do_checks,
` return 42;`, ` return 42;`,
`}`, `}`,
...@@ -119,7 +132,7 @@ function run_tests(shard) { ...@@ -119,7 +132,7 @@ function run_tests(shard) {
: "global"; : "global";
var do_checks = [ var do_checks = [
` assertEquals_(${receiver}, this);`, ` assertEquals_(${receiver}, this);`,
` assertEquals_(undefined, new.target);`, ` ${!cfg.check_new_target ? "// " : ""}assertEquals_(undefined, new.target);`,
check_arguments, check_arguments,
` checkStackTrace_([f, test]);`, ` checkStackTrace_([f, test]);`,
].join("\n"); ].join("\n");
...@@ -127,9 +140,10 @@ function run_tests(shard) { ...@@ -127,9 +140,10 @@ function run_tests(shard) {
var lines = [ var lines = [
`function f(a) {`, `function f(a) {`,
` ${inlinable_comment(cfg.f_inlinable)}`, ` ${inlinable_comment(cfg.f_inlinable)}`,
` counter++;`,
` var expected_args = [${cfg.f_args}];`, ` var expected_args = [${cfg.f_args}];`,
do_checks, do_checks,
` %DeoptimizeNow();`, deopt_template(cfg.deopt_mode),
do_checks, do_checks,
` return 42;`, ` return 42;`,
`}`, `}`,
...@@ -144,7 +158,7 @@ function run_tests(shard) { ...@@ -144,7 +158,7 @@ function run_tests(shard) {
source_template: function(cfg) { source_template: function(cfg) {
var do_checks = [ var do_checks = [
` assertEquals_(receiver, this);`, ` assertEquals_(receiver, this);`,
` assertEquals_(undefined, new.target);`, ` ${!cfg.check_new_target ? "// " : ""}assertEquals_(undefined, new.target);`,
check_arguments, check_arguments,
` checkStackTrace_([f, test]);`, ` checkStackTrace_([f, test]);`,
].join("\n"); ].join("\n");
...@@ -153,9 +167,10 @@ function run_tests(shard) { ...@@ -153,9 +167,10 @@ function run_tests(shard) {
`function f(a) {`, `function f(a) {`,
` "use strict";`, ` "use strict";`,
` ${inlinable_comment(cfg.f_inlinable)}`, ` ${inlinable_comment(cfg.f_inlinable)}`,
` counter++;`,
` var expected_args = [${cfg.f_args}];`, ` var expected_args = [${cfg.f_args}];`,
do_checks, do_checks,
` %DeoptimizeNow();`, deopt_template(cfg.deopt_mode),
do_checks, do_checks,
` return 42;`, ` return 42;`,
`}`, `}`,
...@@ -173,7 +188,7 @@ function run_tests(shard) { ...@@ -173,7 +188,7 @@ function run_tests(shard) {
: "global"; : "global";
var do_checks = [ var do_checks = [
` assertEquals_(${receiver}, this);`, ` assertEquals_(${receiver}, this);`,
` assertEquals_(undefined, new.target);`, ` ${!cfg.check_new_target ? "// " : ""}assertEquals_(undefined, new.target);`,
check_arguments, check_arguments,
` checkStackTrace_([f, test]);`, ` checkStackTrace_([f, test]);`,
].join("\n"); ].join("\n");
...@@ -181,9 +196,10 @@ function run_tests(shard) { ...@@ -181,9 +196,10 @@ function run_tests(shard) {
var lines = [ var lines = [
`function f(a) {`, `function f(a) {`,
` ${inlinable_comment(cfg.f_inlinable)}`, ` ${inlinable_comment(cfg.f_inlinable)}`,
` counter++;`,
` var expected_args = [${cfg.f_args}];`, ` var expected_args = [${cfg.f_args}];`,
do_checks, do_checks,
` %DeoptimizeNow();`, deopt_template(cfg.deopt_mode),
do_checks, do_checks,
` return 42;`, ` return 42;`,
`}`, `}`,
...@@ -210,6 +226,23 @@ function run_tests(shard) { ...@@ -210,6 +226,23 @@ function run_tests(shard) {
}; };
var g_cfg_reflect_apply = {
receiver: "the_receiver",
source_template: function(cfg) {
var lines = [
`function g(a) {`,
` "use strict";`,
` ${inlinable_comment(cfg.g_inlinable)}`,
` var expected_args = [${cfg.g_args}];`,
check_arguments,
` return Reflect.apply(${cfg.f_name}, the_receiver, [${cfg.f_args}]);`,
`}`,
];
return lines.join("\n");
},
};
var g_cfg_function_apply = { var g_cfg_function_apply = {
receiver: "the_receiver", receiver: "the_receiver",
source_template: function(cfg) { source_template: function(cfg) {
...@@ -282,6 +315,7 @@ function run_tests(shard) { ...@@ -282,6 +315,7 @@ function run_tests(shard) {
` var undefined = void 0;`, ` var undefined = void 0;`,
` var global = Function('return this')();`, ` var global = Function('return this')();`,
` var the_receiver = {receiver: 1};`, ` var the_receiver = {receiver: 1};`,
` var counter = 0;`,
``, ``,
` // Don't inline helper functions`, ` // Don't inline helper functions`,
` %NeverOptimizeFunction(assertEquals);`, ` %NeverOptimizeFunction(assertEquals);`,
...@@ -293,13 +327,12 @@ function run_tests(shard) { ...@@ -293,13 +327,12 @@ function run_tests(shard) {
` "use strict";`, ` "use strict";`,
` assertEquals_(42, g(${cfg.g_args}));`, ` assertEquals_(42, g(${cfg.g_args}));`,
` }`, ` }`,
` ${cfg.f_inlinable ? "%SetForceInlineFlag(f)" : ""};`,
` ${cfg.g_inlinable ? "%SetForceInlineFlag(g)" : ""};`,
` ${"test();".repeat(cfg.test_warmup_count)}`, ` ${"test();".repeat(cfg.test_warmup_count)}`,
` ${cfg.f_inlinable ? "%SetForceInlineFlag(f)" : "%OptimizeFunctionOnNextCall(f)"};`,
` ${cfg.g_inlinable ? "%SetForceInlineFlag(g)" : "%OptimizeFunctionOnNextCall(g)"};`,
` %OptimizeFunctionOnNextCall(test);`, ` %OptimizeFunctionOnNextCall(test);`,
` %OptimizeFunctionOnNextCall(f);`,
` %OptimizeFunctionOnNextCall(g);`,
` test();`, ` test();`,
` assertEquals(${1 + cfg.test_warmup_count}, counter);`,
`})();`, `})();`,
``, ``,
]; ];
...@@ -311,6 +344,9 @@ function run_tests(shard) { ...@@ -311,6 +344,9 @@ function run_tests(shard) {
var g_args_variants = ["", "10", "10, 20"]; var g_args_variants = ["", "10", "10, 20"];
var f_inlinable_variants = [true, false]; var f_inlinable_variants = [true, false];
var g_inlinable_variants = [true, false]; var g_inlinable_variants = [true, false];
// This is to avoid bailing out because of referencing new.target.
var check_new_target_variants = [true, false];
var deopt_mode_variants = ["none", "f", "g", "test"];
var f_variants = [ var f_variants = [
f_cfg_sloppy, f_cfg_sloppy,
f_cfg_strict, f_cfg_strict,
...@@ -320,42 +356,51 @@ function run_tests(shard) { ...@@ -320,42 +356,51 @@ function run_tests(shard) {
]; ];
var g_variants = [ var g_variants = [
g_cfg_normal, g_cfg_normal,
g_cfg_function_call, g_cfg_reflect_apply,
g_cfg_function_apply, g_cfg_function_apply,
g_cfg_function_apply_arguments_object, g_cfg_function_apply_arguments_object,
g_cfg_function_call,
]; ];
var test_warmup_counts = [0, 1, 2]; var test_warmup_counts = [0, 1, 2];
var iter = 0; var iter = 0;
var tests_executed = 0;
if (shard !== undefined) { if (shard !== undefined) {
print("Running shard #" + shard); print("Running shard #" + shard);
} }
f_variants.forEach((f_cfg) => { f_variants.forEach((f_cfg) => {
g_variants.forEach((g_cfg) => { check_new_target_variants.forEach((check_new_target) => {
f_args_variants.forEach((f_args) => { deopt_mode_variants.forEach((deopt_mode) => {
g_args_variants.forEach((g_args) => { g_variants.forEach((g_cfg) => {
f_inlinable_variants.forEach((f_inlinable) => { f_args_variants.forEach((f_args) => {
g_inlinable_variants.forEach((g_inlinable) => { g_args_variants.forEach((g_args) => {
test_warmup_counts.forEach((test_warmup_count) => { f_inlinable_variants.forEach((f_inlinable) => {
if (shard !== undefined && (iter++) % SHARDS_COUNT != shard) { g_inlinable_variants.forEach((g_inlinable) => {
print("skipping..."); test_warmup_counts.forEach((test_warmup_count) => {
return; if (shard !== undefined && (iter++) % SHARDS_COUNT != shard) {
} print("skipping...");
var cfg = { return;
f_source_template: f_cfg.source_template, }
f_inlinable, tests_executed++;
f_args, var cfg = {
f_name: f_cfg.func_name, f_source_template: f_cfg.source_template,
f_receiver: g_cfg.receiver, f_inlinable,
g_source_template: g_cfg.source_template, f_args,
g_inlinable, f_name: f_cfg.func_name,
g_args, f_receiver: g_cfg.receiver,
test_warmup_count, g_source_template: g_cfg.source_template,
}; g_inlinable,
var source = test_template(cfg); g_args,
print("===================="); test_warmup_count,
print(source); check_new_target,
eval(source); deopt_mode,
};
var source = test_template(cfg);
print("====================");
print(source);
eval(source);
});
});
}); });
}); });
}); });
...@@ -363,6 +408,7 @@ function run_tests(shard) { ...@@ -363,6 +408,7 @@ function run_tests(shard) {
}); });
}); });
}); });
print("Number of tests executed: " + tests_executed);
} }
// Uncomment to run all the tests at once or use shard runners. // Uncomment to run all the tests at once or use shard runners.
......
...@@ -3,8 +3,6 @@ ...@@ -3,8 +3,6 @@
// found in the LICENSE file. // found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-tailcalls --stack-size=100 // Flags: --allow-natives-syntax --harmony-tailcalls --stack-size=100
// TODO(v8:4698), TODO(ishell): support these cases.
// Flags: --nostress-opt
// //
// Tail calls work only in strict mode. // Tail calls work only in strict mode.
......
...@@ -3,8 +3,6 @@ ...@@ -3,8 +3,6 @@
// found in the LICENSE file. // found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-tailcalls // Flags: --allow-natives-syntax --harmony-tailcalls
// TODO(v8:4698), TODO(ishell): support these cases.
// Flags: --max-inlined-source-size=0
"use strict"; "use strict";
Error.prepareStackTrace = (error,stack) => { Error.prepareStackTrace = (error,stack) => {
......
...@@ -43,9 +43,6 @@ ...@@ -43,9 +43,6 @@
# This test non-deterministically runs out of memory on Windows ia32. # This test non-deterministically runs out of memory on Windows ia32.
'regress/regress-crbug-160010': [SKIP], 'regress/regress-crbug-160010': [SKIP],
# Issue 4698: not fully supported by Turbofan yet
'es6/tail-call': [PASS, NO_VARIANTS],
# Issue 3389: deopt_every_n_garbage_collections is unsafe # Issue 3389: deopt_every_n_garbage_collections is unsafe
'regress/regress-2653': [SKIP], 'regress/regress-2653': [SKIP],
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment