[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}
This commit is contained in:
ishell 2016-03-21 01:48:16 -07:00 committed by Commit bot
parent 2a1570ef1a
commit acbb968ded
32 changed files with 500 additions and 202 deletions

View File

@ -546,12 +546,7 @@ LInstruction* LChunkBuilder::DefineFixedDouble(
LInstruction* LChunkBuilder::AssignEnvironment(LInstruction* instr) {
HEnvironment* hydrogen_env = current_block_->last_environment();
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;
return LChunkBuilderBase::AssignEnvironment(instr, hydrogen_env);
}
@ -877,15 +872,39 @@ void LChunkBuilder::AddInstruction(LInstruction* instr,
chunk_->AddInstruction(instr, current_block_);
if (instr->IsCall()) {
HEnvironment* hydrogen_env = current_block_->last_environment();
HValue* hydrogen_value_for_lazy_bailout = hydrogen_val;
if (hydrogen_val->HasObservableSideEffects()) {
HSimulate* sim = HSimulate::cast(hydrogen_val->next());
sim->ReplayEnvironment(current_block_->last_environment());
hydrogen_value_for_lazy_bailout = sim;
DCHECK_NOT_NULL(hydrogen_env);
if (instr->IsSyntacticTailCall()) {
// If it was a syntactic tail call we need to drop the current frame and
// 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(
LCallWithDescriptor* result = new(zone()) LCallWithDescriptor(
descriptor, ops, zone());
if (instr->syntactic_tail_call_mode() == TailCallMode::kAllow) {
result->MarkAsSyntacticTailCall();
}
return MarkAsCall(DefineFixed(result, r0), instr);
}
@ -1073,6 +1095,9 @@ LInstruction* LChunkBuilder::DoInvokeFunction(HInvokeFunction* instr) {
LOperand* context = UseFixed(instr->context(), cp);
LOperand* function = UseFixed(instr->function(), r1);
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);
}

View File

@ -224,6 +224,13 @@ class LInstruction : public ZoneObject {
void MarkAsCall() { bit_field_ = IsCallBits::update(bit_field_, true); }
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.
bool ClobbersTemps() const { return IsCall(); }
bool ClobbersRegisters() const { return IsCall(); }
@ -258,6 +265,8 @@ class LInstruction : public ZoneObject {
virtual LOperand* TempAt(int i) = 0;
class IsCallBits: public BitField<bool, 0, 1> {};
class IsSyntacticTailCallBits : public BitField<bool, IsCallBits::kNext, 1> {
};
LEnvironment* environment_;
SetOncePointer<LPointerMap> pointer_map_;

View File

@ -715,27 +715,46 @@ void LChunkBuilder::AddInstruction(LInstruction* instr,
chunk_->AddInstruction(instr, current_block_);
if (instr->IsCall()) {
HEnvironment* hydrogen_env = current_block_->last_environment();
HValue* hydrogen_value_for_lazy_bailout = hydrogen_val;
if (hydrogen_val->HasObservableSideEffects()) {
HSimulate* sim = HSimulate::cast(hydrogen_val->next());
sim->ReplayEnvironment(current_block_->last_environment());
hydrogen_value_for_lazy_bailout = sim;
DCHECK_NOT_NULL(hydrogen_env);
if (instr->IsSyntacticTailCall()) {
// If it was a syntactic tail call we need to drop the current frame and
// 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) {
HEnvironment* hydrogen_env = current_block_->last_environment();
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;
return LChunkBuilderBase::AssignEnvironment(instr, hydrogen_env);
}
@ -1018,6 +1037,9 @@ LInstruction* LChunkBuilder::DoCallWithDescriptor(
LCallWithDescriptor* result = new(zone()) LCallWithDescriptor(descriptor,
ops,
zone());
if (instr->syntactic_tail_call_mode() == TailCallMode::kAllow) {
result->MarkAsSyntacticTailCall();
}
return MarkAsCall(DefineFixed(result, x0), instr);
}
@ -1510,6 +1532,9 @@ LInstruction* LChunkBuilder::DoInvokeFunction(HInvokeFunction* instr) {
// The function is required (by MacroAssembler::InvokeFunction) to be in x1.
LOperand* function = UseFixed(instr->function(), x1);
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);
}

View File

@ -235,6 +235,13 @@ class LInstruction : public ZoneObject {
void MarkAsCall() { bit_field_ = IsCallBits::update(bit_field_, true); }
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.
bool ClobbersTemps() const { return IsCall(); }
bool ClobbersRegisters() const { return IsCall(); }
@ -262,6 +269,8 @@ class LInstruction : public ZoneObject {
private:
class IsCallBits: public BitField<bool, 0, 1> {};
class IsSyntacticTailCallBits : public BitField<bool, IsCallBits::kNext, 1> {
};
LEnvironment* environment_;
SetOncePointer<LPointerMap> pointer_map_;

View File

@ -911,6 +911,18 @@ std::ostream& HBinaryCall::PrintDataTo(std::ostream& os) const { // NOLINT
<< 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() {
if (skip_check()) return;
@ -1033,7 +1045,11 @@ std::ostream& HCallWithDescriptor::PrintDataTo(
for (int i = 0; i < OperandCount(); i++) {
os << NameOf(OperandAt(i)) << " ";
}
return os << "#" << argument_count();
os << "#" << argument_count();
if (syntactic_tail_call_mode() == TailCallMode::kAllow) {
os << ", JSTailCall";
}
return os;
}

View File

@ -2214,19 +2214,17 @@ class HBinaryCall : public HCall<2> {
};
enum CallMode { NORMAL_CALL, TAIL_CALL };
class HCallWithDescriptor final : public HInstruction {
public:
static HCallWithDescriptor* New(Isolate* isolate, Zone* zone, HValue* context,
HValue* target, int argument_count,
CallInterfaceDescriptor descriptor,
const Vector<HValue*>& operands,
CallMode call_mode = NORMAL_CALL) {
HCallWithDescriptor* res = new (zone) HCallWithDescriptor(
target, argument_count, descriptor, operands, call_mode, zone);
DCHECK_EQ(operands.length(), res->GetParameterCount());
static HCallWithDescriptor* New(
Isolate* isolate, Zone* zone, HValue* context, HValue* target,
int argument_count, CallInterfaceDescriptor descriptor,
const Vector<HValue*>& operands,
TailCallMode syntactic_tail_call_mode = TailCallMode::kDisallow,
TailCallMode tail_call_mode = TailCallMode::kDisallow) {
HCallWithDescriptor* res = new (zone)
HCallWithDescriptor(target, argument_count, descriptor, operands,
syntactic_tail_call_mode, tail_call_mode, zone);
return res;
}
@ -2248,7 +2246,16 @@ class HCallWithDescriptor final : public HInstruction {
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 {
return argument_count_;
@ -2268,14 +2275,18 @@ class HCallWithDescriptor final : public HInstruction {
// The argument count includes the receiver.
HCallWithDescriptor(HValue* target, int argument_count,
CallInterfaceDescriptor descriptor,
const Vector<HValue*>& operands, CallMode call_mode,
Zone* zone)
const Vector<HValue*>& operands,
TailCallMode syntactic_tail_call_mode,
TailCallMode tail_call_mode, Zone* zone)
: descriptor_(descriptor),
values_(GetParameterCount() + 1, zone),
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.
DCHECK(call_mode != TAIL_CALL || argument_count == 0);
DCHECK(tail_call_mode != TailCallMode::kAllow || argument_count == 0);
AddOperand(target, zone);
for (int i = 0; i < operands.length(); i++) {
AddOperand(operands[i], zone);
@ -2300,15 +2311,18 @@ class HCallWithDescriptor final : public HInstruction {
CallInterfaceDescriptor descriptor_;
ZoneList<HValue*> values_;
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 {
public:
DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P4(HInvokeFunction, HValue*,
DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P5(HInvokeFunction, HValue*,
Handle<JSFunction>, int,
TailCallMode);
TailCallMode, TailCallMode);
HValue* context() { return first(); }
HValue* function() { return second(); }
@ -2316,12 +2330,22 @@ class HInvokeFunction final : public HBinaryCall {
int formal_parameter_count() const { return formal_parameter_count_; }
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 {
return TailCallModeField::decode(bit_field_);
}
DECLARE_CONCRETE_INSTRUCTION(InvokeFunction)
std::ostream& PrintTo(std::ostream& os) const override; // NOLINT
std::ostream& PrintDataTo(std::ostream& os) const override; // NOLINT
private:
void set_has_stack_check(bool has_stack_check) {
bit_field_ = HasStackCheckField::update(bit_field_, has_stack_check);
@ -2329,10 +2353,15 @@ class HInvokeFunction final : public HBinaryCall {
HInvokeFunction(HValue* context, HValue* function,
Handle<JSFunction> known_function, int argument_count,
TailCallMode syntactic_tail_call_mode,
TailCallMode tail_call_mode)
: HBinaryCall(context, function, argument_count),
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_ =
known_function.is_null()
? 0
@ -2349,6 +2378,8 @@ class HInvokeFunction final : public HBinaryCall {
class HasStackCheckField : public BitField<bool, 0, 1> {};
class TailCallModeField
: public BitField<TailCallMode, HasStackCheckField::kNext, 1> {};
class SyntacticTailCallModeField
: public BitField<TailCallMode, TailCallModeField::kNext, 1> {};
uint32_t bit_field_;
};

View File

@ -5513,9 +5513,8 @@ void HOptimizedGraphBuilder::VisitFunctionLiteral(FunctionLiteral* expr) {
FastNewClosureDescriptor descriptor(isolate());
HValue* values[] = {context(), shared_info_value};
HConstant* stub_value = Add<HConstant>(stub.GetCode());
instr = New<HCallWithDescriptor>(stub_value, 0, descriptor,
Vector<HValue*>(values, arraysize(values)),
NORMAL_CALL);
instr = New<HCallWithDescriptor>(
stub_value, 0, descriptor, Vector<HValue*>(values, arraysize(values)));
} else {
Add<HPushArguments>(shared_info_value);
Runtime::FunctionId function_id =
@ -5796,9 +5795,9 @@ void HOptimizedGraphBuilder::VisitRegExpLiteral(RegExpLiteral* expr) {
context(), AddThisFunction(), Add<HConstant>(expr->literal_index()),
Add<HConstant>(expr->pattern()), Add<HConstant>(expr->flags())};
HConstant* stub_value = Add<HConstant>(callable.code());
HInstruction* instr = New<HCallWithDescriptor>(
stub_value, 0, callable.descriptor(),
Vector<HValue*>(values, arraysize(values)), NORMAL_CALL);
HInstruction* instr =
New<HCallWithDescriptor>(stub_value, 0, callable.descriptor(),
Vector<HValue*>(values, arraysize(values)));
return ast_context()->ReturnInstruction(instr, expr->id());
}
@ -6583,7 +6582,7 @@ HValue* HOptimizedGraphBuilder::BuildMonomorphicAccess(
info->NeedsWrappingFor(Handle<JSFunction>::cast(info->accessor()))) {
HValue* function = Add<HConstant>(info->accessor());
PushArgumentsFromEnvironment(argument_count);
return NewCallFunction(function, argument_count,
return NewCallFunction(function, argument_count, TailCallMode::kDisallow,
ConvertReceiverMode::kNotNullOrUndefined,
TailCallMode::kDisallow);
} else if (FLAG_inline_accessors && can_inline_accessor) {
@ -6600,7 +6599,8 @@ HValue* HOptimizedGraphBuilder::BuildMonomorphicAccess(
return nullptr;
}
return NewCallConstantFunction(Handle<JSFunction>::cast(info->accessor()),
argument_count, TailCallMode::kDisallow);
argument_count, TailCallMode::kDisallow,
TailCallMode::kDisallow);
}
DCHECK(info->IsDataConstant());
@ -7999,8 +7999,8 @@ void HOptimizedGraphBuilder::AddCheckPrototypeMaps(Handle<JSObject> holder,
}
HInstruction* HOptimizedGraphBuilder::NewCallFunction(
HValue* function, int argument_count, ConvertReceiverMode convert_mode,
TailCallMode tail_call_mode) {
HValue* function, int argument_count, TailCallMode syntactic_tail_call_mode,
ConvertReceiverMode convert_mode, TailCallMode tail_call_mode) {
HValue* arity = Add<HConstant>(argument_count - 1);
HValue* op_vals[] = {context(), function, arity};
@ -8010,12 +8010,14 @@ HInstruction* HOptimizedGraphBuilder::NewCallFunction(
HConstant* stub = Add<HConstant>(callable.code());
return New<HCallWithDescriptor>(stub, argument_count, callable.descriptor(),
Vector<HValue*>(op_vals, arraysize(op_vals)));
Vector<HValue*>(op_vals, arraysize(op_vals)),
syntactic_tail_call_mode);
}
HInstruction* HOptimizedGraphBuilder::NewCallFunctionViaIC(
HValue* function, int argument_count, ConvertReceiverMode convert_mode,
TailCallMode tail_call_mode, FeedbackVectorSlot slot) {
HValue* function, int argument_count, TailCallMode syntactic_tail_call_mode,
ConvertReceiverMode convert_mode, TailCallMode tail_call_mode,
FeedbackVectorSlot slot) {
int arity = argument_count - 1;
Handle<TypeFeedbackVector> vector(current_feedback_vector(), isolate());
HValue* index_val = Add<HConstant>(vector->GetIndex(slot));
@ -8028,14 +8030,16 @@ HInstruction* HOptimizedGraphBuilder::NewCallFunctionViaIC(
HConstant* stub = Add<HConstant>(callable.code());
return New<HCallWithDescriptor>(stub, argument_count, callable.descriptor(),
Vector<HValue*>(op_vals, arraysize(op_vals)));
Vector<HValue*>(op_vals, arraysize(op_vals)),
syntactic_tail_call_mode);
}
HInstruction* HOptimizedGraphBuilder::NewCallConstantFunction(
Handle<JSFunction> function, int argument_count,
TailCallMode tail_call_mode) {
TailCallMode syntactic_tail_call_mode, TailCallMode tail_call_mode) {
HValue* target = Add<HConstant>(function);
return New<HInvokeFunction>(target, function, argument_count, tail_call_mode);
return New<HInvokeFunction>(target, function, argument_count,
syntactic_tail_call_mode, tail_call_mode);
}
@ -8186,10 +8190,12 @@ void HOptimizedGraphBuilder::HandlePolymorphicCallNamed(Call* expr,
// HWrapReceiver.
HInstruction* call =
needs_wrapping
? NewCallFunction(function, argument_count,
ConvertReceiverMode::kNotNullOrUndefined,
tail_call_mode)
: NewCallConstantFunction(target, argument_count, tail_call_mode);
? NewCallFunction(
function, argument_count, syntactic_tail_call_mode,
ConvertReceiverMode::kNotNullOrUndefined, tail_call_mode)
: NewCallConstantFunction(target, argument_count,
syntactic_tail_call_mode,
tail_call_mode);
PushArgumentsFromEnvironment(argument_count);
AddInstruction(call);
Drop(1); // Drop the function.
@ -8219,8 +8225,8 @@ void HOptimizedGraphBuilder::HandlePolymorphicCallNamed(Call* expr,
CHECK_ALIVE(VisitExpressions(expr->arguments()));
HInstruction* call = NewCallFunction(
function, argument_count, ConvertReceiverMode::kNotNullOrUndefined,
tail_call_mode);
function, argument_count, syntactic_tail_call_mode,
ConvertReceiverMode::kNotNullOrUndefined, tail_call_mode);
PushArgumentsFromEnvironment(argument_count);
@ -9106,8 +9112,8 @@ bool HOptimizedGraphBuilder::TryInlineBuiltinMethodCall(
if_inline.Else();
{
Add<HPushArguments>(receiver);
result = AddInstruction(
NewCallConstantFunction(function, 1, TailCallMode::kDisallow));
result = AddInstruction(NewCallConstantFunction(
function, 1, TailCallMode::kDisallow, TailCallMode::kDisallow));
if (!ast_context()->IsEffect()) Push(result);
}
if_inline.End();
@ -9371,8 +9377,9 @@ void HOptimizedGraphBuilder::HandleIndirectCall(Call* expr, HValue* function,
function_state()->ComputeTailCallMode(syntactic_tail_call_mode);
PushArgumentsFromEnvironment(arguments_count);
HInvokeFunction* call = New<HInvokeFunction>(function, known_function,
arguments_count, tail_call_mode);
HInvokeFunction* call =
New<HInvokeFunction>(function, known_function, arguments_count,
syntactic_tail_call_mode, tail_call_mode);
Drop(1); // Function
ast_context()->ReturnInstruction(call, expr->id());
}
@ -9769,14 +9776,15 @@ void HOptimizedGraphBuilder::VisitCall(Call* expr) {
// the receiver.
// TODO(verwaest): Support creation of value wrappers directly in
// HWrapReceiver.
call = NewCallFunction(function, argument_count,
ConvertReceiverMode::kNotNullOrUndefined,
tail_call_mode);
call = NewCallFunction(
function, argument_count, syntactic_tail_call_mode,
ConvertReceiverMode::kNotNullOrUndefined, tail_call_mode);
} else if (TryInlineCall(expr)) {
return;
} else {
call = NewCallConstantFunction(known_function, argument_count,
tail_call_mode);
call =
NewCallConstantFunction(known_function, argument_count,
syntactic_tail_call_mode, tail_call_mode);
}
} else {
@ -9795,7 +9803,7 @@ void HOptimizedGraphBuilder::VisitCall(Call* expr) {
Push(receiver);
CHECK_ALIVE(VisitExpressions(expr->arguments(), arguments_flag));
call = NewCallFunction(function, argument_count,
call = NewCallFunction(function, argument_count, syntactic_tail_call_mode,
ConvertReceiverMode::kNotNullOrUndefined,
tail_call_mode);
}
@ -9845,7 +9853,7 @@ void HOptimizedGraphBuilder::VisitCall(Call* expr) {
PushArgumentsFromEnvironment(argument_count);
call = NewCallConstantFunction(expr->target(), argument_count,
tail_call_mode);
syntactic_tail_call_mode, tail_call_mode);
} else {
PushArgumentsFromEnvironment(argument_count);
if (expr->is_uninitialized() &&
@ -9853,12 +9861,13 @@ void HOptimizedGraphBuilder::VisitCall(Call* expr) {
// We've never seen this call before, so let's have Crankshaft learn
// through the type vector.
call = NewCallFunctionViaIC(function, argument_count,
syntactic_tail_call_mode,
ConvertReceiverMode::kNullOrUndefined,
tail_call_mode, expr->CallFeedbackICSlot());
} else {
call = NewCallFunction(function, argument_count,
ConvertReceiverMode::kNullOrUndefined,
tail_call_mode);
call = NewCallFunction(
function, argument_count, syntactic_tail_call_mode,
ConvertReceiverMode::kNullOrUndefined, tail_call_mode);
}
}
}
@ -10500,6 +10509,7 @@ void HOptimizedGraphBuilder::VisitCallRuntime(CallRuntime* expr) {
CHECK_ALIVE(VisitExpressions(expr->arguments()));
PushArgumentsFromEnvironment(argument_count);
HInstruction* call = NewCallConstantFunction(known_function, argument_count,
TailCallMode::kDisallow,
TailCallMode::kDisallow);
Drop(1); // Function
return ast_context()->ReturnInstruction(call, expr->id());

View File

@ -2838,16 +2838,19 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
Handle<Map> receiver_map);
HInstruction* NewCallFunction(HValue* function, int argument_count,
TailCallMode syntactic_tail_call_mode,
ConvertReceiverMode convert_mode,
TailCallMode tail_call_mode);
HInstruction* NewCallFunctionViaIC(HValue* function, int argument_count,
TailCallMode syntactic_tail_call_mode,
ConvertReceiverMode convert_mode,
TailCallMode tail_call_mode,
FeedbackVectorSlot slot);
HInstruction* NewCallConstantFunction(Handle<JSFunction> target,
int argument_count,
TailCallMode syntactic_tail_call_mode,
TailCallMode tail_call_mode);
bool CanBeFunctionApplyArguments(Call* expr);

View File

@ -590,11 +590,7 @@ LInstruction* LChunkBuilder::DefineFixedDouble(
LInstruction* LChunkBuilder::AssignEnvironment(LInstruction* instr) {
HEnvironment* hydrogen_env = current_block_->last_environment();
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;
return LChunkBuilderBase::AssignEnvironment(instr, hydrogen_env);
}
@ -909,15 +905,39 @@ void LChunkBuilder::AddInstruction(LInstruction* instr,
chunk_->AddInstruction(instr, current_block_);
if (instr->IsCall()) {
HEnvironment* hydrogen_env = current_block_->last_environment();
HValue* hydrogen_value_for_lazy_bailout = hydrogen_val;
if (hydrogen_val->HasObservableSideEffects()) {
HSimulate* sim = HSimulate::cast(hydrogen_val->next());
sim->ReplayEnvironment(current_block_->last_environment());
hydrogen_value_for_lazy_bailout = sim;
DCHECK_NOT_NULL(hydrogen_env);
if (instr->IsSyntacticTailCall()) {
// If it was a syntactic tail call we need to drop the current frame and
// 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(
LCallWithDescriptor* result = new(zone()) LCallWithDescriptor(
descriptor, ops, zone());
if (instr->syntactic_tail_call_mode() == TailCallMode::kAllow) {
result->MarkAsSyntacticTailCall();
}
return MarkAsCall(DefineFixed(result, eax), instr, CANNOT_DEOPTIMIZE_EAGERLY);
}
@ -1106,6 +1129,9 @@ LInstruction* LChunkBuilder::DoInvokeFunction(HInvokeFunction* instr) {
LOperand* context = UseFixed(instr->context(), esi);
LOperand* function = UseFixed(instr->function(), edi);
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);
}

View File

@ -225,6 +225,13 @@ class LInstruction : public ZoneObject {
void MarkAsCall() { bit_field_ = IsCallBits::update(bit_field_, true); }
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.
bool ClobbersTemps() const { return IsCall(); }
bool ClobbersRegisters() const { return IsCall(); }
@ -259,6 +266,8 @@ class LInstruction : public ZoneObject {
virtual LOperand* TempAt(int i) = 0;
class IsCallBits: public BitField<bool, 0, 1> {};
class IsSyntacticTailCallBits : public BitField<bool, IsCallBits::kNext, 1> {
};
LEnvironment* environment_;
SetOncePointer<LPointerMap> pointer_map_;

View File

@ -508,6 +508,14 @@ void LChunkBuilderBase::Retry(BailoutReason reason) {
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(
HEnvironment* hydrogen_env, int* argument_index_accumulator,

View File

@ -745,6 +745,11 @@ class LChunkBuilderBase BASE_EMBEDDED {
// Will not be moved to a register even if one is freely available.
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,
int* argument_index_accumulator,
ZoneList<HValue*>* objects_to_materialize);

View File

@ -553,12 +553,7 @@ LInstruction* LChunkBuilder::DefineFixedDouble(
LInstruction* LChunkBuilder::AssignEnvironment(LInstruction* instr) {
HEnvironment* hydrogen_env = current_block_->last_environment();
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;
return LChunkBuilderBase::AssignEnvironment(instr, hydrogen_env);
}
@ -887,15 +882,39 @@ void LChunkBuilder::AddInstruction(LInstruction* instr,
chunk_->AddInstruction(instr, current_block_);
if (instr->IsCall()) {
HEnvironment* hydrogen_env = current_block_->last_environment();
HValue* hydrogen_value_for_lazy_bailout = hydrogen_val;
if (hydrogen_val->HasObservableSideEffects()) {
HSimulate* sim = HSimulate::cast(hydrogen_val->next());
sim->ReplayEnvironment(current_block_->last_environment());
hydrogen_value_for_lazy_bailout = sim;
DCHECK_NOT_NULL(hydrogen_env);
if (instr->IsSyntacticTailCall()) {
// If it was a syntactic tail call we need to drop the current frame and
// 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(
LCallWithDescriptor* result = new(zone()) LCallWithDescriptor(
descriptor, ops, zone());
if (instr->syntactic_tail_call_mode() == TailCallMode::kAllow) {
result->MarkAsSyntacticTailCall();
}
return MarkAsCall(DefineFixed(result, v0), instr);
}
@ -1078,6 +1100,9 @@ LInstruction* LChunkBuilder::DoInvokeFunction(HInvokeFunction* instr) {
LOperand* context = UseFixed(instr->context(), cp);
LOperand* function = UseFixed(instr->function(), a1);
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);
}

View File

@ -221,6 +221,13 @@ class LInstruction : public ZoneObject {
void MarkAsCall() { bit_field_ = IsCallBits::update(bit_field_, true); }
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.
bool ClobbersTemps() const { return IsCall(); }
bool ClobbersRegisters() const { return IsCall(); }
@ -255,6 +262,8 @@ class LInstruction : public ZoneObject {
virtual LOperand* TempAt(int i) = 0;
class IsCallBits: public BitField<bool, 0, 1> {};
class IsSyntacticTailCallBits : public BitField<bool, IsCallBits::kNext, 1> {
};
LEnvironment* environment_;
SetOncePointer<LPointerMap> pointer_map_;

View File

@ -553,12 +553,7 @@ LInstruction* LChunkBuilder::DefineFixedDouble(
LInstruction* LChunkBuilder::AssignEnvironment(LInstruction* instr) {
HEnvironment* hydrogen_env = current_block_->last_environment();
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;
return LChunkBuilderBase::AssignEnvironment(instr, hydrogen_env);
}
@ -887,15 +882,39 @@ void LChunkBuilder::AddInstruction(LInstruction* instr,
chunk_->AddInstruction(instr, current_block_);
if (instr->IsCall()) {
HEnvironment* hydrogen_env = current_block_->last_environment();
HValue* hydrogen_value_for_lazy_bailout = hydrogen_val;
if (hydrogen_val->HasObservableSideEffects()) {
HSimulate* sim = HSimulate::cast(hydrogen_val->next());
sim->ReplayEnvironment(current_block_->last_environment());
hydrogen_value_for_lazy_bailout = sim;
DCHECK_NOT_NULL(hydrogen_env);
if (instr->IsSyntacticTailCall()) {
// If it was a syntactic tail call we need to drop the current frame and
// 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(
LCallWithDescriptor* result = new(zone()) LCallWithDescriptor(
descriptor, ops, zone());
if (instr->syntactic_tail_call_mode() == TailCallMode::kAllow) {
result->MarkAsSyntacticTailCall();
}
return MarkAsCall(DefineFixed(result, v0), instr);
}
@ -1078,6 +1100,9 @@ LInstruction* LChunkBuilder::DoInvokeFunction(HInvokeFunction* instr) {
LOperand* context = UseFixed(instr->context(), cp);
LOperand* function = UseFixed(instr->function(), a1);
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);
}

View File

@ -224,6 +224,13 @@ class LInstruction : public ZoneObject {
void MarkAsCall() { bit_field_ = IsCallBits::update(bit_field_, true); }
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.
bool ClobbersTemps() const { return IsCall(); }
bool ClobbersRegisters() const { return IsCall(); }
@ -258,6 +265,8 @@ class LInstruction : public ZoneObject {
virtual LOperand* TempAt(int i) = 0;
class IsCallBits: public BitField<bool, 0, 1> {};
class IsSyntacticTailCallBits : public BitField<bool, IsCallBits::kNext, 1> {
};
LEnvironment* environment_;
SetOncePointer<LPointerMap> pointer_map_;

View File

@ -581,11 +581,7 @@ LInstruction* LChunkBuilder::DefineFixedDouble(
LInstruction* LChunkBuilder::AssignEnvironment(LInstruction* instr) {
HEnvironment* hydrogen_env = current_block_->last_environment();
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;
return LChunkBuilderBase::AssignEnvironment(instr, hydrogen_env);
}
@ -901,15 +897,39 @@ void LChunkBuilder::AddInstruction(LInstruction* instr,
chunk_->AddInstruction(instr, current_block_);
if (instr->IsCall()) {
HEnvironment* hydrogen_env = current_block_->last_environment();
HValue* hydrogen_value_for_lazy_bailout = hydrogen_val;
if (hydrogen_val->HasObservableSideEffects()) {
HSimulate* sim = HSimulate::cast(hydrogen_val->next());
sim->ReplayEnvironment(current_block_->last_environment());
hydrogen_value_for_lazy_bailout = sim;
DCHECK_NOT_NULL(hydrogen_env);
if (instr->IsSyntacticTailCall()) {
// If it was a syntactic tail call we need to drop the current frame and
// 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(
LCallWithDescriptor* result = new(zone()) LCallWithDescriptor(
descriptor, ops, zone());
if (instr->syntactic_tail_call_mode() == TailCallMode::kAllow) {
result->MarkAsSyntacticTailCall();
}
return MarkAsCall(DefineFixed(result, rax), instr);
}
@ -1095,6 +1118,9 @@ LInstruction* LChunkBuilder::DoInvokeFunction(HInvokeFunction* instr) {
LOperand* context = UseFixed(instr->context(), rsi);
LOperand* function = UseFixed(instr->function(), rdi);
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);
}

View File

@ -221,6 +221,13 @@ class LInstruction : public ZoneObject {
void MarkAsCall() { bit_field_ = IsCallBits::update(bit_field_, true); }
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.
bool ClobbersTemps() const { return IsCall(); }
bool ClobbersRegisters() const { return IsCall(); }
@ -259,6 +266,8 @@ class LInstruction : public ZoneObject {
virtual LOperand* TempAt(int i) = 0;
class IsCallBits: public BitField<bool, 0, 1> {};
class IsSyntacticTailCallBits : public BitField<bool, IsCallBits::kNext, 1> {
};
LEnvironment* environment_;
SetOncePointer<LPointerMap> pointer_map_;

View File

@ -3,8 +3,6 @@
// found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-tailcalls
// TODO(v8:4698), TODO(ishell): support these cases.
// Flags: --turbo --nostress-opt
try {
load("mjsunit/es6/tail-call-megatest.js");

View File

@ -3,8 +3,6 @@
// found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-tailcalls
// TODO(v8:4698), TODO(ishell): support these cases.
// Flags: --turbo --nostress-opt
try {
load("mjsunit/es6/tail-call-megatest.js");

View File

@ -3,8 +3,6 @@
// found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-tailcalls
// TODO(v8:4698), TODO(ishell): support these cases.
// Flags: --turbo --nostress-opt
try {
load("mjsunit/es6/tail-call-megatest.js");

View File

@ -3,8 +3,6 @@
// found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-tailcalls
// TODO(v8:4698), TODO(ishell): support these cases.
// Flags: --turbo --nostress-opt
try {
load("mjsunit/es6/tail-call-megatest.js");

View File

@ -3,8 +3,6 @@
// found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-tailcalls
// TODO(v8:4698), TODO(ishell): support these cases.
// Flags: --turbo --nostress-opt
try {
load("mjsunit/es6/tail-call-megatest.js");

View File

@ -3,8 +3,6 @@
// found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-tailcalls
// TODO(v8:4698), TODO(ishell): support these cases.
// Flags: --turbo --nostress-opt
try {
load("mjsunit/es6/tail-call-megatest.js");

View File

@ -3,8 +3,6 @@
// found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-tailcalls
// TODO(v8:4698), TODO(ishell): support these cases.
// Flags: --turbo --nostress-opt
try {
load("mjsunit/es6/tail-call-megatest.js");

View File

@ -3,8 +3,6 @@
// found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-tailcalls
// TODO(v8:4698), TODO(ishell): support these cases.
// Flags: --turbo --nostress-opt
try {
load("mjsunit/es6/tail-call-megatest.js");

View File

@ -3,8 +3,6 @@
// found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-tailcalls
// TODO(v8:4698), TODO(ishell): support these cases.
// Flags: --turbo --nostress-opt
try {
load("mjsunit/es6/tail-call-megatest.js");

View File

@ -3,8 +3,6 @@
// found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-tailcalls
// TODO(v8:4698), TODO(ishell): support these cases.
// Flags: --turbo --nostress-opt
try {
load("mjsunit/es6/tail-call-megatest.js");

View File

@ -3,8 +3,6 @@
// found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-tailcalls
// TODO(v8:4698), TODO(ishell): support these cases.
// Flags: --turbo --nostress-opt
Error.prepareStackTrace = (error,stack) => {
@ -59,6 +57,19 @@ function run_tests(shard) {
}
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 = {
func_name: 'f',
source_template: function(cfg) {
@ -66,7 +77,7 @@ function run_tests(shard) {
: "global";
var do_checks = [
` assertEquals_(${receiver}, this);`,
` assertEquals_(undefined, new.target);`,
` ${!cfg.check_new_target ? "// " : ""}assertEquals_(undefined, new.target);`,
check_arguments,
` checkStackTrace_([f, test]);`,
].join("\n");
@ -74,9 +85,10 @@ function run_tests(shard) {
var lines = [
`function f(a) {`,
` ${inlinable_comment(cfg.f_inlinable)}`,
` counter++;`,
` var expected_args = [${cfg.f_args}];`,
do_checks,
` %DeoptimizeNow();`,
deopt_template(cfg.deopt_mode),
do_checks,
` return 42;`,
`}`,
@ -92,7 +104,7 @@ function run_tests(shard) {
: "undefined";
var do_checks = [
` assertEquals_(${receiver}, this);`,
` assertEquals_(undefined, new.target);`,
` ${!cfg.check_new_target ? "// " : ""}assertEquals_(undefined, new.target);`,
check_arguments,
` checkStackTrace_([f, test]);`,
].join("\n");
@ -101,9 +113,10 @@ function run_tests(shard) {
`function f(a) {`,
` "use strict";`,
` ${inlinable_comment(cfg.f_inlinable)}`,
` counter++;`,
` var expected_args = [${cfg.f_args}];`,
do_checks,
` %DeoptimizeNow();`,
deopt_template(cfg.deopt_mode),
do_checks,
` return 42;`,
`}`,
@ -119,7 +132,7 @@ function run_tests(shard) {
: "global";
var do_checks = [
` assertEquals_(${receiver}, this);`,
` assertEquals_(undefined, new.target);`,
` ${!cfg.check_new_target ? "// " : ""}assertEquals_(undefined, new.target);`,
check_arguments,
` checkStackTrace_([f, test]);`,
].join("\n");
@ -127,9 +140,10 @@ function run_tests(shard) {
var lines = [
`function f(a) {`,
` ${inlinable_comment(cfg.f_inlinable)}`,
` counter++;`,
` var expected_args = [${cfg.f_args}];`,
do_checks,
` %DeoptimizeNow();`,
deopt_template(cfg.deopt_mode),
do_checks,
` return 42;`,
`}`,
@ -144,7 +158,7 @@ function run_tests(shard) {
source_template: function(cfg) {
var do_checks = [
` assertEquals_(receiver, this);`,
` assertEquals_(undefined, new.target);`,
` ${!cfg.check_new_target ? "// " : ""}assertEquals_(undefined, new.target);`,
check_arguments,
` checkStackTrace_([f, test]);`,
].join("\n");
@ -153,9 +167,10 @@ function run_tests(shard) {
`function f(a) {`,
` "use strict";`,
` ${inlinable_comment(cfg.f_inlinable)}`,
` counter++;`,
` var expected_args = [${cfg.f_args}];`,
do_checks,
` %DeoptimizeNow();`,
deopt_template(cfg.deopt_mode),
do_checks,
` return 42;`,
`}`,
@ -173,7 +188,7 @@ function run_tests(shard) {
: "global";
var do_checks = [
` assertEquals_(${receiver}, this);`,
` assertEquals_(undefined, new.target);`,
` ${!cfg.check_new_target ? "// " : ""}assertEquals_(undefined, new.target);`,
check_arguments,
` checkStackTrace_([f, test]);`,
].join("\n");
@ -181,9 +196,10 @@ function run_tests(shard) {
var lines = [
`function f(a) {`,
` ${inlinable_comment(cfg.f_inlinable)}`,
` counter++;`,
` var expected_args = [${cfg.f_args}];`,
do_checks,
` %DeoptimizeNow();`,
deopt_template(cfg.deopt_mode),
do_checks,
` return 42;`,
`}`,
@ -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 = {
receiver: "the_receiver",
source_template: function(cfg) {
@ -282,6 +315,7 @@ function run_tests(shard) {
` var undefined = void 0;`,
` var global = Function('return this')();`,
` var the_receiver = {receiver: 1};`,
` var counter = 0;`,
``,
` // Don't inline helper functions`,
` %NeverOptimizeFunction(assertEquals);`,
@ -293,13 +327,12 @@ function run_tests(shard) {
` "use strict";`,
` assertEquals_(42, g(${cfg.g_args}));`,
` }`,
` ${cfg.f_inlinable ? "%SetForceInlineFlag(f)" : ""};`,
` ${cfg.g_inlinable ? "%SetForceInlineFlag(g)" : ""};`,
` ${"test();".repeat(cfg.test_warmup_count)}`,
` ${cfg.f_inlinable ? "%SetForceInlineFlag(f)" : "%OptimizeFunctionOnNextCall(f)"};`,
` ${cfg.g_inlinable ? "%SetForceInlineFlag(g)" : "%OptimizeFunctionOnNextCall(g)"};`,
` %OptimizeFunctionOnNextCall(test);`,
` %OptimizeFunctionOnNextCall(f);`,
` %OptimizeFunctionOnNextCall(g);`,
` test();`,
` assertEquals(${1 + cfg.test_warmup_count}, counter);`,
`})();`,
``,
];
@ -311,6 +344,9 @@ function run_tests(shard) {
var g_args_variants = ["", "10", "10, 20"];
var f_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 = [
f_cfg_sloppy,
f_cfg_strict,
@ -320,42 +356,51 @@ function run_tests(shard) {
];
var g_variants = [
g_cfg_normal,
g_cfg_function_call,
g_cfg_reflect_apply,
g_cfg_function_apply,
g_cfg_function_apply_arguments_object,
g_cfg_function_call,
];
var test_warmup_counts = [0, 1, 2];
var iter = 0;
var tests_executed = 0;
if (shard !== undefined) {
print("Running shard #" + shard);
}
f_variants.forEach((f_cfg) => {
g_variants.forEach((g_cfg) => {
f_args_variants.forEach((f_args) => {
g_args_variants.forEach((g_args) => {
f_inlinable_variants.forEach((f_inlinable) => {
g_inlinable_variants.forEach((g_inlinable) => {
test_warmup_counts.forEach((test_warmup_count) => {
if (shard !== undefined && (iter++) % SHARDS_COUNT != shard) {
print("skipping...");
return;
}
var cfg = {
f_source_template: f_cfg.source_template,
f_inlinable,
f_args,
f_name: f_cfg.func_name,
f_receiver: g_cfg.receiver,
g_source_template: g_cfg.source_template,
g_inlinable,
g_args,
test_warmup_count,
};
var source = test_template(cfg);
print("====================");
print(source);
eval(source);
check_new_target_variants.forEach((check_new_target) => {
deopt_mode_variants.forEach((deopt_mode) => {
g_variants.forEach((g_cfg) => {
f_args_variants.forEach((f_args) => {
g_args_variants.forEach((g_args) => {
f_inlinable_variants.forEach((f_inlinable) => {
g_inlinable_variants.forEach((g_inlinable) => {
test_warmup_counts.forEach((test_warmup_count) => {
if (shard !== undefined && (iter++) % SHARDS_COUNT != shard) {
print("skipping...");
return;
}
tests_executed++;
var cfg = {
f_source_template: f_cfg.source_template,
f_inlinable,
f_args,
f_name: f_cfg.func_name,
f_receiver: g_cfg.receiver,
g_source_template: g_cfg.source_template,
g_inlinable,
g_args,
test_warmup_count,
check_new_target,
deopt_mode,
};
var source = test_template(cfg);
print("====================");
print(source);
eval(source);
});
});
});
});
});
@ -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.

View File

@ -3,8 +3,6 @@
// found in the LICENSE file.
// 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.

View File

@ -3,8 +3,6 @@
// found in the LICENSE file.
// Flags: --allow-natives-syntax --harmony-tailcalls
// TODO(v8:4698), TODO(ishell): support these cases.
// Flags: --max-inlined-source-size=0
"use strict";
Error.prepareStackTrace = (error,stack) => {

View File

@ -43,9 +43,6 @@
# This test non-deterministically runs out of memory on Windows ia32.
'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
'regress/regress-2653': [SKIP],