Initial shot at deoptimizing JSCallFunction in Turbofan.
BUG= R=bmeurer@chromium.org Review URL: https://codereview.chromium.org/444883006 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@23020 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
216e6618db
commit
5c2e968bdb
@ -49,9 +49,10 @@ CallDescriptor* Linkage::GetRuntimeCallDescriptor(
|
||||
|
||||
|
||||
CallDescriptor* Linkage::GetStubCallDescriptor(
|
||||
CodeStubInterfaceDescriptor* descriptor, int stack_parameter_count) {
|
||||
CodeStubInterfaceDescriptor* descriptor, int stack_parameter_count,
|
||||
CallDescriptor::DeoptimizationSupport can_deoptimize, Zone* zone) {
|
||||
return LinkageHelper::GetStubCallDescriptor<LinkageHelperTraits>(
|
||||
this->info_->zone(), descriptor, stack_parameter_count);
|
||||
zone, descriptor, stack_parameter_count, can_deoptimize);
|
||||
}
|
||||
|
||||
|
||||
|
@ -49,9 +49,10 @@ CallDescriptor* Linkage::GetRuntimeCallDescriptor(
|
||||
|
||||
|
||||
CallDescriptor* Linkage::GetStubCallDescriptor(
|
||||
CodeStubInterfaceDescriptor* descriptor, int stack_parameter_count) {
|
||||
CodeStubInterfaceDescriptor* descriptor, int stack_parameter_count,
|
||||
CallDescriptor::DeoptimizationSupport can_deoptimize, Zone* zone) {
|
||||
return LinkageHelper::GetStubCallDescriptor<LinkageHelperTraits>(
|
||||
this->info_->zone(), descriptor, stack_parameter_count);
|
||||
zone, descriptor, stack_parameter_count, can_deoptimize);
|
||||
}
|
||||
|
||||
|
||||
|
@ -722,8 +722,13 @@ void AstGraphBuilder::VisitForInStatement(ForInStatement* stmt) {
|
||||
environment()->Push(value);
|
||||
// result is either the string key or Smi(0) indicating the property
|
||||
// is gone.
|
||||
// TODO(jarin) Insert lazy deoptimization support here - the call
|
||||
// actually
|
||||
// can deoptimize.
|
||||
Node* res = ProcessArguments(
|
||||
javascript()->Call(3, NO_CALL_FUNCTION_FLAGS), 3);
|
||||
javascript()->Call(3, NO_CALL_FUNCTION_FLAGS,
|
||||
CallDescriptor::kCannotDeoptimize),
|
||||
3);
|
||||
Node* property_missing = NewNode(javascript()->StrictEqual(), res,
|
||||
jsgraph()->ZeroConstant());
|
||||
{
|
||||
@ -1246,9 +1251,12 @@ void AstGraphBuilder::VisitCall(Call* expr) {
|
||||
}
|
||||
|
||||
// Create node to perform the function call.
|
||||
Operator* call = javascript()->Call(args->length() + 2, flags);
|
||||
Operator* call = javascript()->Call(args->length() + 2, flags,
|
||||
CallDescriptor::kCanDeoptimize);
|
||||
Node* value = ProcessArguments(call, args->length() + 2);
|
||||
ast_context()->ProduceValue(value);
|
||||
|
||||
BuildLazyBailout(value, expr->id());
|
||||
}
|
||||
|
||||
|
||||
@ -1283,9 +1291,12 @@ void AstGraphBuilder::VisitCallJSRuntime(CallRuntime* expr) {
|
||||
VisitForValues(args);
|
||||
|
||||
// Create node to perform the JS runtime call.
|
||||
Operator* call = javascript()->Call(args->length() + 2, flags);
|
||||
Operator* call = javascript()->Call(args->length() + 2, flags,
|
||||
CallDescriptor::kCanDeoptimize);
|
||||
Node* value = ProcessArguments(call, args->length() + 2);
|
||||
ast_context()->ProduceValue(value);
|
||||
|
||||
BuildLazyBailout(value, expr->id());
|
||||
}
|
||||
|
||||
|
||||
|
@ -215,7 +215,9 @@ void CodeGenerator::PopulateDeoptimizationData(Handle<Code> code_object) {
|
||||
for (int i = 0; i < deopt_count; i++) {
|
||||
FrameStateDescriptor* descriptor = code()->GetDeoptimizationEntry(i);
|
||||
data->SetAstId(i, descriptor->bailout_id());
|
||||
data->SetTranslationIndex(i, Smi::FromInt(0));
|
||||
CHECK_NE(NULL, deoptimization_states_[i]);
|
||||
data->SetTranslationIndex(
|
||||
i, Smi::FromInt(deoptimization_states_[i]->translation_id_));
|
||||
data->SetArgumentsStackHeight(i, Smi::FromInt(0));
|
||||
data->SetPc(i, Smi::FromInt(-1));
|
||||
}
|
||||
|
@ -45,9 +45,10 @@ CallDescriptor* Linkage::GetRuntimeCallDescriptor(
|
||||
|
||||
|
||||
CallDescriptor* Linkage::GetStubCallDescriptor(
|
||||
CodeStubInterfaceDescriptor* descriptor, int stack_parameter_count) {
|
||||
CodeStubInterfaceDescriptor* descriptor, int stack_parameter_count,
|
||||
CallDescriptor::DeoptimizationSupport can_deoptimize, Zone* zone) {
|
||||
return LinkageHelper::GetStubCallDescriptor<LinkageHelperTraits>(
|
||||
this->info_->zone(), descriptor, stack_parameter_count);
|
||||
zone, descriptor, stack_parameter_count, can_deoptimize);
|
||||
}
|
||||
|
||||
|
||||
|
@ -349,18 +349,22 @@ void JSGenericLowering::ReplaceWithBuiltinCall(Node* node,
|
||||
}
|
||||
|
||||
|
||||
static CallDescriptor::DeoptimizationSupport DeoptimizationSupportForNode(
|
||||
Node* node) {
|
||||
return OperatorProperties::CanLazilyDeoptimize(node->op())
|
||||
? CallDescriptor::kCanDeoptimize
|
||||
: CallDescriptor::kCannotDeoptimize;
|
||||
}
|
||||
|
||||
|
||||
void JSGenericLowering::ReplaceWithRuntimeCall(Node* node,
|
||||
Runtime::FunctionId f,
|
||||
int nargs_override) {
|
||||
Operator::Property props = node->op()->properties();
|
||||
const Runtime::Function* fun = Runtime::FunctionForId(f);
|
||||
int nargs = (nargs_override < 0) ? fun->nargs : nargs_override;
|
||||
CallDescriptor::DeoptimizationSupport deopt =
|
||||
OperatorProperties::CanLazilyDeoptimize(node->op())
|
||||
? CallDescriptor::kCanDeoptimize
|
||||
: CallDescriptor::kCannotDeoptimize;
|
||||
CallDescriptor* desc =
|
||||
linkage()->GetRuntimeCallDescriptor(f, nargs, props, deopt);
|
||||
CallDescriptor* desc = linkage()->GetRuntimeCallDescriptor(
|
||||
f, nargs, props, DeoptimizationSupportForNode(node));
|
||||
Node* ref = ExternalConstant(ExternalReference(f, isolate()));
|
||||
Node* arity = Int32Constant(nargs);
|
||||
if (!centrystub_constant_.is_set()) {
|
||||
@ -524,7 +528,8 @@ Node* JSGenericLowering::LowerJSCallFunction(Node* node) {
|
||||
CallParameters p = OpParameter<CallParameters>(node);
|
||||
CallFunctionStub stub(isolate(), p.arity - 2, p.flags);
|
||||
CodeStubInterfaceDescriptor* d = GetInterfaceDescriptor(isolate(), &stub);
|
||||
CallDescriptor* desc = linkage()->GetStubCallDescriptor(d, p.arity - 1);
|
||||
CallDescriptor* desc = linkage()->GetStubCallDescriptor(
|
||||
d, p.arity - 1, DeoptimizationSupportForNode(node));
|
||||
Node* stub_code = CodeConstant(stub.GetCode());
|
||||
PatchInsertInput(node, 0, stub_code);
|
||||
PatchOperator(node, common()->Call(desc));
|
||||
|
@ -5,6 +5,7 @@
|
||||
#ifndef V8_COMPILER_JS_OPERATOR_H_
|
||||
#define V8_COMPILER_JS_OPERATOR_H_
|
||||
|
||||
#include "src/compiler/linkage.h"
|
||||
#include "src/compiler/opcodes.h"
|
||||
#include "src/compiler/operator.h"
|
||||
#include "src/unique.h"
|
||||
@ -48,6 +49,9 @@ struct LoadNamedParameters {
|
||||
struct CallParameters {
|
||||
int arity;
|
||||
CallFunctionFlags flags;
|
||||
// TODO(jarin) Remove the deopt flag once we can deoptimize all JavaScript
|
||||
// calls (specifically the FILTER_KEY call in ForInStatement).
|
||||
CallDescriptor::DeoptimizationSupport can_deoptimize;
|
||||
};
|
||||
|
||||
// Interface for building JavaScript-level operators, e.g. directly from the
|
||||
@ -103,8 +107,9 @@ class JSOperatorBuilder {
|
||||
|
||||
Operator* Create() { SIMPLE(JSCreate, Operator::kEliminatable, 0, 1); }
|
||||
|
||||
Operator* Call(int arguments, CallFunctionFlags flags) {
|
||||
CallParameters parameters = {arguments, flags};
|
||||
Operator* Call(int arguments, CallFunctionFlags flags,
|
||||
CallDescriptor::DeoptimizationSupport can_deoptimize) {
|
||||
CallParameters parameters = {arguments, flags, can_deoptimize};
|
||||
OP1(JSCallFunction, CallParameters, parameters, Operator::kNoProperties,
|
||||
arguments, 1);
|
||||
}
|
||||
|
@ -128,7 +128,8 @@ class LinkageHelper {
|
||||
template <typename LinkageTraits>
|
||||
static CallDescriptor* GetStubCallDescriptor(
|
||||
Zone* zone, CodeStubInterfaceDescriptor* descriptor,
|
||||
int stack_parameter_count) {
|
||||
int stack_parameter_count,
|
||||
CallDescriptor::DeoptimizationSupport can_deoptimize) {
|
||||
int register_parameter_count = descriptor->GetEnvironmentParameterCount();
|
||||
int parameter_count = register_parameter_count + stack_parameter_count;
|
||||
const int code_count = 1;
|
||||
@ -165,9 +166,8 @@ class LinkageHelper {
|
||||
locations, // locations
|
||||
Operator::kNoProperties, // properties
|
||||
kNoCalleeSaved, // callee-saved registers
|
||||
CallDescriptor::kCannotDeoptimize, // deoptimization
|
||||
can_deoptimize, // deoptimization
|
||||
CodeStub::MajorName(descriptor->MajorKey(), false));
|
||||
// TODO(jarin) should deoptimize!
|
||||
}
|
||||
|
||||
|
||||
|
@ -102,6 +102,14 @@ CallDescriptor* Linkage::GetRuntimeCallDescriptor(
|
||||
}
|
||||
|
||||
|
||||
CallDescriptor* Linkage::GetStubCallDescriptor(
|
||||
CodeStubInterfaceDescriptor* descriptor, int stack_parameter_count,
|
||||
CallDescriptor::DeoptimizationSupport can_deoptimize) {
|
||||
return GetStubCallDescriptor(descriptor, stack_parameter_count,
|
||||
can_deoptimize, this->info_->zone());
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
// Provide unimplemented methods on unsupported architectures, to at least link.
|
||||
//==============================================================================
|
||||
@ -122,7 +130,8 @@ CallDescriptor* Linkage::GetRuntimeCallDescriptor(
|
||||
|
||||
|
||||
CallDescriptor* Linkage::GetStubCallDescriptor(
|
||||
CodeStubInterfaceDescriptor* descriptor, int stack_parameter_count) {
|
||||
CodeStubInterfaceDescriptor* descriptor, int stack_parameter_count,
|
||||
CallDescriptor::DeoptimizationSupport can_deoptimize, Zone* zone) {
|
||||
UNIMPLEMENTED();
|
||||
return NULL;
|
||||
}
|
||||
|
@ -147,8 +147,13 @@ class Linkage : public ZoneObject {
|
||||
Operator::Property properties,
|
||||
CallDescriptor::DeoptimizationSupport can_deoptimize, Zone* zone);
|
||||
|
||||
CallDescriptor* GetStubCallDescriptor(CodeStubInterfaceDescriptor* descriptor,
|
||||
int stack_parameter_count = 0);
|
||||
CallDescriptor* GetStubCallDescriptor(
|
||||
CodeStubInterfaceDescriptor* descriptor, int stack_parameter_count = 0,
|
||||
CallDescriptor::DeoptimizationSupport can_deoptimize =
|
||||
CallDescriptor::kCannotDeoptimize);
|
||||
static CallDescriptor* GetStubCallDescriptor(
|
||||
CodeStubInterfaceDescriptor* descriptor, int stack_parameter_count,
|
||||
CallDescriptor::DeoptimizationSupport can_deoptimize, Zone* zone);
|
||||
|
||||
// Creates a call descriptor for simplified C calls that is appropriate
|
||||
// for the host platform. This simplified calling convention only supports
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include "src/v8.h"
|
||||
|
||||
#include "src/compiler/js-operator.h"
|
||||
#include "src/compiler/opcodes.h"
|
||||
#include "src/compiler/operator-properties.h"
|
||||
|
||||
@ -130,6 +131,14 @@ inline bool OperatorProperties::IsScheduleRoot(Operator* op) {
|
||||
}
|
||||
|
||||
inline bool OperatorProperties::CanLazilyDeoptimize(Operator* op) {
|
||||
// TODO(jarin) This function allows turning on lazy deoptimization
|
||||
// incrementally. It will change as we turn on lazy deopt for
|
||||
// more nodes.
|
||||
|
||||
if (!FLAG_turbo_deoptimization) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (op->opcode() == IrOpcode::kCall) {
|
||||
CallOperator* call_op = reinterpret_cast<CallOperator*>(op);
|
||||
CallDescriptor* descriptor = call_op->parameter();
|
||||
@ -142,6 +151,11 @@ inline bool OperatorProperties::CanLazilyDeoptimize(Operator* op) {
|
||||
reinterpret_cast<Operator1<Runtime::FunctionId>*>(op)->parameter();
|
||||
return function == Runtime::kDeoptimizeFunction;
|
||||
}
|
||||
if (op->opcode() == IrOpcode::kJSCallFunction) {
|
||||
CallParameters p =
|
||||
reinterpret_cast<Operator1<CallParameters>*>(op)->parameter();
|
||||
return p.can_deoptimize == CallDescriptor::kCanDeoptimize;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -744,6 +744,70 @@ void RegisterAllocator::MeetRegisterConstraints(BasicBlock* block) {
|
||||
if (!AllocationOk()) return;
|
||||
}
|
||||
}
|
||||
|
||||
// Meet register constraints for the instruction in the end.
|
||||
if (!code()->IsGapAt(end)) {
|
||||
MeetRegisterConstraintsForLastInstructionInBlock(block);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void RegisterAllocator::MeetRegisterConstraintsForLastInstructionInBlock(
|
||||
BasicBlock* block) {
|
||||
int end = block->last_instruction_index();
|
||||
Instruction* last_instruction = InstructionAt(end);
|
||||
for (size_t i = 0; i < last_instruction->OutputCount(); i++) {
|
||||
InstructionOperand* output_operand = last_instruction->OutputAt(i);
|
||||
DCHECK(!output_operand->IsConstant());
|
||||
UnallocatedOperand* output = UnallocatedOperand::cast(output_operand);
|
||||
int output_vreg = output->virtual_register();
|
||||
LiveRange* range = LiveRangeFor(output_vreg);
|
||||
bool assigned = false;
|
||||
if (output->HasFixedPolicy()) {
|
||||
AllocateFixed(output, -1, false);
|
||||
// This value is produced on the stack, we never need to spill it.
|
||||
if (output->IsStackSlot()) {
|
||||
range->SetSpillOperand(output);
|
||||
range->SetSpillStartIndex(end);
|
||||
assigned = true;
|
||||
}
|
||||
|
||||
BasicBlock::Successors successors = block->successors();
|
||||
for (BasicBlock::Successors::iterator succ = successors.begin();
|
||||
succ != successors.end(); ++succ) {
|
||||
DCHECK((*succ)->PredecessorCount() == 1);
|
||||
int gap_index = (*succ)->first_instruction_index() + 1;
|
||||
DCHECK(code()->IsGapAt(gap_index));
|
||||
|
||||
// Create an unconstrained operand for the same virtual register
|
||||
// and insert a gap move from the fixed output to the operand.
|
||||
UnallocatedOperand* output_copy =
|
||||
new (code_zone()) UnallocatedOperand(UnallocatedOperand::ANY);
|
||||
output_copy->set_virtual_register(output_vreg);
|
||||
|
||||
code()->AddGapMove(gap_index, output, output_copy);
|
||||
}
|
||||
}
|
||||
|
||||
if (!assigned) {
|
||||
BasicBlock::Successors successors = block->successors();
|
||||
for (BasicBlock::Successors::iterator succ = successors.begin();
|
||||
succ != successors.end(); ++succ) {
|
||||
DCHECK((*succ)->PredecessorCount() == 1);
|
||||
int gap_index = (*succ)->first_instruction_index() + 1;
|
||||
range->SetSpillStartIndex(gap_index);
|
||||
|
||||
// This move to spill operand is not a real use. Liveness analysis
|
||||
// and splitting of live ranges do not account for it.
|
||||
// Thus it should be inserted to a lifetime position corresponding to
|
||||
// the instruction end.
|
||||
GapInstruction* gap = code()->GapAt(gap_index);
|
||||
ParallelMove* move =
|
||||
gap->GetOrCreateParallelMove(GapInstruction::BEFORE, code_zone());
|
||||
move->AddMove(output, range->GetSpillOperand(), code_zone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -786,6 +850,8 @@ void RegisterAllocator::MeetConstraintsBetween(Instruction* first,
|
||||
code()->AddGapMove(gap_index, first_output, output_copy);
|
||||
}
|
||||
|
||||
// Make sure we add a gap move for spilling (if we have not done
|
||||
// so already).
|
||||
if (!assigned) {
|
||||
range->SetSpillStartIndex(gap_index);
|
||||
|
||||
|
@ -391,6 +391,7 @@ class RegisterAllocator BASE_EMBEDDED {
|
||||
void MeetRegisterConstraints(BasicBlock* block);
|
||||
void MeetConstraintsBetween(Instruction* first, Instruction* second,
|
||||
int gap_index);
|
||||
void MeetRegisterConstraintsForLastInstructionInBlock(BasicBlock* block);
|
||||
void ResolvePhis(BasicBlock* block);
|
||||
|
||||
// Helper methods for building intervals.
|
||||
|
@ -64,9 +64,10 @@ CallDescriptor* Linkage::GetRuntimeCallDescriptor(
|
||||
|
||||
|
||||
CallDescriptor* Linkage::GetStubCallDescriptor(
|
||||
CodeStubInterfaceDescriptor* descriptor, int stack_parameter_count) {
|
||||
CodeStubInterfaceDescriptor* descriptor, int stack_parameter_count,
|
||||
CallDescriptor::DeoptimizationSupport can_deoptimize, Zone* zone) {
|
||||
return LinkageHelper::GetStubCallDescriptor<LinkageHelperTraits>(
|
||||
this->info_->zone(), descriptor, stack_parameter_count);
|
||||
zone, descriptor, stack_parameter_count, can_deoptimize);
|
||||
}
|
||||
|
||||
|
||||
|
@ -11128,7 +11128,7 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint(
|
||||
os << "Deoptimization Input Data (deopt points = " << deopt_count << ")\n";
|
||||
if (0 != deopt_count) {
|
||||
os << " index ast id argc pc";
|
||||
if (FLAG_print_code_verbose) os << "commands";
|
||||
if (FLAG_print_code_verbose) os << " commands";
|
||||
os << "\n";
|
||||
}
|
||||
for (int i = 0; i < deopt_count; i++) {
|
||||
@ -11158,7 +11158,7 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint(
|
||||
Translation::BEGIN !=
|
||||
(opcode = static_cast<Translation::Opcode>(iterator.Next()))) {
|
||||
Vector<char> buf2 = Vector<char>::New(128);
|
||||
SNPrintF(buf2, "%24s %s ", "", Translation::StringFor(opcode));
|
||||
SNPrintF(buf2, "%27s %s ", "", Translation::StringFor(opcode));
|
||||
os << buf2.start();
|
||||
|
||||
switch (opcode) {
|
||||
@ -11284,11 +11284,11 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint(
|
||||
if (return_address_patch_count != 0) {
|
||||
os << "Return address patch data (count = " << return_address_patch_count
|
||||
<< ")\n";
|
||||
os << "index pc patched_pc\n";
|
||||
os << " index pc patched_pc\n";
|
||||
}
|
||||
for (int i = 0; i < return_address_patch_count; i++) {
|
||||
Vector<char> buf = Vector<char>::New(128);
|
||||
SNPrintF(buf, "%6d %6d %10d", i, ReturnAddressPc(i)->value(),
|
||||
SNPrintF(buf, "%6d %6d %12d\n", i, ReturnAddressPc(i)->value(),
|
||||
PatchedAddressPc(i)->value());
|
||||
os << buf.start();
|
||||
}
|
||||
|
@ -107,9 +107,6 @@
|
||||
'test-debug/DebugBreakLoop': [PASS, NO_VARIANTS],
|
||||
|
||||
# Support for lazy deoptimization is missing.
|
||||
'test-deoptimization/DeoptimizeSimple': [PASS, NO_VARIANTS],
|
||||
'test-deoptimization/DeoptimizeSimpleNested': [PASS, NO_VARIANTS],
|
||||
'test-deoptimization/DeoptimizeSimpleWithArguments': [PASS, NO_VARIANTS],
|
||||
'test-deoptimization/DeoptimizeBinaryOperation*': [PASS, NO_VARIANTS],
|
||||
'test-deoptimization/DeoptimizeCompare': [PASS, NO_VARIANTS],
|
||||
'test-deoptimization/DeoptimizeLoadICStoreIC': [PASS, NO_VARIANTS],
|
||||
|
@ -1713,6 +1713,8 @@ static Handle<JSFunction> Compile(const char* source) {
|
||||
|
||||
|
||||
TEST(BuildScheduleTrivialLazyDeoptCall) {
|
||||
FLAG_turbo_deoptimization = true;
|
||||
|
||||
HandleAndZoneScope scope;
|
||||
Isolate* isolate = scope.main_isolate();
|
||||
Graph graph(scope.main_zone());
|
||||
|
@ -113,6 +113,8 @@ static Handle<JSFunction> GetJSFunction(v8::Handle<v8::Object> obj,
|
||||
|
||||
|
||||
TEST(DeoptimizeSimple) {
|
||||
i::FLAG_turbo_deoptimization = true;
|
||||
|
||||
LocalContext env;
|
||||
v8::HandleScope scope(env->GetIsolate());
|
||||
|
||||
@ -151,6 +153,8 @@ TEST(DeoptimizeSimple) {
|
||||
|
||||
|
||||
TEST(DeoptimizeSimpleWithArguments) {
|
||||
i::FLAG_turbo_deoptimization = true;
|
||||
|
||||
LocalContext env;
|
||||
v8::HandleScope scope(env->GetIsolate());
|
||||
|
||||
@ -190,6 +194,8 @@ TEST(DeoptimizeSimpleWithArguments) {
|
||||
|
||||
|
||||
TEST(DeoptimizeSimpleNested) {
|
||||
i::FLAG_turbo_deoptimization = true;
|
||||
|
||||
LocalContext env;
|
||||
v8::HandleScope scope(env->GetIsolate());
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user