[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:
parent
2a1570ef1a
commit
acbb968ded
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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_;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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_;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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_;
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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_;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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_;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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_;
|
||||
|
@ -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");
|
||||
|
@ -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");
|
||||
|
@ -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");
|
||||
|
@ -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");
|
||||
|
@ -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");
|
||||
|
@ -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");
|
||||
|
@ -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");
|
||||
|
@ -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");
|
||||
|
@ -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");
|
||||
|
@ -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");
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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) => {
|
||||
|
@ -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],
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user