Support inlining at call-sites with mismatched number of arguments.
Review URL: https://chromiumcodereview.appspot.com/9265004 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10483 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
0b3ce1dd9e
commit
04289e8d17
@ -1,4 +1,4 @@
|
||||
// Copyright 2011 the V8 project authors. All rights reserved.
|
||||
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
@ -574,11 +574,12 @@ static MaybeObject* ConstructArgumentsObjectForInlinedFunction(
|
||||
Handle<JSFunction> inlined_function,
|
||||
int inlined_frame_index) {
|
||||
Factory* factory = Isolate::Current()->factory();
|
||||
int args_count = inlined_function->shared()->formal_parameter_count();
|
||||
ScopedVector<SlotRef> args_slots(args_count);
|
||||
SlotRef::ComputeSlotMappingForArguments(frame,
|
||||
Vector<SlotRef> args_slots =
|
||||
SlotRef::ComputeSlotMappingForArguments(
|
||||
frame,
|
||||
inlined_frame_index,
|
||||
&args_slots);
|
||||
inlined_function->shared()->formal_parameter_count());
|
||||
int args_count = args_slots.length();
|
||||
Handle<JSObject> arguments =
|
||||
factory->NewArgumentsObject(inlined_function, args_count);
|
||||
Handle<FixedArray> array = factory->NewFixedArray(args_count);
|
||||
@ -587,6 +588,7 @@ static MaybeObject* ConstructArgumentsObjectForInlinedFunction(
|
||||
array->set(i, *value);
|
||||
}
|
||||
arguments->set_elements(*array);
|
||||
args_slots.Dispose();
|
||||
|
||||
// Return the freshly allocated arguments object.
|
||||
return *arguments;
|
||||
|
@ -1760,6 +1760,7 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
|
||||
__ bind(&invoke);
|
||||
__ Call(r3);
|
||||
|
||||
masm->isolate()->heap()->SetArgumentsAdaptorDeoptPCOffset(masm->pc_offset());
|
||||
// Exit frame and return.
|
||||
LeaveArgumentsAdaptorFrame(masm);
|
||||
__ Jump(lr);
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2011 the V8 project authors. All rights reserved.
|
||||
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
@ -211,12 +211,13 @@ void Deoptimizer::DoComputeOsrOutputFrame() {
|
||||
ASSERT(Translation::BEGIN == opcode);
|
||||
USE(opcode);
|
||||
int count = iterator.Next();
|
||||
iterator.Skip(1); // Drop JS frame count.
|
||||
ASSERT(count == 1);
|
||||
USE(count);
|
||||
|
||||
opcode = static_cast<Translation::Opcode>(iterator.Next());
|
||||
USE(opcode);
|
||||
ASSERT(Translation::FRAME == opcode);
|
||||
ASSERT(Translation::JS_FRAME == opcode);
|
||||
unsigned node_id = iterator.Next();
|
||||
USE(node_id);
|
||||
ASSERT(node_id == ast_id);
|
||||
@ -252,9 +253,7 @@ void Deoptimizer::DoComputeOsrOutputFrame() {
|
||||
output_ = new FrameDescription*[1];
|
||||
output_[0] = new(output_frame_size) FrameDescription(
|
||||
output_frame_size, function_);
|
||||
#ifdef DEBUG
|
||||
output_[0]->SetKind(Code::OPTIMIZED_FUNCTION);
|
||||
#endif
|
||||
output_[0]->SetFrameType(StackFrame::JAVA_SCRIPT);
|
||||
|
||||
// Clear the incoming parameters in the optimized frame to avoid
|
||||
// confusing the garbage collector.
|
||||
@ -342,15 +341,115 @@ void Deoptimizer::DoComputeOsrOutputFrame() {
|
||||
}
|
||||
|
||||
|
||||
void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator,
|
||||
int frame_index) {
|
||||
JSFunction* function = JSFunction::cast(ComputeLiteral(iterator->Next()));
|
||||
unsigned height = iterator->Next();
|
||||
unsigned height_in_bytes = height * kPointerSize;
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" translating arguments adaptor => height=%d\n", height_in_bytes);
|
||||
}
|
||||
|
||||
unsigned fixed_frame_size = ArgumentsAdaptorFrameConstants::kFrameSize;
|
||||
unsigned input_frame_size = input_->GetFrameSize();
|
||||
unsigned output_frame_size = height_in_bytes + fixed_frame_size;
|
||||
|
||||
// Allocate and store the output frame description.
|
||||
FrameDescription* output_frame =
|
||||
new(output_frame_size) FrameDescription(output_frame_size, function);
|
||||
output_frame->SetFrameType(StackFrame::ARGUMENTS_ADAPTOR);
|
||||
|
||||
// Arguments adaptor can not be topmost or bottommost.
|
||||
ASSERT(frame_index > 0 && frame_index < output_count_ - 1);
|
||||
ASSERT(output_[frame_index] == NULL);
|
||||
output_[frame_index] = output_frame;
|
||||
|
||||
// The top address of the frame is computed from the previous
|
||||
// frame's top and this frame's size.
|
||||
uint32_t top_address;
|
||||
top_address = output_[frame_index - 1]->GetTop() - output_frame_size;
|
||||
output_frame->SetTop(top_address);
|
||||
|
||||
// Compute the incoming parameter translation.
|
||||
int parameter_count = height;
|
||||
unsigned output_offset = output_frame_size;
|
||||
unsigned input_offset = input_frame_size;
|
||||
for (int i = 0; i < parameter_count; ++i) {
|
||||
output_offset -= kPointerSize;
|
||||
DoTranslateCommand(iterator, frame_index, output_offset);
|
||||
}
|
||||
input_offset -= (parameter_count * kPointerSize);
|
||||
|
||||
// Read caller's PC from the previous frame.
|
||||
output_offset -= kPointerSize;
|
||||
input_offset -= kPointerSize;
|
||||
intptr_t callers_pc = output_[frame_index - 1]->GetPc();
|
||||
output_frame->SetFrameSlot(output_offset, callers_pc);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08x: [top + %d] <- 0x%08x ; caller's pc\n",
|
||||
top_address + output_offset, output_offset, callers_pc);
|
||||
}
|
||||
|
||||
// Read caller's FP from the previous frame, and set this frame's FP.
|
||||
output_offset -= kPointerSize;
|
||||
input_offset -= kPointerSize;
|
||||
intptr_t value = output_[frame_index - 1]->GetFp();
|
||||
output_frame->SetFrameSlot(output_offset, value);
|
||||
intptr_t fp_value = top_address + output_offset;
|
||||
output_frame->SetFp(fp_value);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08x: [top + %d] <- 0x%08x ; caller's fp\n",
|
||||
fp_value, output_offset, value);
|
||||
}
|
||||
|
||||
// A marker value is used in place of the context.
|
||||
output_offset -= kPointerSize;
|
||||
input_offset -= kPointerSize;
|
||||
intptr_t context = reinterpret_cast<intptr_t>(
|
||||
Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
|
||||
output_frame->SetFrameSlot(output_offset, context);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08x: [top + %d] <- 0x%08x ; context (adaptor sentinel)\n",
|
||||
top_address + output_offset, output_offset, context);
|
||||
}
|
||||
|
||||
// The function was mentioned explicitly in the ARGUMENTS_ADAPTOR_FRAME.
|
||||
output_offset -= kPointerSize;
|
||||
input_offset -= kPointerSize;
|
||||
value = reinterpret_cast<intptr_t>(function);
|
||||
output_frame->SetFrameSlot(output_offset, value);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08x: [top + %d] <- 0x%08x ; function\n",
|
||||
top_address + output_offset, output_offset, value);
|
||||
}
|
||||
|
||||
// Number of incoming arguments.
|
||||
output_offset -= kPointerSize;
|
||||
input_offset -= kPointerSize;
|
||||
value = reinterpret_cast<uint32_t>(Smi::FromInt(height - 1));
|
||||
output_frame->SetFrameSlot(output_offset, value);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08x: [top + %d] <- 0x%08x ; argc (%d)\n",
|
||||
top_address + output_offset, output_offset, value, height - 1);
|
||||
}
|
||||
|
||||
ASSERT(0 == output_offset);
|
||||
|
||||
Builtins* builtins = isolate_->builtins();
|
||||
Code* adaptor_trampoline =
|
||||
builtins->builtin(Builtins::kArgumentsAdaptorTrampoline);
|
||||
uint32_t pc = reinterpret_cast<uint32_t>(
|
||||
adaptor_trampoline->instruction_start() +
|
||||
isolate_->heap()->arguments_adaptor_deopt_pc_offset()->value());
|
||||
output_frame->SetPc(pc);
|
||||
}
|
||||
|
||||
|
||||
// This code is very similar to ia32 code, but relies on register names (fp, sp)
|
||||
// and how the frame is laid out.
|
||||
void Deoptimizer::DoComputeFrame(TranslationIterator* iterator,
|
||||
void Deoptimizer::DoComputeJSFrame(TranslationIterator* iterator,
|
||||
int frame_index) {
|
||||
// Read the ast node id, function, and frame height for this output frame.
|
||||
Translation::Opcode opcode =
|
||||
static_cast<Translation::Opcode>(iterator->Next());
|
||||
USE(opcode);
|
||||
ASSERT(Translation::FRAME == opcode);
|
||||
int node_id = iterator->Next();
|
||||
JSFunction* function = JSFunction::cast(ComputeLiteral(iterator->Next()));
|
||||
unsigned height = iterator->Next();
|
||||
@ -370,9 +469,7 @@ void Deoptimizer::DoComputeFrame(TranslationIterator* iterator,
|
||||
// Allocate and store the output frame description.
|
||||
FrameDescription* output_frame =
|
||||
new(output_frame_size) FrameDescription(output_frame_size, function);
|
||||
#ifdef DEBUG
|
||||
output_frame->SetKind(Code::FUNCTION);
|
||||
#endif
|
||||
output_frame->SetFrameType(StackFrame::JAVA_SCRIPT);
|
||||
|
||||
bool is_bottommost = (0 == frame_index);
|
||||
bool is_topmost = (output_count_ - 1 == frame_index);
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2011 the V8 project authors. All rights reserved.
|
||||
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
@ -136,6 +136,9 @@ class ExitFrameConstants : public AllStatic {
|
||||
|
||||
class StandardFrameConstants : public AllStatic {
|
||||
public:
|
||||
// Fixed part of the frame consists of return address, caller fp,
|
||||
// context and function.
|
||||
static const int kFixedFrameSize = 4 * kPointerSize;
|
||||
static const int kExpressionsOffset = -3 * kPointerSize;
|
||||
static const int kMarkerOffset = -2 * kPointerSize;
|
||||
static const int kContextOffset = -1 * kPointerSize;
|
||||
@ -161,6 +164,8 @@ class JavaScriptFrameConstants : public AllStatic {
|
||||
class ArgumentsAdaptorFrameConstants : public AllStatic {
|
||||
public:
|
||||
static const int kLengthOffset = StandardFrameConstants::kExpressionsOffset;
|
||||
static const int kFrameSize =
|
||||
StandardFrameConstants::kFixedFrameSize + kPointerSize;
|
||||
};
|
||||
|
||||
|
||||
|
@ -1005,14 +1005,16 @@ LEnvironment* LChunkBuilder::CreateEnvironment(
|
||||
LEnvironment* outer =
|
||||
CreateEnvironment(hydrogen_env->outer(), argument_index_accumulator);
|
||||
int ast_id = hydrogen_env->ast_id();
|
||||
ASSERT(ast_id != AstNode::kNoNumber);
|
||||
ASSERT(ast_id != AstNode::kNoNumber || hydrogen_env->is_arguments_adaptor());
|
||||
int value_count = hydrogen_env->length();
|
||||
LEnvironment* result = new LEnvironment(hydrogen_env->closure(),
|
||||
hydrogen_env->is_arguments_adaptor(),
|
||||
ast_id,
|
||||
hydrogen_env->parameter_count(),
|
||||
argument_count_,
|
||||
value_count,
|
||||
outer);
|
||||
int argument_index = *argument_index_accumulator;
|
||||
for (int i = 0; i < value_count; ++i) {
|
||||
if (hydrogen_env->is_special_index(i)) continue;
|
||||
|
||||
@ -1021,13 +1023,17 @@ LEnvironment* LChunkBuilder::CreateEnvironment(
|
||||
if (value->IsArgumentsObject()) {
|
||||
op = NULL;
|
||||
} else if (value->IsPushArgument()) {
|
||||
op = new LArgument((*argument_index_accumulator)++);
|
||||
op = new LArgument(argument_index++);
|
||||
} else {
|
||||
op = UseAny(value);
|
||||
}
|
||||
result->AddValue(op, value->representation());
|
||||
}
|
||||
|
||||
if (!hydrogen_env->is_arguments_adaptor()) {
|
||||
*argument_index_accumulator = argument_index;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -2244,6 +2250,7 @@ LInstruction* LChunkBuilder::DoEnterInlined(HEnterInlined* instr) {
|
||||
HEnvironment* outer = current_block_->last_environment();
|
||||
HConstant* undefined = graph()->GetConstantUndefined();
|
||||
HEnvironment* inner = outer->CopyForInlining(instr->closure(),
|
||||
instr->arguments_count(),
|
||||
instr->function(),
|
||||
undefined,
|
||||
instr->call_kind());
|
||||
@ -2254,7 +2261,8 @@ LInstruction* LChunkBuilder::DoEnterInlined(HEnterInlined* instr) {
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoLeaveInlined(HLeaveInlined* instr) {
|
||||
HEnvironment* outer = current_block_->last_environment()->outer();
|
||||
HEnvironment* outer = current_block_->last_environment()->
|
||||
DiscardInlined(false);
|
||||
current_block_->UpdateEnvironment(outer);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -479,7 +479,11 @@ void LCodeGen::WriteTranslation(LEnvironment* environment,
|
||||
|
||||
WriteTranslation(environment->outer(), translation);
|
||||
int closure_id = DefineDeoptimizationLiteral(environment->closure());
|
||||
translation->BeginFrame(environment->ast_id(), closure_id, height);
|
||||
if (environment->is_arguments_adaptor()) {
|
||||
translation->BeginArgumentsAdaptorFrame(closure_id, translation_size);
|
||||
} else {
|
||||
translation->BeginJSFrame(environment->ast_id(), closure_id, height);
|
||||
}
|
||||
for (int i = 0; i < translation_size; ++i) {
|
||||
LOperand* value = environment->values()->at(i);
|
||||
// spilled_registers_ and spilled_double_registers_ are either
|
||||
@ -612,10 +616,14 @@ void LCodeGen::RegisterEnvironmentForDeoptimization(LEnvironment* environment,
|
||||
// |>------------ translation_size ------------<|
|
||||
|
||||
int frame_count = 0;
|
||||
int jsframe_count = 0;
|
||||
for (LEnvironment* e = environment; e != NULL; e = e->outer()) {
|
||||
++frame_count;
|
||||
if (!e->is_arguments_adaptor()) {
|
||||
++jsframe_count;
|
||||
}
|
||||
Translation translation(&translations_, frame_count);
|
||||
}
|
||||
Translation translation(&translations_, frame_count, jsframe_count);
|
||||
WriteTranslation(environment, &translation);
|
||||
int deoptimization_index = deoptimizations_.length();
|
||||
int pc_offset = masm()->pc_offset();
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2011 the V8 project authors. All rights reserved.
|
||||
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
@ -104,10 +104,27 @@ Deoptimizer* Deoptimizer::Grab(Isolate* isolate) {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
int Deoptimizer::ConvertJSFrameIndexToFrameIndex(int jsframe_index) {
|
||||
if (jsframe_index == 0) return 0;
|
||||
|
||||
int frame_index = 0;
|
||||
while (jsframe_index >= 0) {
|
||||
FrameDescription* frame = output_[frame_index];
|
||||
if (frame->GetFrameType() == StackFrame::JAVA_SCRIPT) {
|
||||
jsframe_index--;
|
||||
}
|
||||
frame_index++;
|
||||
}
|
||||
|
||||
return frame_index - 1;
|
||||
}
|
||||
|
||||
|
||||
#ifdef ENABLE_DEBUGGER_SUPPORT
|
||||
DeoptimizedFrameInfo* Deoptimizer::DebuggerInspectableFrame(
|
||||
JavaScriptFrame* frame,
|
||||
int frame_index,
|
||||
int jsframe_index,
|
||||
Isolate* isolate) {
|
||||
ASSERT(isolate == Isolate::Current());
|
||||
ASSERT(frame->is_optimized());
|
||||
@ -143,22 +160,40 @@ DeoptimizedFrameInfo* Deoptimizer::DebuggerInspectableFrame(
|
||||
|
||||
// Create the GC safe output frame information and register it for GC
|
||||
// handling.
|
||||
ASSERT_LT(frame_index, deoptimizer->output_count());
|
||||
ASSERT_LT(jsframe_index, deoptimizer->jsframe_count());
|
||||
|
||||
// Convert JS frame index into frame index.
|
||||
int frame_index = deoptimizer->ConvertJSFrameIndexToFrameIndex(jsframe_index);
|
||||
|
||||
bool has_arguments_adaptor =
|
||||
frame_index > 0 &&
|
||||
deoptimizer->output_[frame_index - 1]->GetFrameType() ==
|
||||
StackFrame::ARGUMENTS_ADAPTOR;
|
||||
|
||||
DeoptimizedFrameInfo* info =
|
||||
new DeoptimizedFrameInfo(deoptimizer, frame_index);
|
||||
new DeoptimizedFrameInfo(deoptimizer, frame_index, has_arguments_adaptor);
|
||||
isolate->deoptimizer_data()->deoptimized_frame_info_ = info;
|
||||
|
||||
// Get the "simulated" top and size for the requested frame.
|
||||
Address top =
|
||||
reinterpret_cast<Address>(deoptimizer->output_[frame_index]->GetTop());
|
||||
uint32_t size = deoptimizer->output_[frame_index]->GetFrameSize();
|
||||
FrameDescription* parameters_frame =
|
||||
deoptimizer->output_[
|
||||
has_arguments_adaptor ? (frame_index - 1) : frame_index];
|
||||
|
||||
uint32_t parameters_size = (info->parameters_count() + 1) * kPointerSize;
|
||||
Address parameters_top = reinterpret_cast<Address>(
|
||||
parameters_frame->GetTop() + (parameters_frame->GetFrameSize() -
|
||||
parameters_size));
|
||||
|
||||
uint32_t expressions_size = info->expression_count() * kPointerSize;
|
||||
Address expressions_top = reinterpret_cast<Address>(
|
||||
deoptimizer->output_[frame_index]->GetTop());
|
||||
|
||||
// Done with the GC-unsafe frame descriptions. This re-enables allocation.
|
||||
deoptimizer->DeleteFrameDescriptions();
|
||||
|
||||
// Allocate a heap number for the doubles belonging to this frame.
|
||||
deoptimizer->MaterializeHeapNumbersForDebuggerInspectableFrame(
|
||||
top, size, info);
|
||||
parameters_top, parameters_size, expressions_top, expressions_size, info);
|
||||
|
||||
// Finished using the deoptimizer instance.
|
||||
delete deoptimizer;
|
||||
@ -313,6 +348,7 @@ Deoptimizer::Deoptimizer(Isolate* isolate,
|
||||
fp_to_sp_delta_(fp_to_sp_delta),
|
||||
input_(NULL),
|
||||
output_count_(0),
|
||||
jsframe_count_(0),
|
||||
output_(NULL),
|
||||
frame_alignment_marker_(isolate->heap()->frame_alignment_marker()),
|
||||
has_alignment_padding_(0),
|
||||
@ -377,9 +413,7 @@ Deoptimizer::Deoptimizer(Isolate* isolate,
|
||||
ASSERT(HEAP->allow_allocation(false));
|
||||
unsigned size = ComputeInputFrameSize();
|
||||
input_ = new(size) FrameDescription(size, function);
|
||||
#ifdef DEBUG
|
||||
input_->SetKind(Code::OPTIMIZED_FUNCTION);
|
||||
#endif
|
||||
input_->SetFrameType(StackFrame::JAVA_SCRIPT);
|
||||
}
|
||||
|
||||
|
||||
@ -515,6 +549,7 @@ void Deoptimizer::DoComputeOutputFrames() {
|
||||
// Read the number of output frames and allocate an array for their
|
||||
// descriptions.
|
||||
int count = iterator.Next();
|
||||
iterator.Next(); // Drop JS frames count.
|
||||
ASSERT(output_ == NULL);
|
||||
output_ = new FrameDescription*[count];
|
||||
for (int i = 0; i < count; ++i) {
|
||||
@ -524,7 +559,21 @@ void Deoptimizer::DoComputeOutputFrames() {
|
||||
|
||||
// Translate each output frame.
|
||||
for (int i = 0; i < count; ++i) {
|
||||
DoComputeFrame(&iterator, i);
|
||||
// Read the ast node id, function, and frame height for this output frame.
|
||||
Translation::Opcode opcode =
|
||||
static_cast<Translation::Opcode>(iterator.Next());
|
||||
switch (opcode) {
|
||||
case Translation::JS_FRAME:
|
||||
DoComputeJSFrame(&iterator, i);
|
||||
jsframe_count_++;
|
||||
break;
|
||||
case Translation::ARGUMENTS_ADAPTOR_FRAME:
|
||||
DoComputeArgumentsAdaptorFrame(&iterator, i);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Print some helpful diagnostic information.
|
||||
@ -565,39 +614,52 @@ void Deoptimizer::MaterializeHeapNumbers() {
|
||||
|
||||
#ifdef ENABLE_DEBUGGER_SUPPORT
|
||||
void Deoptimizer::MaterializeHeapNumbersForDebuggerInspectableFrame(
|
||||
Address top, uint32_t size, DeoptimizedFrameInfo* info) {
|
||||
Address parameters_top,
|
||||
uint32_t parameters_size,
|
||||
Address expressions_top,
|
||||
uint32_t expressions_size,
|
||||
DeoptimizedFrameInfo* info) {
|
||||
ASSERT_EQ(DEBUGGER, bailout_type_);
|
||||
Address parameters_bottom = parameters_top + parameters_size;
|
||||
Address expressions_bottom = expressions_top + expressions_size;
|
||||
for (int i = 0; i < deferred_heap_numbers_.length(); i++) {
|
||||
HeapNumberMaterializationDescriptor d = deferred_heap_numbers_[i];
|
||||
|
||||
// Check of the heap number to materialize actually belong to the frame
|
||||
// being extracted.
|
||||
Address slot = d.slot_address();
|
||||
if (top <= slot && slot < top + size) {
|
||||
if (parameters_top <= slot && slot < parameters_bottom) {
|
||||
Handle<Object> num = isolate_->factory()->NewNumber(d.value());
|
||||
// Calculate the index with the botton of the expression stack
|
||||
// at index 0, and the fixed part (including incoming arguments)
|
||||
// at negative indexes.
|
||||
int index = static_cast<int>(
|
||||
info->expression_count_ - (slot - top) / kPointerSize - 1);
|
||||
|
||||
int index = (info->parameters_count() - 1) -
|
||||
(slot - parameters_top) / kPointerSize;
|
||||
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF("Materializing a new heap number %p [%e] in slot %p"
|
||||
"for stack index %d\n",
|
||||
"for parameter slot #%d\n",
|
||||
reinterpret_cast<void*>(*num),
|
||||
d.value(),
|
||||
d.slot_address(),
|
||||
index);
|
||||
}
|
||||
if (index >=0) {
|
||||
info->SetExpression(index, *num);
|
||||
} else {
|
||||
// Calculate parameter index subtracting one for the receiver.
|
||||
int parameter_index =
|
||||
index +
|
||||
static_cast<int>(size) / kPointerSize -
|
||||
info->expression_count_ - 1;
|
||||
info->SetParameter(parameter_index, *num);
|
||||
|
||||
info->SetParameter(index, *num);
|
||||
} else if (expressions_top <= slot && slot < expressions_bottom) {
|
||||
Handle<Object> num = isolate_->factory()->NewNumber(d.value());
|
||||
|
||||
int index = info->expression_count() - 1 -
|
||||
(slot - expressions_top) / kPointerSize;
|
||||
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF("Materializing a new heap number %p [%e] in slot %p"
|
||||
"for expression slot #%d\n",
|
||||
reinterpret_cast<void*>(*num),
|
||||
d.value(),
|
||||
d.slot_address(),
|
||||
index);
|
||||
}
|
||||
|
||||
info->SetExpression(index, *num);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -622,7 +684,8 @@ void Deoptimizer::DoTranslateCommand(TranslationIterator* iterator,
|
||||
|
||||
switch (opcode) {
|
||||
case Translation::BEGIN:
|
||||
case Translation::FRAME:
|
||||
case Translation::JS_FRAME:
|
||||
case Translation::ARGUMENTS_ADAPTOR_FRAME:
|
||||
case Translation::DUPLICATE:
|
||||
UNREACHABLE();
|
||||
return;
|
||||
@ -691,7 +754,7 @@ void Deoptimizer::DoTranslateCommand(TranslationIterator* iterator,
|
||||
case Translation::STACK_SLOT: {
|
||||
int input_slot_index = iterator->Next();
|
||||
unsigned input_offset =
|
||||
input_->GetOffsetFromSlotIndex(this, input_slot_index);
|
||||
input_->GetOffsetFromSlotIndex(input_slot_index);
|
||||
intptr_t input_value = input_->GetFrameSlot(input_offset);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08" V8PRIxPTR ": ",
|
||||
@ -710,7 +773,7 @@ void Deoptimizer::DoTranslateCommand(TranslationIterator* iterator,
|
||||
case Translation::INT32_STACK_SLOT: {
|
||||
int input_slot_index = iterator->Next();
|
||||
unsigned input_offset =
|
||||
input_->GetOffsetFromSlotIndex(this, input_slot_index);
|
||||
input_->GetOffsetFromSlotIndex(input_slot_index);
|
||||
intptr_t value = input_->GetFrameSlot(input_offset);
|
||||
bool is_smi = Smi::IsValid(value);
|
||||
if (FLAG_trace_deopt) {
|
||||
@ -739,7 +802,7 @@ void Deoptimizer::DoTranslateCommand(TranslationIterator* iterator,
|
||||
case Translation::DOUBLE_STACK_SLOT: {
|
||||
int input_slot_index = iterator->Next();
|
||||
unsigned input_offset =
|
||||
input_->GetOffsetFromSlotIndex(this, input_slot_index);
|
||||
input_->GetOffsetFromSlotIndex(input_slot_index);
|
||||
double value = input_->GetDoubleFrameSlot(input_offset);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- %e ; [esp + %d]\n",
|
||||
@ -808,7 +871,8 @@ bool Deoptimizer::DoOsrTranslateCommand(TranslationIterator* iterator,
|
||||
|
||||
switch (opcode) {
|
||||
case Translation::BEGIN:
|
||||
case Translation::FRAME:
|
||||
case Translation::JS_FRAME:
|
||||
case Translation::ARGUMENTS_ADAPTOR_FRAME:
|
||||
case Translation::DUPLICATE:
|
||||
UNREACHABLE(); // Malformed input.
|
||||
return false;
|
||||
@ -871,7 +935,7 @@ bool Deoptimizer::DoOsrTranslateCommand(TranslationIterator* iterator,
|
||||
case Translation::STACK_SLOT: {
|
||||
int output_index = iterator->Next();
|
||||
unsigned output_offset =
|
||||
output->GetOffsetFromSlotIndex(this, output_index);
|
||||
output->GetOffsetFromSlotIndex(output_index);
|
||||
if (FLAG_trace_osr) {
|
||||
PrintF(" [sp + %d] <- 0x%08" V8PRIxPTR " ; [sp + %d] ",
|
||||
output_offset,
|
||||
@ -890,7 +954,7 @@ bool Deoptimizer::DoOsrTranslateCommand(TranslationIterator* iterator,
|
||||
|
||||
int output_index = iterator->Next();
|
||||
unsigned output_offset =
|
||||
output->GetOffsetFromSlotIndex(this, output_index);
|
||||
output->GetOffsetFromSlotIndex(output_index);
|
||||
int int32_value = input_object->IsSmi()
|
||||
? Smi::cast(input_object)->value()
|
||||
: DoubleToInt32(input_object->Number());
|
||||
@ -922,7 +986,7 @@ bool Deoptimizer::DoOsrTranslateCommand(TranslationIterator* iterator,
|
||||
|
||||
int output_index = iterator->Next();
|
||||
unsigned output_offset =
|
||||
output->GetOffsetFromSlotIndex(this, output_index);
|
||||
output->GetOffsetFromSlotIndex(output_index);
|
||||
double double_value = input_object->Number();
|
||||
uint64_t int_value = BitCast<uint64_t, double>(double_value);
|
||||
int32_t lower = static_cast<int32_t>(int_value);
|
||||
@ -1033,8 +1097,8 @@ unsigned Deoptimizer::ComputeInputFrameSize() const {
|
||||
unsigned Deoptimizer::ComputeFixedSize(JSFunction* function) const {
|
||||
// The fixed part of the frame consists of the return address, frame
|
||||
// pointer, function, context, and all the incoming arguments.
|
||||
static const unsigned kFixedSlotSize = 4 * kPointerSize;
|
||||
return ComputeIncomingArgumentSize(function) + kFixedSlotSize;
|
||||
return ComputeIncomingArgumentSize(function) +
|
||||
StandardFrameConstants::kFixedFrameSize;
|
||||
}
|
||||
|
||||
|
||||
@ -1154,49 +1218,62 @@ FrameDescription::FrameDescription(uint32_t frame_size,
|
||||
}
|
||||
|
||||
|
||||
unsigned FrameDescription::GetOffsetFromSlotIndex(Deoptimizer* deoptimizer,
|
||||
int slot_index) {
|
||||
int FrameDescription::ComputeFixedSize() {
|
||||
return StandardFrameConstants::kFixedFrameSize +
|
||||
(ComputeParametersCount() + 1) * kPointerSize;
|
||||
}
|
||||
|
||||
|
||||
unsigned FrameDescription::GetOffsetFromSlotIndex(int slot_index) {
|
||||
if (slot_index >= 0) {
|
||||
// Local or spill slots. Skip the fixed part of the frame
|
||||
// including all arguments.
|
||||
unsigned base =
|
||||
GetFrameSize() - deoptimizer->ComputeFixedSize(GetFunction());
|
||||
unsigned base = GetFrameSize() - ComputeFixedSize();
|
||||
return base - ((slot_index + 1) * kPointerSize);
|
||||
} else {
|
||||
// Incoming parameter.
|
||||
unsigned base = GetFrameSize() -
|
||||
deoptimizer->ComputeIncomingArgumentSize(GetFunction());
|
||||
int arg_size = (ComputeParametersCount() + 1) * kPointerSize;
|
||||
unsigned base = GetFrameSize() - arg_size;
|
||||
return base - ((slot_index + 1) * kPointerSize);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int FrameDescription::ComputeParametersCount() {
|
||||
switch (type_) {
|
||||
case StackFrame::JAVA_SCRIPT:
|
||||
return function_->shared()->formal_parameter_count();
|
||||
case StackFrame::ARGUMENTS_ADAPTOR: {
|
||||
// Last slot contains number of incomming arguments as a smi.
|
||||
// Can't use GetExpression(0) because it would cause infinite recursion.
|
||||
return reinterpret_cast<Smi*>(*GetFrameSlotPointer(0))->value();
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Object* FrameDescription::GetParameter(Deoptimizer* deoptimizer, int index) {
|
||||
ASSERT_EQ(Code::FUNCTION, kind_);
|
||||
Object* FrameDescription::GetParameter(int index) {
|
||||
ASSERT(index >= 0);
|
||||
ASSERT(index < ComputeParametersCount());
|
||||
// The slot indexes for incoming arguments are negative.
|
||||
unsigned offset = GetOffsetFromSlotIndex(deoptimizer,
|
||||
index - ComputeParametersCount());
|
||||
unsigned offset = GetOffsetFromSlotIndex(index - ComputeParametersCount());
|
||||
return reinterpret_cast<Object*>(*GetFrameSlotPointer(offset));
|
||||
}
|
||||
|
||||
|
||||
unsigned FrameDescription::GetExpressionCount(Deoptimizer* deoptimizer) {
|
||||
ASSERT_EQ(Code::FUNCTION, kind_);
|
||||
unsigned size = GetFrameSize() - deoptimizer->ComputeFixedSize(GetFunction());
|
||||
unsigned FrameDescription::GetExpressionCount() {
|
||||
ASSERT_EQ(StackFrame::JAVA_SCRIPT, type_);
|
||||
unsigned size = GetFrameSize() - ComputeFixedSize();
|
||||
return size / kPointerSize;
|
||||
}
|
||||
|
||||
|
||||
Object* FrameDescription::GetExpression(Deoptimizer* deoptimizer, int index) {
|
||||
ASSERT_EQ(Code::FUNCTION, kind_);
|
||||
unsigned offset = GetOffsetFromSlotIndex(deoptimizer, index);
|
||||
Object* FrameDescription::GetExpression(int index) {
|
||||
ASSERT_EQ(StackFrame::JAVA_SCRIPT, type_);
|
||||
unsigned offset = GetOffsetFromSlotIndex(index);
|
||||
return reinterpret_cast<Object*>(*GetFrameSlotPointer(offset));
|
||||
}
|
||||
|
||||
@ -1242,8 +1319,15 @@ Handle<ByteArray> TranslationBuffer::CreateByteArray() {
|
||||
}
|
||||
|
||||
|
||||
void Translation::BeginFrame(int node_id, int literal_id, unsigned height) {
|
||||
buffer_->Add(FRAME);
|
||||
void Translation::BeginArgumentsAdaptorFrame(int literal_id, unsigned height) {
|
||||
buffer_->Add(ARGUMENTS_ADAPTOR_FRAME);
|
||||
buffer_->Add(literal_id);
|
||||
buffer_->Add(height);
|
||||
}
|
||||
|
||||
|
||||
void Translation::BeginJSFrame(int node_id, int literal_id, unsigned height) {
|
||||
buffer_->Add(JS_FRAME);
|
||||
buffer_->Add(node_id);
|
||||
buffer_->Add(literal_id);
|
||||
buffer_->Add(height);
|
||||
@ -1307,7 +1391,6 @@ int Translation::NumberOfOperandsFor(Opcode opcode) {
|
||||
case ARGUMENTS_OBJECT:
|
||||
case DUPLICATE:
|
||||
return 0;
|
||||
case BEGIN:
|
||||
case REGISTER:
|
||||
case INT32_REGISTER:
|
||||
case DOUBLE_REGISTER:
|
||||
@ -1316,7 +1399,10 @@ int Translation::NumberOfOperandsFor(Opcode opcode) {
|
||||
case DOUBLE_STACK_SLOT:
|
||||
case LITERAL:
|
||||
return 1;
|
||||
case FRAME:
|
||||
case BEGIN:
|
||||
case ARGUMENTS_ADAPTOR_FRAME:
|
||||
return 2;
|
||||
case JS_FRAME:
|
||||
return 3;
|
||||
}
|
||||
UNREACHABLE();
|
||||
@ -1330,8 +1416,10 @@ const char* Translation::StringFor(Opcode opcode) {
|
||||
switch (opcode) {
|
||||
case BEGIN:
|
||||
return "BEGIN";
|
||||
case FRAME:
|
||||
return "FRAME";
|
||||
case JS_FRAME:
|
||||
return "JS_FRAME";
|
||||
case ARGUMENTS_ADAPTOR_FRAME:
|
||||
return "ARGUMENTS_ADAPTOR_FRAME";
|
||||
case REGISTER:
|
||||
return "REGISTER";
|
||||
case INT32_REGISTER:
|
||||
@ -1385,7 +1473,8 @@ SlotRef SlotRef::ComputeSlotForNextArgument(TranslationIterator* iterator,
|
||||
|
||||
switch (opcode) {
|
||||
case Translation::BEGIN:
|
||||
case Translation::FRAME:
|
||||
case Translation::JS_FRAME:
|
||||
case Translation::ARGUMENTS_ADAPTOR_FRAME:
|
||||
// Peeled off before getting here.
|
||||
break;
|
||||
|
||||
@ -1431,9 +1520,27 @@ SlotRef SlotRef::ComputeSlotForNextArgument(TranslationIterator* iterator,
|
||||
}
|
||||
|
||||
|
||||
void SlotRef::ComputeSlotMappingForArguments(JavaScriptFrame* frame,
|
||||
int inlined_frame_index,
|
||||
Vector<SlotRef>* args_slots) {
|
||||
void SlotRef::ComputeSlotsForArguments(Vector<SlotRef>* args_slots,
|
||||
TranslationIterator* it,
|
||||
DeoptimizationInputData* data,
|
||||
JavaScriptFrame* frame) {
|
||||
// Process the translation commands for the arguments.
|
||||
|
||||
// Skip the translation command for the receiver.
|
||||
it->Skip(Translation::NumberOfOperandsFor(
|
||||
static_cast<Translation::Opcode>(it->Next())));
|
||||
|
||||
// Compute slots for arguments.
|
||||
for (int i = 0; i < args_slots->length(); ++i) {
|
||||
(*args_slots)[i] = ComputeSlotForNextArgument(it, data, frame);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vector<SlotRef> SlotRef::ComputeSlotMappingForArguments(
|
||||
JavaScriptFrame* frame,
|
||||
int inlined_jsframe_index,
|
||||
int formal_parameter_count) {
|
||||
AssertNoAllocation no_gc;
|
||||
int deopt_index = AstNode::kNoNumber;
|
||||
DeoptimizationInputData* data =
|
||||
@ -1442,51 +1549,73 @@ void SlotRef::ComputeSlotMappingForArguments(JavaScriptFrame* frame,
|
||||
data->TranslationIndex(deopt_index)->value());
|
||||
Translation::Opcode opcode = static_cast<Translation::Opcode>(it.Next());
|
||||
ASSERT(opcode == Translation::BEGIN);
|
||||
int frame_count = it.Next();
|
||||
USE(frame_count);
|
||||
ASSERT(frame_count > inlined_frame_index);
|
||||
int frames_to_skip = inlined_frame_index;
|
||||
it.Next(); // Drop frame count.
|
||||
int jsframe_count = it.Next();
|
||||
USE(jsframe_count);
|
||||
ASSERT(jsframe_count > inlined_jsframe_index);
|
||||
int jsframes_to_skip = inlined_jsframe_index;
|
||||
while (true) {
|
||||
opcode = static_cast<Translation::Opcode>(it.Next());
|
||||
if (opcode == Translation::ARGUMENTS_ADAPTOR_FRAME) {
|
||||
if (jsframes_to_skip == 0) {
|
||||
ASSERT(Translation::NumberOfOperandsFor(opcode) == 2);
|
||||
|
||||
it.Skip(1); // literal id
|
||||
int height = it.Next();
|
||||
|
||||
// We reached the arguments adaptor frame corresponding to the
|
||||
// inlined function in question. Number of arguments is height - 1.
|
||||
Vector<SlotRef> args_slots =
|
||||
Vector<SlotRef>::New(height - 1); // Minus receiver.
|
||||
ComputeSlotsForArguments(&args_slots, &it, data, frame);
|
||||
return args_slots;
|
||||
}
|
||||
} else if (opcode == Translation::JS_FRAME) {
|
||||
if (jsframes_to_skip == 0) {
|
||||
// Skip over operands to advance to the next opcode.
|
||||
it.Skip(Translation::NumberOfOperandsFor(opcode));
|
||||
if (opcode == Translation::FRAME) {
|
||||
if (frames_to_skip == 0) {
|
||||
|
||||
// We reached the frame corresponding to the inlined function
|
||||
// in question. Process the translation commands for the
|
||||
// arguments.
|
||||
//
|
||||
// Skip the translation command for the receiver.
|
||||
it.Skip(Translation::NumberOfOperandsFor(
|
||||
static_cast<Translation::Opcode>(it.Next())));
|
||||
// Compute slots for arguments.
|
||||
for (int i = 0; i < args_slots->length(); ++i) {
|
||||
(*args_slots)[i] = ComputeSlotForNextArgument(&it, data, frame);
|
||||
// arguments. Number of arguments is equal to the number of
|
||||
// format parameter count.
|
||||
Vector<SlotRef> args_slots =
|
||||
Vector<SlotRef>::New(formal_parameter_count);
|
||||
ComputeSlotsForArguments(&args_slots, &it, data, frame);
|
||||
return args_slots;
|
||||
}
|
||||
return;
|
||||
}
|
||||
frames_to_skip--;
|
||||
jsframes_to_skip--;
|
||||
}
|
||||
|
||||
// Skip over operands to advance to the next opcode.
|
||||
it.Skip(Translation::NumberOfOperandsFor(opcode));
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
return Vector<SlotRef>();
|
||||
}
|
||||
|
||||
#ifdef ENABLE_DEBUGGER_SUPPORT
|
||||
|
||||
DeoptimizedFrameInfo::DeoptimizedFrameInfo(
|
||||
Deoptimizer* deoptimizer, int frame_index) {
|
||||
Deoptimizer* deoptimizer, int frame_index, bool has_arguments_adaptor) {
|
||||
FrameDescription* output_frame = deoptimizer->output_[frame_index];
|
||||
SetFunction(output_frame->GetFunction());
|
||||
expression_count_ = output_frame->GetExpressionCount(deoptimizer);
|
||||
expression_count_ = output_frame->GetExpressionCount();
|
||||
expression_stack_ = new Object*[expression_count_];
|
||||
for (int i = 0; i < expression_count_; i++) {
|
||||
SetExpression(i, output_frame->GetExpression(i));
|
||||
}
|
||||
|
||||
if (has_arguments_adaptor) {
|
||||
output_frame = deoptimizer->output_[frame_index - 1];
|
||||
ASSERT(output_frame->GetFrameType() == StackFrame::ARGUMENTS_ADAPTOR);
|
||||
}
|
||||
|
||||
parameters_count_ = output_frame->ComputeParametersCount();
|
||||
parameters_ = new Object*[parameters_count_];
|
||||
for (int i = 0; i < parameters_count_; i++) {
|
||||
SetParameter(i, output_frame->GetParameter(deoptimizer, i));
|
||||
}
|
||||
expression_stack_ = new Object*[expression_count_];
|
||||
for (int i = 0; i < expression_count_; i++) {
|
||||
SetExpression(i, output_frame->GetExpression(deoptimizer, i));
|
||||
SetParameter(i, output_frame->GetParameter(i));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2011 the V8 project authors. All rights reserved.
|
||||
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
@ -119,6 +119,9 @@ class Deoptimizer : public Malloced {
|
||||
|
||||
int output_count() const { return output_count_; }
|
||||
|
||||
// Number of created JS frames. Not all created frames are necessarily JS.
|
||||
int jsframe_count() const { return jsframe_count_; }
|
||||
|
||||
static Deoptimizer* New(JSFunction* function,
|
||||
BailoutType type,
|
||||
unsigned bailout_id,
|
||||
@ -131,7 +134,7 @@ class Deoptimizer : public Malloced {
|
||||
// The returned object with information on the optimized frame needs to be
|
||||
// freed before another one can be generated.
|
||||
static DeoptimizedFrameInfo* DebuggerInspectableFrame(JavaScriptFrame* frame,
|
||||
int frame_index,
|
||||
int jsframe_index,
|
||||
Isolate* isolate);
|
||||
static void DeleteDebuggerInspectableFrame(DeoptimizedFrameInfo* info,
|
||||
Isolate* isolate);
|
||||
@ -196,7 +199,11 @@ class Deoptimizer : public Malloced {
|
||||
void MaterializeHeapNumbers();
|
||||
#ifdef ENABLE_DEBUGGER_SUPPORT
|
||||
void MaterializeHeapNumbersForDebuggerInspectableFrame(
|
||||
Address top, uint32_t size, DeoptimizedFrameInfo* info);
|
||||
Address parameters_top,
|
||||
uint32_t parameters_size,
|
||||
Address expressions_top,
|
||||
uint32_t expressions_size,
|
||||
DeoptimizedFrameInfo* info);
|
||||
#endif
|
||||
|
||||
static void ComputeOutputFrames(Deoptimizer* deoptimizer);
|
||||
@ -257,6 +264,8 @@ class Deoptimizer : public Malloced {
|
||||
int count_;
|
||||
};
|
||||
|
||||
int ConvertJSFrameIndexToFrameIndex(int jsframe_index);
|
||||
|
||||
private:
|
||||
static const int kNumberOfEntries = 8192;
|
||||
|
||||
@ -271,7 +280,9 @@ class Deoptimizer : public Malloced {
|
||||
|
||||
void DoComputeOutputFrames();
|
||||
void DoComputeOsrOutputFrame();
|
||||
void DoComputeFrame(TranslationIterator* iterator, int frame_index);
|
||||
void DoComputeJSFrame(TranslationIterator* iterator, int frame_index);
|
||||
void DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator,
|
||||
int frame_index);
|
||||
void DoTranslateCommand(TranslationIterator* iterator,
|
||||
int frame_index,
|
||||
unsigned output_offset);
|
||||
@ -319,6 +330,8 @@ class Deoptimizer : public Malloced {
|
||||
FrameDescription* input_;
|
||||
// Number of output frames.
|
||||
int output_count_;
|
||||
// Number of output js frames.
|
||||
int jsframe_count_;
|
||||
// Array of output frame descriptions.
|
||||
FrameDescription** output_;
|
||||
|
||||
@ -362,7 +375,7 @@ class FrameDescription {
|
||||
|
||||
JSFunction* GetFunction() const { return function_; }
|
||||
|
||||
unsigned GetOffsetFromSlotIndex(Deoptimizer* deoptimizer, int slot_index);
|
||||
unsigned GetOffsetFromSlotIndex(int slot_index);
|
||||
|
||||
intptr_t GetFrameSlot(unsigned offset) {
|
||||
return *GetFrameSlotPointer(offset);
|
||||
@ -423,22 +436,20 @@ class FrameDescription {
|
||||
|
||||
void SetContinuation(intptr_t pc) { continuation_ = pc; }
|
||||
|
||||
#ifdef DEBUG
|
||||
Code::Kind GetKind() const { return kind_; }
|
||||
void SetKind(Code::Kind kind) { kind_ = kind; }
|
||||
#endif
|
||||
StackFrame::Type GetFrameType() const { return type_; }
|
||||
void SetFrameType(StackFrame::Type type) { type_ = type; }
|
||||
|
||||
// Get the incoming arguments count.
|
||||
int ComputeParametersCount();
|
||||
|
||||
// Get a parameter value for an unoptimized frame.
|
||||
Object* GetParameter(Deoptimizer* deoptimizer, int index);
|
||||
Object* GetParameter(int index);
|
||||
|
||||
// Get the expression stack height for a unoptimized frame.
|
||||
unsigned GetExpressionCount(Deoptimizer* deoptimizer);
|
||||
unsigned GetExpressionCount();
|
||||
|
||||
// Get the expression stack value for an unoptimized frame.
|
||||
Object* GetExpression(Deoptimizer* deoptimizer, int index);
|
||||
Object* GetExpression(int index);
|
||||
|
||||
static int registers_offset() {
|
||||
return OFFSET_OF(FrameDescription, registers_);
|
||||
@ -481,6 +492,7 @@ class FrameDescription {
|
||||
intptr_t top_;
|
||||
intptr_t pc_;
|
||||
intptr_t fp_;
|
||||
StackFrame::Type type_;
|
||||
Smi* state_;
|
||||
#ifdef DEBUG
|
||||
Code::Kind kind_;
|
||||
@ -499,6 +511,8 @@ class FrameDescription {
|
||||
return reinterpret_cast<intptr_t*>(
|
||||
reinterpret_cast<Address>(this) + frame_content_offset() + offset);
|
||||
}
|
||||
|
||||
int ComputeFixedSize();
|
||||
};
|
||||
|
||||
|
||||
@ -541,7 +555,8 @@ class Translation BASE_EMBEDDED {
|
||||
public:
|
||||
enum Opcode {
|
||||
BEGIN,
|
||||
FRAME,
|
||||
JS_FRAME,
|
||||
ARGUMENTS_ADAPTOR_FRAME,
|
||||
REGISTER,
|
||||
INT32_REGISTER,
|
||||
DOUBLE_REGISTER,
|
||||
@ -556,17 +571,19 @@ class Translation BASE_EMBEDDED {
|
||||
DUPLICATE
|
||||
};
|
||||
|
||||
Translation(TranslationBuffer* buffer, int frame_count)
|
||||
Translation(TranslationBuffer* buffer, int frame_count, int jsframe_count)
|
||||
: buffer_(buffer),
|
||||
index_(buffer->CurrentIndex()) {
|
||||
buffer_->Add(BEGIN);
|
||||
buffer_->Add(frame_count);
|
||||
buffer_->Add(jsframe_count);
|
||||
}
|
||||
|
||||
int index() const { return index_; }
|
||||
|
||||
// Commands.
|
||||
void BeginFrame(int node_id, int literal_id, unsigned height);
|
||||
void BeginJSFrame(int node_id, int literal_id, unsigned height);
|
||||
void BeginArgumentsAdaptorFrame(int literal_id, unsigned height);
|
||||
void StoreRegister(Register reg);
|
||||
void StoreInt32Register(Register reg);
|
||||
void StoreDoubleRegister(DoubleRegister reg);
|
||||
@ -656,9 +673,10 @@ class SlotRef BASE_EMBEDDED {
|
||||
}
|
||||
}
|
||||
|
||||
static void ComputeSlotMappingForArguments(JavaScriptFrame* frame,
|
||||
static Vector<SlotRef> ComputeSlotMappingForArguments(
|
||||
JavaScriptFrame* frame,
|
||||
int inlined_frame_index,
|
||||
Vector<SlotRef>* args_slots);
|
||||
int formal_parameter_count);
|
||||
|
||||
private:
|
||||
Address addr_;
|
||||
@ -678,6 +696,12 @@ class SlotRef BASE_EMBEDDED {
|
||||
static SlotRef ComputeSlotForNextArgument(TranslationIterator* iterator,
|
||||
DeoptimizationInputData* data,
|
||||
JavaScriptFrame* frame);
|
||||
|
||||
static void ComputeSlotsForArguments(
|
||||
Vector<SlotRef>* args_slots,
|
||||
TranslationIterator* iterator,
|
||||
DeoptimizationInputData* data,
|
||||
JavaScriptFrame* frame);
|
||||
};
|
||||
|
||||
|
||||
@ -686,9 +710,13 @@ class SlotRef BASE_EMBEDDED {
|
||||
// needs to inspect a frame that is part of an optimized frame. The
|
||||
// internally used FrameDescription objects are not GC safe so for use
|
||||
// by the debugger frame information is copied to an object of this type.
|
||||
// Represents parameters in unadapted form so their number might mismatch
|
||||
// formal parameter count.
|
||||
class DeoptimizedFrameInfo : public Malloced {
|
||||
public:
|
||||
DeoptimizedFrameInfo(Deoptimizer* deoptimizer, int frame_index);
|
||||
DeoptimizedFrameInfo(Deoptimizer* deoptimizer,
|
||||
int frame_index,
|
||||
bool has_arguments_adaptor);
|
||||
virtual ~DeoptimizedFrameInfo();
|
||||
|
||||
// GC support.
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2011 the V8 project authors. All rights reserved.
|
||||
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
@ -813,17 +813,18 @@ void OptimizedFrame::Summarize(List<FrameSummary>* frames) {
|
||||
data->TranslationIndex(deopt_index)->value());
|
||||
Translation::Opcode opcode = static_cast<Translation::Opcode>(it.Next());
|
||||
ASSERT(opcode == Translation::BEGIN);
|
||||
int frame_count = it.Next();
|
||||
it.Next(); // Drop frame count.
|
||||
int jsframe_count = it.Next();
|
||||
|
||||
// We create the summary in reverse order because the frames
|
||||
// in the deoptimization translation are ordered bottom-to-top.
|
||||
int i = frame_count;
|
||||
int i = jsframe_count;
|
||||
while (i > 0) {
|
||||
opcode = static_cast<Translation::Opcode>(it.Next());
|
||||
if (opcode == Translation::FRAME) {
|
||||
if (opcode == Translation::JS_FRAME) {
|
||||
// We don't inline constructor calls, so only the first, outermost
|
||||
// frame can be a constructor frame in case of inlining.
|
||||
bool is_constructor = (i == frame_count) && IsConstructor();
|
||||
bool is_constructor = (i == jsframe_count) && IsConstructor();
|
||||
|
||||
i--;
|
||||
int ast_id = it.Next();
|
||||
@ -918,8 +919,9 @@ int OptimizedFrame::GetInlineCount() {
|
||||
Translation::Opcode opcode = static_cast<Translation::Opcode>(it.Next());
|
||||
ASSERT(opcode == Translation::BEGIN);
|
||||
USE(opcode);
|
||||
int frame_count = it.Next();
|
||||
return frame_count;
|
||||
it.Next(); // Drop frame count.
|
||||
int jsframe_count = it.Next();
|
||||
return jsframe_count;
|
||||
}
|
||||
|
||||
|
||||
@ -934,14 +936,15 @@ void OptimizedFrame::GetFunctions(List<JSFunction*>* functions) {
|
||||
data->TranslationIndex(deopt_index)->value());
|
||||
Translation::Opcode opcode = static_cast<Translation::Opcode>(it.Next());
|
||||
ASSERT(opcode == Translation::BEGIN);
|
||||
int frame_count = it.Next();
|
||||
it.Next(); // Drop frame count.
|
||||
int jsframe_count = it.Next();
|
||||
|
||||
// We insert the frames in reverse order because the frames
|
||||
// in the deoptimization translation are ordered bottom-to-top.
|
||||
while (frame_count > 0) {
|
||||
while (jsframe_count > 0) {
|
||||
opcode = static_cast<Translation::Opcode>(it.Next());
|
||||
if (opcode == Translation::FRAME) {
|
||||
frame_count--;
|
||||
if (opcode == Translation::JS_FRAME) {
|
||||
jsframe_count--;
|
||||
it.Next(); // Skip ast id.
|
||||
int function_id = it.Next();
|
||||
it.Next(); // Skip height.
|
||||
|
@ -156,6 +156,7 @@ inline Heap* _inline_get_heap_();
|
||||
V(Script, empty_script, EmptyScript) \
|
||||
V(Smi, real_stack_limit, RealStackLimit) \
|
||||
V(StringDictionary, intrinsic_function_names, IntrinsicFunctionNames) \
|
||||
V(Smi, arguments_adaptor_deopt_pc_offset, ArgumentsAdaptorDeoptPCOffset)
|
||||
|
||||
#define ROOT_LIST(V) \
|
||||
STRONG_ROOT_LIST(V) \
|
||||
@ -1067,7 +1068,7 @@ class Heap {
|
||||
// Heap root getters. We have versions with and without type::cast() here.
|
||||
// You can't use type::cast during GC because the assert fails.
|
||||
// TODO(1490): Try removing the unchecked accessors, now that GC marking does
|
||||
// not corrupt the stack.
|
||||
// not corrupt the map.
|
||||
#define ROOT_ACCESSOR(type, name, camel_name) \
|
||||
type* name() { \
|
||||
return type::cast(roots_[k##camel_name##RootIndex]); \
|
||||
@ -1517,6 +1518,11 @@ class Heap {
|
||||
return seed;
|
||||
}
|
||||
|
||||
void SetArgumentsAdaptorDeoptPCOffset(int pc_offset) {
|
||||
ASSERT(arguments_adaptor_deopt_pc_offset() == Smi::FromInt(0));
|
||||
set_arguments_adaptor_deopt_pc_offset(Smi::FromInt(pc_offset));
|
||||
}
|
||||
|
||||
private:
|
||||
Heap();
|
||||
|
||||
|
@ -1340,9 +1340,11 @@ class HStackCheck: public HTemplateInstruction<1> {
|
||||
class HEnterInlined: public HTemplateInstruction<0> {
|
||||
public:
|
||||
HEnterInlined(Handle<JSFunction> closure,
|
||||
int arguments_count,
|
||||
FunctionLiteral* function,
|
||||
CallKind call_kind)
|
||||
: closure_(closure),
|
||||
arguments_count_(arguments_count),
|
||||
function_(function),
|
||||
call_kind_(call_kind) {
|
||||
}
|
||||
@ -1350,6 +1352,7 @@ class HEnterInlined: public HTemplateInstruction<0> {
|
||||
virtual void PrintDataTo(StringStream* stream);
|
||||
|
||||
Handle<JSFunction> closure() const { return closure_; }
|
||||
int arguments_count() const { return arguments_count_; }
|
||||
FunctionLiteral* function() const { return function_; }
|
||||
CallKind call_kind() const { return call_kind_; }
|
||||
|
||||
@ -1361,6 +1364,7 @@ class HEnterInlined: public HTemplateInstruction<0> {
|
||||
|
||||
private:
|
||||
Handle<JSFunction> closure_;
|
||||
int arguments_count_;
|
||||
FunctionLiteral* function_;
|
||||
CallKind call_kind_;
|
||||
};
|
||||
|
@ -167,8 +167,7 @@ void HBasicBlock::Finish(HControlInstruction* end) {
|
||||
void HBasicBlock::Goto(HBasicBlock* block, bool drop_extra) {
|
||||
if (block->IsInlineReturnTarget()) {
|
||||
AddInstruction(new(zone()) HLeaveInlined);
|
||||
last_environment_ = last_environment()->outer();
|
||||
if (drop_extra) last_environment_->Drop(1);
|
||||
last_environment_ = last_environment()->DiscardInlined(drop_extra);
|
||||
}
|
||||
AddSimulate(AstNode::kNoNumber);
|
||||
HGoto* instr = new(zone()) HGoto(block);
|
||||
@ -182,8 +181,7 @@ void HBasicBlock::AddLeaveInlined(HValue* return_value,
|
||||
ASSERT(target->IsInlineReturnTarget());
|
||||
ASSERT(return_value != NULL);
|
||||
AddInstruction(new(zone()) HLeaveInlined);
|
||||
last_environment_ = last_environment()->outer();
|
||||
if (drop_extra) last_environment_->Drop(1);
|
||||
last_environment_ = last_environment()->DiscardInlined(drop_extra);
|
||||
last_environment()->Push(return_value);
|
||||
AddSimulate(AstNode::kNoNumber);
|
||||
HGoto* instr = new(zone()) HGoto(target);
|
||||
@ -2076,6 +2074,7 @@ AstContext::AstContext(HGraphBuilder* owner, Expression::Context kind)
|
||||
for_typeof_(false) {
|
||||
owner->set_ast_context(this); // Push.
|
||||
#ifdef DEBUG
|
||||
ASSERT(!owner->environment()->is_arguments_adaptor());
|
||||
original_length_ = owner->environment()->length();
|
||||
#endif
|
||||
}
|
||||
@ -2089,14 +2088,16 @@ AstContext::~AstContext() {
|
||||
EffectContext::~EffectContext() {
|
||||
ASSERT(owner()->HasStackOverflow() ||
|
||||
owner()->current_block() == NULL ||
|
||||
owner()->environment()->length() == original_length_);
|
||||
(owner()->environment()->length() == original_length_ &&
|
||||
!owner()->environment()->is_arguments_adaptor()));
|
||||
}
|
||||
|
||||
|
||||
ValueContext::~ValueContext() {
|
||||
ASSERT(owner()->HasStackOverflow() ||
|
||||
owner()->current_block() == NULL ||
|
||||
owner()->environment()->length() == original_length_ + 1);
|
||||
(owner()->environment()->length() == original_length_ + 1 &&
|
||||
!owner()->environment()->is_arguments_adaptor()));
|
||||
}
|
||||
|
||||
|
||||
@ -4828,7 +4829,9 @@ bool HGraphBuilder::TryInline(Call* expr, bool drop_extra) {
|
||||
TraceInline(target, caller, "inline depth limit reached");
|
||||
return false;
|
||||
}
|
||||
if (!env->outer()->is_arguments_adaptor()) {
|
||||
current_level++;
|
||||
}
|
||||
env = env->outer();
|
||||
}
|
||||
|
||||
@ -4876,11 +4879,8 @@ bool HGraphBuilder::TryInline(Call* expr, bool drop_extra) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't inline functions that uses the arguments object or that
|
||||
// have a mismatching number of parameters.
|
||||
int arity = expr->arguments()->length();
|
||||
if (function->scope()->arguments() != NULL ||
|
||||
arity != target_shared->formal_parameter_count()) {
|
||||
// Don't inline functions that uses the arguments object.
|
||||
if (function->scope()->arguments() != NULL) {
|
||||
TraceInline(target, caller, "target requires special argument handling");
|
||||
return false;
|
||||
}
|
||||
@ -4944,6 +4944,7 @@ bool HGraphBuilder::TryInline(Call* expr, bool drop_extra) {
|
||||
HConstant* undefined = graph()->GetConstantUndefined();
|
||||
HEnvironment* inner_env =
|
||||
environment()->CopyForInlining(target,
|
||||
expr->arguments()->length(),
|
||||
function,
|
||||
undefined,
|
||||
call_kind);
|
||||
@ -4963,6 +4964,7 @@ bool HGraphBuilder::TryInline(Call* expr, bool drop_extra) {
|
||||
body_entry->SetJoinId(expr->ReturnId());
|
||||
set_current_block(body_entry);
|
||||
AddInstruction(new(zone()) HEnterInlined(target,
|
||||
expr->arguments()->length(),
|
||||
function,
|
||||
call_kind));
|
||||
VisitDeclarations(target_info.scope()->declarations());
|
||||
@ -6902,7 +6904,8 @@ HEnvironment::HEnvironment(HEnvironment* outer,
|
||||
outer_(outer),
|
||||
pop_count_(0),
|
||||
push_count_(0),
|
||||
ast_id_(AstNode::kNoNumber) {
|
||||
ast_id_(AstNode::kNoNumber),
|
||||
arguments_adaptor_(false) {
|
||||
Initialize(scope->num_parameters() + 1, scope->num_stack_slots(), 0);
|
||||
}
|
||||
|
||||
@ -6916,11 +6919,28 @@ HEnvironment::HEnvironment(const HEnvironment* other)
|
||||
outer_(NULL),
|
||||
pop_count_(0),
|
||||
push_count_(0),
|
||||
ast_id_(other->ast_id()) {
|
||||
ast_id_(other->ast_id()),
|
||||
arguments_adaptor_(false) {
|
||||
Initialize(other);
|
||||
}
|
||||
|
||||
|
||||
HEnvironment::HEnvironment(HEnvironment* outer,
|
||||
Handle<JSFunction> closure,
|
||||
int arguments)
|
||||
: closure_(closure),
|
||||
values_(arguments),
|
||||
assigned_variables_(0),
|
||||
parameter_count_(arguments),
|
||||
local_count_(0),
|
||||
outer_(outer),
|
||||
pop_count_(0),
|
||||
push_count_(0),
|
||||
ast_id_(AstNode::kNoNumber),
|
||||
arguments_adaptor_(true) {
|
||||
}
|
||||
|
||||
|
||||
void HEnvironment::Initialize(int parameter_count,
|
||||
int local_count,
|
||||
int stack_height) {
|
||||
@ -6944,6 +6964,7 @@ void HEnvironment::Initialize(const HEnvironment* other) {
|
||||
pop_count_ = other->pop_count_;
|
||||
push_count_ = other->push_count_;
|
||||
ast_id_ = other->ast_id_;
|
||||
arguments_adaptor_ = other->arguments_adaptor_;
|
||||
}
|
||||
|
||||
|
||||
@ -7047,20 +7068,36 @@ HEnvironment* HEnvironment::CopyAsLoopHeader(HBasicBlock* loop_header) const {
|
||||
|
||||
HEnvironment* HEnvironment::CopyForInlining(
|
||||
Handle<JSFunction> target,
|
||||
int arguments,
|
||||
FunctionLiteral* function,
|
||||
HConstant* undefined,
|
||||
CallKind call_kind) const {
|
||||
ASSERT(!is_arguments_adaptor());
|
||||
|
||||
Zone* zone = closure()->GetIsolate()->zone();
|
||||
|
||||
// Outer environment is a copy of this one without the arguments.
|
||||
int arity = function->scope()->num_parameters();
|
||||
|
||||
HEnvironment* outer = Copy();
|
||||
outer->Drop(arity + 1); // Including receiver.
|
||||
outer->Drop(arguments + 1); // Including receiver.
|
||||
outer->ClearHistory();
|
||||
Zone* zone = closure()->GetIsolate()->zone();
|
||||
|
||||
if (arity != arguments) {
|
||||
// Create artificial arguments adaptation environment.
|
||||
outer = new(zone) HEnvironment(outer, target, arguments + 1);
|
||||
for (int i = 0; i <= arguments; ++i) { // Include receiver.
|
||||
outer->Push(ExpressionStackAt(arguments - i));
|
||||
}
|
||||
outer->ClearHistory();
|
||||
}
|
||||
|
||||
HEnvironment* inner =
|
||||
new(zone) HEnvironment(outer, function->scope(), target);
|
||||
// Get the argument values from the original environment.
|
||||
for (int i = 0; i <= arity; ++i) { // Include receiver.
|
||||
HValue* push = ExpressionStackAt(arity - i);
|
||||
HValue* push = (i <= arguments) ?
|
||||
ExpressionStackAt(arguments - i) : undefined;
|
||||
inner->SetValueAt(i, push);
|
||||
}
|
||||
// If the function we are inlining is a strict mode function or a
|
||||
@ -7070,7 +7107,7 @@ HEnvironment* HEnvironment::CopyForInlining(
|
||||
call_kind == CALL_AS_FUNCTION) {
|
||||
inner->SetValueAt(0, undefined);
|
||||
}
|
||||
inner->SetValueAt(arity + 1, outer->LookupContext());
|
||||
inner->SetValueAt(arity + 1, LookupContext());
|
||||
for (int i = arity + 2; i < inner->length(); ++i) {
|
||||
inner->SetValueAt(i, undefined);
|
||||
}
|
||||
@ -7086,7 +7123,7 @@ void HEnvironment::PrintTo(StringStream* stream) {
|
||||
if (i == parameter_count()) stream->Add("specials\n");
|
||||
if (i == parameter_count() + specials_count()) stream->Add("locals\n");
|
||||
if (i == parameter_count() + specials_count() + local_count()) {
|
||||
stream->Add("expressions");
|
||||
stream->Add("expressions\n");
|
||||
}
|
||||
HValue* val = values_.at(i);
|
||||
stream->Add("%d: ", i);
|
||||
@ -7097,6 +7134,7 @@ void HEnvironment::PrintTo(StringStream* stream) {
|
||||
}
|
||||
stream->Add("\n");
|
||||
}
|
||||
PrintF("\n");
|
||||
}
|
||||
|
||||
|
||||
|
@ -343,6 +343,17 @@ class HEnvironment: public ZoneObject {
|
||||
Scope* scope,
|
||||
Handle<JSFunction> closure);
|
||||
|
||||
bool is_arguments_adaptor() const {
|
||||
return arguments_adaptor_;
|
||||
}
|
||||
|
||||
HEnvironment* DiscardInlined(bool drop_extra) {
|
||||
HEnvironment* outer = outer_->is_arguments_adaptor() ?
|
||||
outer_->outer_ : outer_;
|
||||
if (drop_extra) outer->Drop(1);
|
||||
return outer;
|
||||
}
|
||||
|
||||
// Simple accessors.
|
||||
Handle<JSFunction> closure() const { return closure_; }
|
||||
const ZoneList<HValue*>* values() const { return &values_; }
|
||||
@ -427,6 +438,7 @@ class HEnvironment: public ZoneObject {
|
||||
// environment is the outer environment but the top expression stack
|
||||
// elements are moved to an inner environment as parameters.
|
||||
HEnvironment* CopyForInlining(Handle<JSFunction> target,
|
||||
int arguments,
|
||||
FunctionLiteral* function,
|
||||
HConstant* undefined,
|
||||
CallKind call_kind) const;
|
||||
@ -450,6 +462,10 @@ class HEnvironment: public ZoneObject {
|
||||
private:
|
||||
explicit HEnvironment(const HEnvironment* other);
|
||||
|
||||
// Create an argument adaptor environment.
|
||||
HEnvironment(HEnvironment* outer, Handle<JSFunction> closure, int arguments);
|
||||
|
||||
|
||||
// True if index is included in the expression stack part of the environment.
|
||||
bool HasExpressionAt(int index) const;
|
||||
|
||||
@ -478,6 +494,7 @@ class HEnvironment: public ZoneObject {
|
||||
int pop_count_;
|
||||
int push_count_;
|
||||
int ast_id_;
|
||||
bool arguments_adaptor_;
|
||||
};
|
||||
|
||||
|
||||
|
@ -537,7 +537,7 @@ static void Generate_NotifyDeoptimizedHelper(MacroAssembler* masm,
|
||||
{
|
||||
FrameScope scope(masm, StackFrame::INTERNAL);
|
||||
|
||||
// Pass the function and deoptimization type to the runtime system.
|
||||
// Pass deoptimization type to the runtime system.
|
||||
__ push(Immediate(Smi::FromInt(static_cast<int>(type))));
|
||||
__ CallRuntime(Runtime::kNotifyDeoptimized, 1);
|
||||
|
||||
@ -1644,6 +1644,7 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
|
||||
__ mov(edi, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
|
||||
__ call(edx);
|
||||
|
||||
masm->isolate()->heap()->SetArgumentsAdaptorDeoptPCOffset(masm->pc_offset());
|
||||
// Leave frame and return.
|
||||
LeaveArgumentsAdaptorFrame(masm);
|
||||
__ ret(0);
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2011 the V8 project authors. All rights reserved.
|
||||
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
@ -299,12 +299,13 @@ void Deoptimizer::DoComputeOsrOutputFrame() {
|
||||
ASSERT(Translation::BEGIN == opcode);
|
||||
USE(opcode);
|
||||
int count = iterator.Next();
|
||||
iterator.Next(); // Drop JS frames count.
|
||||
ASSERT(count == 1);
|
||||
USE(count);
|
||||
|
||||
opcode = static_cast<Translation::Opcode>(iterator.Next());
|
||||
USE(opcode);
|
||||
ASSERT(Translation::FRAME == opcode);
|
||||
ASSERT(Translation::JS_FRAME == opcode);
|
||||
unsigned node_id = iterator.Next();
|
||||
USE(node_id);
|
||||
ASSERT(node_id == ast_id);
|
||||
@ -340,9 +341,7 @@ void Deoptimizer::DoComputeOsrOutputFrame() {
|
||||
output_ = new FrameDescription*[1];
|
||||
output_[0] = new(output_frame_size) FrameDescription(
|
||||
output_frame_size, function_);
|
||||
#ifdef DEBUG
|
||||
output_[0]->SetKind(Code::OPTIMIZED_FUNCTION);
|
||||
#endif
|
||||
output_[0]->SetFrameType(StackFrame::JAVA_SCRIPT);
|
||||
|
||||
// Clear the incoming parameters in the optimized frame to avoid
|
||||
// confusing the garbage collector.
|
||||
@ -437,13 +436,112 @@ void Deoptimizer::DoComputeOsrOutputFrame() {
|
||||
}
|
||||
|
||||
|
||||
void Deoptimizer::DoComputeFrame(TranslationIterator* iterator,
|
||||
void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator,
|
||||
int frame_index) {
|
||||
JSFunction* function = JSFunction::cast(ComputeLiteral(iterator->Next()));
|
||||
unsigned height = iterator->Next();
|
||||
unsigned height_in_bytes = height * kPointerSize;
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" translating arguments adaptor => height=%d\n", height_in_bytes);
|
||||
}
|
||||
|
||||
unsigned fixed_frame_size = ArgumentsAdaptorFrameConstants::kFrameSize;
|
||||
unsigned input_frame_size = input_->GetFrameSize();
|
||||
unsigned output_frame_size = height_in_bytes + fixed_frame_size;
|
||||
|
||||
// Allocate and store the output frame description.
|
||||
FrameDescription* output_frame =
|
||||
new(output_frame_size) FrameDescription(output_frame_size, function);
|
||||
output_frame->SetFrameType(StackFrame::ARGUMENTS_ADAPTOR);
|
||||
|
||||
// Arguments adaptor can not be topmost or bottommost.
|
||||
ASSERT(frame_index > 0 && frame_index < output_count_ - 1);
|
||||
ASSERT(output_[frame_index] == NULL);
|
||||
output_[frame_index] = output_frame;
|
||||
|
||||
// The top address of the frame is computed from the previous
|
||||
// frame's top and this frame's size.
|
||||
uint32_t top_address;
|
||||
top_address = output_[frame_index - 1]->GetTop() - output_frame_size;
|
||||
output_frame->SetTop(top_address);
|
||||
|
||||
// Compute the incoming parameter translation.
|
||||
int parameter_count = height;
|
||||
unsigned output_offset = output_frame_size;
|
||||
unsigned input_offset = input_frame_size;
|
||||
for (int i = 0; i < parameter_count; ++i) {
|
||||
output_offset -= kPointerSize;
|
||||
DoTranslateCommand(iterator, frame_index, output_offset);
|
||||
}
|
||||
input_offset -= (parameter_count * kPointerSize);
|
||||
|
||||
// Read caller's PC from the previous frame.
|
||||
output_offset -= kPointerSize;
|
||||
input_offset -= kPointerSize;
|
||||
intptr_t callers_pc = output_[frame_index - 1]->GetPc();
|
||||
output_frame->SetFrameSlot(output_offset, callers_pc);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08x: [top + %d] <- 0x%08x ; caller's pc\n",
|
||||
top_address + output_offset, output_offset, callers_pc);
|
||||
}
|
||||
|
||||
// Read caller's FP from the previous frame, and set this frame's FP.
|
||||
output_offset -= kPointerSize;
|
||||
input_offset -= kPointerSize;
|
||||
intptr_t value = output_[frame_index - 1]->GetFp();
|
||||
output_frame->SetFrameSlot(output_offset, value);
|
||||
intptr_t fp_value = top_address + output_offset;
|
||||
output_frame->SetFp(fp_value);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08x: [top + %d] <- 0x%08x ; caller's fp\n",
|
||||
fp_value, output_offset, value);
|
||||
}
|
||||
|
||||
// A marker value is used in place of the context.
|
||||
output_offset -= kPointerSize;
|
||||
input_offset -= kPointerSize;
|
||||
intptr_t context = reinterpret_cast<intptr_t>(
|
||||
Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
|
||||
output_frame->SetFrameSlot(output_offset, context);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08x: [top + %d] <- 0x%08x ; context (adaptor sentinel)\n",
|
||||
top_address + output_offset, output_offset, context);
|
||||
}
|
||||
|
||||
// The function was mentioned explicitly in the ARGUMENTS_ADAPTOR_FRAME.
|
||||
output_offset -= kPointerSize;
|
||||
input_offset -= kPointerSize;
|
||||
value = reinterpret_cast<intptr_t>(function);
|
||||
output_frame->SetFrameSlot(output_offset, value);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08x: [top + %d] <- 0x%08x ; function\n",
|
||||
top_address + output_offset, output_offset, value);
|
||||
}
|
||||
|
||||
// Number of incoming arguments.
|
||||
output_offset -= kPointerSize;
|
||||
input_offset -= kPointerSize;
|
||||
value = reinterpret_cast<uint32_t>(Smi::FromInt(height - 1));
|
||||
output_frame->SetFrameSlot(output_offset, value);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08x: [top + %d] <- 0x%08x ; argc (%d)\n",
|
||||
top_address + output_offset, output_offset, value, height - 1);
|
||||
}
|
||||
|
||||
ASSERT(0 == output_offset);
|
||||
|
||||
Builtins* builtins = isolate_->builtins();
|
||||
Code* adaptor_trampoline =
|
||||
builtins->builtin(Builtins::kArgumentsAdaptorTrampoline);
|
||||
uint32_t pc = reinterpret_cast<uint32_t>(
|
||||
adaptor_trampoline->instruction_start() +
|
||||
isolate_->heap()->arguments_adaptor_deopt_pc_offset()->value());
|
||||
output_frame->SetPc(pc);
|
||||
}
|
||||
|
||||
|
||||
void Deoptimizer::DoComputeJSFrame(TranslationIterator* iterator,
|
||||
int frame_index) {
|
||||
// Read the ast node id, function, and frame height for this output frame.
|
||||
Translation::Opcode opcode =
|
||||
static_cast<Translation::Opcode>(iterator->Next());
|
||||
USE(opcode);
|
||||
ASSERT(Translation::FRAME == opcode);
|
||||
int node_id = iterator->Next();
|
||||
JSFunction* function = JSFunction::cast(ComputeLiteral(iterator->Next()));
|
||||
unsigned height = iterator->Next();
|
||||
@ -463,9 +561,7 @@ void Deoptimizer::DoComputeFrame(TranslationIterator* iterator,
|
||||
// Allocate and store the output frame description.
|
||||
FrameDescription* output_frame =
|
||||
new(output_frame_size) FrameDescription(output_frame_size, function);
|
||||
#ifdef DEBUG
|
||||
output_frame->SetKind(Code::FUNCTION);
|
||||
#endif
|
||||
output_frame->SetFrameType(StackFrame::JAVA_SCRIPT);
|
||||
|
||||
bool is_bottommost = (0 == frame_index);
|
||||
bool is_topmost = (output_count_ - 1 == frame_index);
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2011 the V8 project authors. All rights reserved.
|
||||
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
@ -95,9 +95,11 @@ class ExitFrameConstants : public AllStatic {
|
||||
|
||||
class StandardFrameConstants : public AllStatic {
|
||||
public:
|
||||
// Fixed part of the frame consists of return address, caller fp,
|
||||
// context and function.
|
||||
// StandardFrame::IterateExpressions assumes that kContextOffset is the last
|
||||
// object pointer.
|
||||
static const int kFixedFrameSize = 4; // Currently unused.
|
||||
static const int kFixedFrameSize = 4 * kPointerSize;
|
||||
static const int kExpressionsOffset = -3 * kPointerSize;
|
||||
static const int kMarkerOffset = -2 * kPointerSize;
|
||||
static const int kContextOffset = -1 * kPointerSize;
|
||||
@ -123,6 +125,8 @@ class JavaScriptFrameConstants : public AllStatic {
|
||||
class ArgumentsAdaptorFrameConstants : public AllStatic {
|
||||
public:
|
||||
static const int kLengthOffset = StandardFrameConstants::kExpressionsOffset;
|
||||
static const int kFrameSize =
|
||||
StandardFrameConstants::kFixedFrameSize + kPointerSize;
|
||||
};
|
||||
|
||||
|
||||
|
@ -394,7 +394,11 @@ void LCodeGen::WriteTranslation(LEnvironment* environment,
|
||||
|
||||
WriteTranslation(environment->outer(), translation);
|
||||
int closure_id = DefineDeoptimizationLiteral(environment->closure());
|
||||
translation->BeginFrame(environment->ast_id(), closure_id, height);
|
||||
if (environment->is_arguments_adaptor()) {
|
||||
translation->BeginArgumentsAdaptorFrame(closure_id, translation_size);
|
||||
} else {
|
||||
translation->BeginJSFrame(environment->ast_id(), closure_id, height);
|
||||
}
|
||||
for (int i = 0; i < translation_size; ++i) {
|
||||
LOperand* value = environment->values()->at(i);
|
||||
// spilled_registers_ and spilled_double_registers_ are either
|
||||
@ -543,10 +547,14 @@ void LCodeGen::RegisterEnvironmentForDeoptimization(
|
||||
// |>------------ translation_size ------------<|
|
||||
|
||||
int frame_count = 0;
|
||||
int jsframe_count = 0;
|
||||
for (LEnvironment* e = environment; e != NULL; e = e->outer()) {
|
||||
++frame_count;
|
||||
if (!e->is_arguments_adaptor()) {
|
||||
++jsframe_count;
|
||||
}
|
||||
Translation translation(&translations_, frame_count);
|
||||
}
|
||||
Translation translation(&translations_, frame_count, jsframe_count);
|
||||
WriteTranslation(environment, &translation);
|
||||
int deoptimization_index = deoptimizations_.length();
|
||||
int pc_offset = masm()->pc_offset();
|
||||
|
@ -1013,15 +1013,17 @@ LEnvironment* LChunkBuilder::CreateEnvironment(
|
||||
LEnvironment* outer =
|
||||
CreateEnvironment(hydrogen_env->outer(), argument_index_accumulator);
|
||||
int ast_id = hydrogen_env->ast_id();
|
||||
ASSERT(ast_id != AstNode::kNoNumber);
|
||||
ASSERT(ast_id != AstNode::kNoNumber || hydrogen_env->is_arguments_adaptor());
|
||||
int value_count = hydrogen_env->length();
|
||||
LEnvironment* result =
|
||||
new(zone()) LEnvironment(hydrogen_env->closure(),
|
||||
hydrogen_env->is_arguments_adaptor(),
|
||||
ast_id,
|
||||
hydrogen_env->parameter_count(),
|
||||
argument_count_,
|
||||
value_count,
|
||||
outer);
|
||||
int argument_index = *argument_index_accumulator;
|
||||
for (int i = 0; i < value_count; ++i) {
|
||||
if (hydrogen_env->is_special_index(i)) continue;
|
||||
|
||||
@ -1030,13 +1032,17 @@ LEnvironment* LChunkBuilder::CreateEnvironment(
|
||||
if (value->IsArgumentsObject()) {
|
||||
op = NULL;
|
||||
} else if (value->IsPushArgument()) {
|
||||
op = new(zone()) LArgument((*argument_index_accumulator)++);
|
||||
op = new(zone()) LArgument(argument_index++);
|
||||
} else {
|
||||
op = UseAny(value);
|
||||
}
|
||||
result->AddValue(op, value->representation());
|
||||
}
|
||||
|
||||
if (!hydrogen_env->is_arguments_adaptor()) {
|
||||
*argument_index_accumulator = argument_index;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -2380,6 +2386,7 @@ LInstruction* LChunkBuilder::DoEnterInlined(HEnterInlined* instr) {
|
||||
HEnvironment* outer = current_block_->last_environment();
|
||||
HConstant* undefined = graph()->GetConstantUndefined();
|
||||
HEnvironment* inner = outer->CopyForInlining(instr->closure(),
|
||||
instr->arguments_count(),
|
||||
instr->function(),
|
||||
undefined,
|
||||
instr->call_kind());
|
||||
@ -2390,7 +2397,8 @@ LInstruction* LChunkBuilder::DoEnterInlined(HEnterInlined* instr) {
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoLeaveInlined(HLeaveInlined* instr) {
|
||||
HEnvironment* outer = current_block_->last_environment()->outer();
|
||||
HEnvironment* outer = current_block_->last_environment()->
|
||||
DiscardInlined(false);
|
||||
current_block_->UpdateEnvironment(outer);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -439,12 +439,14 @@ class LPointerMap: public ZoneObject {
|
||||
class LEnvironment: public ZoneObject {
|
||||
public:
|
||||
LEnvironment(Handle<JSFunction> closure,
|
||||
bool is_arguments_adaptor,
|
||||
int ast_id,
|
||||
int parameter_count,
|
||||
int argument_count,
|
||||
int value_count,
|
||||
LEnvironment* outer)
|
||||
: closure_(closure),
|
||||
is_arguments_adaptor_(is_arguments_adaptor),
|
||||
arguments_stack_height_(argument_count),
|
||||
deoptimization_index_(Safepoint::kNoDeoptimizationIndex),
|
||||
translation_index_(-1),
|
||||
@ -501,8 +503,11 @@ class LEnvironment: public ZoneObject {
|
||||
|
||||
void PrintTo(StringStream* stream);
|
||||
|
||||
bool is_arguments_adaptor() const { return is_arguments_adaptor_; }
|
||||
|
||||
private:
|
||||
Handle<JSFunction> closure_;
|
||||
bool is_arguments_adaptor_;
|
||||
int arguments_stack_height_;
|
||||
int deoptimization_index_;
|
||||
int translation_index_;
|
||||
|
@ -8113,8 +8113,11 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint(FILE* out) {
|
||||
static_cast<Translation::Opcode>(iterator.Next());
|
||||
ASSERT(Translation::BEGIN == opcode);
|
||||
int frame_count = iterator.Next();
|
||||
PrintF(out, " %s {count=%d}\n", Translation::StringFor(opcode),
|
||||
frame_count);
|
||||
int jsframe_count = iterator.Next();
|
||||
PrintF(out, " %s {frame count=%d, js frame count=%d}\n",
|
||||
Translation::StringFor(opcode),
|
||||
frame_count,
|
||||
jsframe_count);
|
||||
|
||||
while (iterator.HasNext() &&
|
||||
Translation::BEGIN !=
|
||||
@ -8126,7 +8129,7 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint(FILE* out) {
|
||||
UNREACHABLE();
|
||||
break;
|
||||
|
||||
case Translation::FRAME: {
|
||||
case Translation::JS_FRAME: {
|
||||
int ast_id = iterator.Next();
|
||||
int function_id = iterator.Next();
|
||||
JSFunction* function =
|
||||
@ -8138,6 +8141,12 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint(FILE* out) {
|
||||
break;
|
||||
}
|
||||
|
||||
case Translation::ARGUMENTS_ADAPTOR_FRAME: {
|
||||
unsigned height = iterator.Next();
|
||||
PrintF(out, "{arguments adaptor, height=%d}", height);
|
||||
break;
|
||||
}
|
||||
|
||||
case Translation::DUPLICATE:
|
||||
break;
|
||||
|
||||
|
124
src/runtime.cc
124
src/runtime.cc
@ -8126,13 +8126,15 @@ static SmartArrayPointer<Handle<Object> > GetCallerArguments(
|
||||
List<JSFunction*> functions(2);
|
||||
frame->GetFunctions(&functions);
|
||||
if (functions.length() > 1) {
|
||||
int inlined_frame_index = functions.length() - 1;
|
||||
JSFunction* inlined_function = functions[inlined_frame_index];
|
||||
int args_count = inlined_function->shared()->formal_parameter_count();
|
||||
ScopedVector<SlotRef> args_slots(args_count);
|
||||
SlotRef::ComputeSlotMappingForArguments(frame,
|
||||
inlined_frame_index,
|
||||
&args_slots);
|
||||
int inlined_jsframe_index = functions.length() - 1;
|
||||
JSFunction* inlined_function = functions[inlined_jsframe_index];
|
||||
Vector<SlotRef> args_slots =
|
||||
SlotRef::ComputeSlotMappingForArguments(
|
||||
frame,
|
||||
inlined_jsframe_index,
|
||||
inlined_function->shared()->formal_parameter_count());
|
||||
|
||||
int args_count = args_slots.length();
|
||||
|
||||
*total_argc = prefix_argc + args_count;
|
||||
SmartArrayPointer<Handle<Object> > param_data(
|
||||
@ -8141,6 +8143,9 @@ static SmartArrayPointer<Handle<Object> > GetCallerArguments(
|
||||
Handle<Object> val = args_slots[i].GetValue();
|
||||
param_data[prefix_argc + i] = val;
|
||||
}
|
||||
|
||||
args_slots.Dispose();
|
||||
|
||||
return param_data;
|
||||
} else {
|
||||
it.AdvanceToArgumentsFrame();
|
||||
@ -8486,14 +8491,14 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_NotifyDeoptimized) {
|
||||
static_cast<Deoptimizer::BailoutType>(args.smi_at(0));
|
||||
Deoptimizer* deoptimizer = Deoptimizer::Grab(isolate);
|
||||
ASSERT(isolate->heap()->IsAllocationAllowed());
|
||||
int frames = deoptimizer->output_count();
|
||||
int jsframes = deoptimizer->jsframe_count();
|
||||
|
||||
deoptimizer->MaterializeHeapNumbers();
|
||||
delete deoptimizer;
|
||||
|
||||
JavaScriptFrameIterator it(isolate);
|
||||
JavaScriptFrame* frame = NULL;
|
||||
for (int i = 0; i < frames - 1; i++) it.Advance();
|
||||
for (int i = 0; i < jsframes - 1; i++) it.Advance();
|
||||
frame = it.frame();
|
||||
|
||||
RUNTIME_ASSERT(frame->function()->IsJSFunction());
|
||||
@ -10703,13 +10708,13 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameCount) {
|
||||
class FrameInspector {
|
||||
public:
|
||||
FrameInspector(JavaScriptFrame* frame,
|
||||
int inlined_frame_index,
|
||||
int inlined_jsframe_index,
|
||||
Isolate* isolate)
|
||||
: frame_(frame), deoptimized_frame_(NULL), isolate_(isolate) {
|
||||
// Calculate the deoptimized frame.
|
||||
if (frame->is_optimized()) {
|
||||
deoptimized_frame_ = Deoptimizer::DebuggerInspectableFrame(
|
||||
frame, inlined_frame_index, isolate);
|
||||
frame, inlined_jsframe_index, isolate);
|
||||
}
|
||||
has_adapted_arguments_ = frame_->has_adapted_arguments();
|
||||
is_optimized_ = frame_->is_optimized();
|
||||
@ -10825,8 +10830,6 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameDetails) {
|
||||
return heap->undefined_value();
|
||||
}
|
||||
|
||||
int inlined_frame_index = 0; // Inlined frame index in optimized frame.
|
||||
|
||||
int count = 0;
|
||||
JavaScriptFrameIterator it(isolate, id);
|
||||
for (; !it.done(); it.Advance()) {
|
||||
@ -10835,11 +10838,14 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameDetails) {
|
||||
}
|
||||
if (it.done()) return heap->undefined_value();
|
||||
|
||||
if (it.frame()->is_optimized()) {
|
||||
inlined_frame_index =
|
||||
bool is_optimized = it.frame()->is_optimized();
|
||||
|
||||
int inlined_jsframe_index = 0; // Inlined frame index in optimized frame.
|
||||
if (is_optimized) {
|
||||
inlined_jsframe_index =
|
||||
it.frame()->GetInlineCount() - (index - count) - 1;
|
||||
}
|
||||
FrameInspector frame_inspector(it.frame(), inlined_frame_index, isolate);
|
||||
FrameInspector frame_inspector(it.frame(), inlined_jsframe_index, isolate);
|
||||
|
||||
// Traverse the saved contexts chain to find the active context for the
|
||||
// selected frame.
|
||||
@ -10853,12 +10859,11 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameDetails) {
|
||||
it.frame()->LookupCode()->SourcePosition(it.frame()->pc());
|
||||
|
||||
// Check for constructor frame. Inlined frames cannot be construct calls.
|
||||
bool inlined_frame =
|
||||
it.frame()->is_optimized() && inlined_frame_index != 0;
|
||||
bool inlined_frame = is_optimized && inlined_jsframe_index != 0;
|
||||
bool constructor = !inlined_frame && it.frame()->IsConstructor();
|
||||
|
||||
// Get scope info and read from it for local variable information.
|
||||
Handle<JSFunction> function(JSFunction::cast(it.frame()->function()));
|
||||
Handle<JSFunction> function(JSFunction::cast(frame_inspector.GetFunction()));
|
||||
Handle<SharedFunctionInfo> shared(function->shared());
|
||||
Handle<ScopeInfo> scope_info(shared->scope_info());
|
||||
ASSERT(*scope_info != ScopeInfo::Empty());
|
||||
@ -10895,7 +10900,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameDetails) {
|
||||
// Check whether this frame is positioned at return. If not top
|
||||
// frame or if the frame is optimized it cannot be at a return.
|
||||
bool at_return = false;
|
||||
if (!it.frame()->is_optimized() && index == 0) {
|
||||
if (!is_optimized && index == 0) {
|
||||
at_return = isolate->debug()->IsBreakAtReturn(it.frame());
|
||||
}
|
||||
|
||||
@ -10935,7 +10940,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameDetails) {
|
||||
// the provided parameters whereas the function frame always have the number
|
||||
// of arguments matching the functions parameters. The rest of the
|
||||
// information (except for what is collected above) is the same.
|
||||
if (it.frame()->has_adapted_arguments()) {
|
||||
if ((inlined_jsframe_index == 0) && it.frame()->has_adapted_arguments()) {
|
||||
it.AdvanceToArgumentsFrame();
|
||||
frame_inspector.SetArgumentsFrame(it.frame());
|
||||
}
|
||||
@ -10946,11 +10951,6 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameDetails) {
|
||||
if (argument_count < frame_inspector.GetParametersCount()) {
|
||||
argument_count = frame_inspector.GetParametersCount();
|
||||
}
|
||||
#ifdef DEBUG
|
||||
if (it.frame()->is_optimized()) {
|
||||
ASSERT_EQ(argument_count, frame_inspector.GetParametersCount());
|
||||
}
|
||||
#endif
|
||||
|
||||
// Calculate the size of the result.
|
||||
int details_size = kFrameDetailsFirstDynamicIndex +
|
||||
@ -10992,9 +10992,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameDetails) {
|
||||
if (*save->context() == *isolate->debug()->debug_context()) {
|
||||
flags |= 1 << 0;
|
||||
}
|
||||
if (it.frame()->is_optimized()) {
|
||||
if (is_optimized) {
|
||||
flags |= 1 << 1;
|
||||
flags |= inlined_frame_index << 2;
|
||||
flags |= inlined_jsframe_index << 2;
|
||||
}
|
||||
details->set(kFrameDetailsFlagsIndex, Smi::FromInt(flags));
|
||||
|
||||
@ -11011,7 +11011,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameDetails) {
|
||||
}
|
||||
|
||||
// Parameter value.
|
||||
if (i < it.frame()->ComputeParametersCount()) {
|
||||
if (i < frame_inspector.GetParametersCount()) {
|
||||
// Get the value from the stack.
|
||||
details->set(details_index++, frame_inspector.GetParameter(i));
|
||||
} else {
|
||||
@ -11084,14 +11084,13 @@ static bool CopyContextLocalsToScopeObject(
|
||||
|
||||
// Create a plain JSObject which materializes the local scope for the specified
|
||||
// frame.
|
||||
static Handle<JSObject> MaterializeLocalScope(
|
||||
static Handle<JSObject> MaterializeLocalScopeWithFrameInspector(
|
||||
Isolate* isolate,
|
||||
JavaScriptFrame* frame,
|
||||
int inlined_frame_index) {
|
||||
Handle<JSFunction> function(JSFunction::cast(frame->function()));
|
||||
FrameInspector* frame_inspector) {
|
||||
Handle<JSFunction> function(JSFunction::cast(frame_inspector->GetFunction()));
|
||||
Handle<SharedFunctionInfo> shared(function->shared());
|
||||
Handle<ScopeInfo> scope_info(shared->scope_info());
|
||||
FrameInspector frame_inspector(frame, inlined_frame_index, isolate);
|
||||
|
||||
// Allocate and initialize a JSObject with all the arguments, stack locals
|
||||
// heap locals and extension properties of the debugged function.
|
||||
@ -11100,11 +11099,15 @@ static Handle<JSObject> MaterializeLocalScope(
|
||||
|
||||
// First fill all parameters.
|
||||
for (int i = 0; i < scope_info->ParameterCount(); ++i) {
|
||||
Handle<Object> value(
|
||||
i < frame_inspector->GetParametersCount() ?
|
||||
frame_inspector->GetParameter(i) : isolate->heap()->undefined_value());
|
||||
|
||||
RETURN_IF_EMPTY_HANDLE_VALUE(
|
||||
isolate,
|
||||
SetProperty(local_scope,
|
||||
Handle<String>(scope_info->ParameterName(i)),
|
||||
Handle<Object>(frame_inspector.GetParameter(i)),
|
||||
value,
|
||||
NONE,
|
||||
kNonStrictMode),
|
||||
Handle<JSObject>());
|
||||
@ -11116,7 +11119,7 @@ static Handle<JSObject> MaterializeLocalScope(
|
||||
isolate,
|
||||
SetProperty(local_scope,
|
||||
Handle<String>(scope_info->StackLocalName(i)),
|
||||
Handle<Object>(frame_inspector.GetExpression(i)),
|
||||
Handle<Object>(frame_inspector->GetExpression(i)),
|
||||
NONE,
|
||||
kNonStrictMode),
|
||||
Handle<JSObject>());
|
||||
@ -11163,6 +11166,17 @@ static Handle<JSObject> MaterializeLocalScope(
|
||||
}
|
||||
|
||||
|
||||
static Handle<JSObject> MaterializeLocalScope(
|
||||
Isolate* isolate,
|
||||
JavaScriptFrame* frame,
|
||||
int inlined_jsframe_index) {
|
||||
FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate);
|
||||
return MaterializeLocalScopeWithFrameInspector(isolate,
|
||||
frame,
|
||||
&frame_inspector);
|
||||
}
|
||||
|
||||
|
||||
// Create a plain JSObject which materializes the closure content for the
|
||||
// context.
|
||||
static Handle<JSObject> MaterializeClosure(Isolate* isolate,
|
||||
@ -11268,10 +11282,10 @@ class ScopeIterator {
|
||||
|
||||
ScopeIterator(Isolate* isolate,
|
||||
JavaScriptFrame* frame,
|
||||
int inlined_frame_index)
|
||||
int inlined_jsframe_index)
|
||||
: isolate_(isolate),
|
||||
frame_(frame),
|
||||
inlined_frame_index_(inlined_frame_index),
|
||||
inlined_jsframe_index_(inlined_jsframe_index),
|
||||
function_(JSFunction::cast(frame->function())),
|
||||
context_(Context::cast(frame->context())),
|
||||
nested_scope_chain_(4) {
|
||||
@ -11428,7 +11442,7 @@ class ScopeIterator {
|
||||
case ScopeIterator::ScopeTypeLocal:
|
||||
// Materialize the content of the local scope into a JSObject.
|
||||
ASSERT(nested_scope_chain_.length() == 1);
|
||||
return MaterializeLocalScope(isolate_, frame_, inlined_frame_index_);
|
||||
return MaterializeLocalScope(isolate_, frame_, inlined_jsframe_index_);
|
||||
case ScopeIterator::ScopeTypeWith:
|
||||
// Return the with object.
|
||||
return Handle<JSObject>(JSObject::cast(CurrentContext()->extension()));
|
||||
@ -11524,7 +11538,7 @@ class ScopeIterator {
|
||||
private:
|
||||
Isolate* isolate_;
|
||||
JavaScriptFrame* frame_;
|
||||
int inlined_frame_index_;
|
||||
int inlined_jsframe_index_;
|
||||
Handle<JSFunction> function_;
|
||||
Handle<Context> context_;
|
||||
List<Handle<ScopeInfo> > nested_scope_chain_;
|
||||
@ -11586,7 +11600,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetScopeDetails) {
|
||||
if (!maybe_check->ToObject(&check)) return maybe_check;
|
||||
}
|
||||
CONVERT_CHECKED(Smi, wrapped_id, args[1]);
|
||||
CONVERT_NUMBER_CHECKED(int, inlined_frame_index, Int32, args[2]);
|
||||
CONVERT_NUMBER_CHECKED(int, inlined_jsframe_index, Int32, args[2]);
|
||||
CONVERT_NUMBER_CHECKED(int, index, Int32, args[3]);
|
||||
|
||||
// Get the frame where the debugging is performed.
|
||||
@ -11596,7 +11610,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetScopeDetails) {
|
||||
|
||||
// Find the requested scope.
|
||||
int n = 0;
|
||||
ScopeIterator it(isolate, frame, inlined_frame_index);
|
||||
ScopeIterator it(isolate, frame, inlined_jsframe_index);
|
||||
for (; !it.Done() && n < index; it.Next()) {
|
||||
n++;
|
||||
}
|
||||
@ -11994,12 +12008,12 @@ static Handle<Context> CopyNestedScopeContextChain(Isolate* isolate,
|
||||
Handle<JSFunction> function,
|
||||
Handle<Context> base,
|
||||
JavaScriptFrame* frame,
|
||||
int inlined_frame_index) {
|
||||
int inlined_jsframe_index) {
|
||||
HandleScope scope(isolate);
|
||||
List<Handle<ScopeInfo> > scope_chain;
|
||||
List<Handle<Context> > context_chain;
|
||||
|
||||
ScopeIterator it(isolate, frame, inlined_frame_index);
|
||||
ScopeIterator it(isolate, frame, inlined_jsframe_index);
|
||||
for (; it.Type() != ScopeIterator::ScopeTypeGlobal &&
|
||||
it.Type() != ScopeIterator::ScopeTypeLocal ; it.Next()) {
|
||||
ASSERT(!it.Done());
|
||||
@ -12056,8 +12070,7 @@ static Handle<Context> CopyNestedScopeContextChain(Isolate* isolate,
|
||||
// Runtime_DebugEvaluate.
|
||||
static Handle<Object> GetArgumentsObject(Isolate* isolate,
|
||||
JavaScriptFrame* frame,
|
||||
int inlined_frame_index,
|
||||
Handle<JSFunction> function,
|
||||
FrameInspector* frame_inspector,
|
||||
Handle<ScopeInfo> scope_info,
|
||||
Handle<Context> function_context) {
|
||||
// Try to find the value of 'arguments' to pass as parameter. If it is not
|
||||
@ -12081,9 +12094,8 @@ static Handle<Object> GetArgumentsObject(Isolate* isolate,
|
||||
}
|
||||
}
|
||||
|
||||
FrameInspector frame_inspector(frame, inlined_frame_index, isolate);
|
||||
|
||||
int length = frame_inspector.GetParametersCount();
|
||||
Handle<JSFunction> function(JSFunction::cast(frame_inspector->GetFunction()));
|
||||
int length = frame_inspector->GetParametersCount();
|
||||
Handle<JSObject> arguments =
|
||||
isolate->factory()->NewArgumentsObject(function, length);
|
||||
Handle<FixedArray> array = isolate->factory()->NewFixedArray(length);
|
||||
@ -12091,7 +12103,7 @@ static Handle<Object> GetArgumentsObject(Isolate* isolate,
|
||||
AssertNoAllocation no_gc;
|
||||
WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
|
||||
for (int i = 0; i < length; i++) {
|
||||
array->set(i, frame_inspector.GetParameter(i), mode);
|
||||
array->set(i, frame_inspector->GetParameter(i), mode);
|
||||
}
|
||||
arguments->set_elements(*array);
|
||||
return arguments;
|
||||
@ -12127,7 +12139,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugEvaluate) {
|
||||
}
|
||||
}
|
||||
CONVERT_CHECKED(Smi, wrapped_id, args[1]);
|
||||
CONVERT_NUMBER_CHECKED(int, inlined_frame_index, Int32, args[2]);
|
||||
CONVERT_NUMBER_CHECKED(int, inlined_jsframe_index, Int32, args[2]);
|
||||
CONVERT_ARG_CHECKED(String, source, 3);
|
||||
CONVERT_BOOLEAN_CHECKED(disable_break, args[4]);
|
||||
Handle<Object> additional_context(args[5]);
|
||||
@ -12139,7 +12151,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugEvaluate) {
|
||||
StackFrame::Id id = UnwrapFrameId(wrapped_id);
|
||||
JavaScriptFrameIterator it(isolate, id);
|
||||
JavaScriptFrame* frame = it.frame();
|
||||
Handle<JSFunction> function(JSFunction::cast(frame->function()));
|
||||
FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate);
|
||||
Handle<JSFunction> function(JSFunction::cast(frame_inspector.GetFunction()));
|
||||
Handle<ScopeInfo> scope_info(function->shared()->scope_info());
|
||||
|
||||
// Traverse the saved contexts chain to find the active context for the
|
||||
@ -12166,8 +12179,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugEvaluate) {
|
||||
#endif
|
||||
|
||||
// Materialize the content of the local scope into a JSObject.
|
||||
Handle<JSObject> local_scope = MaterializeLocalScope(
|
||||
isolate, frame, inlined_frame_index);
|
||||
Handle<JSObject> local_scope = MaterializeLocalScopeWithFrameInspector(
|
||||
isolate, frame, &frame_inspector);
|
||||
RETURN_IF_EMPTY_HANDLE(isolate, local_scope);
|
||||
|
||||
// Allocate a new context for the debug evaluation and set the extension
|
||||
@ -12187,7 +12200,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugEvaluate) {
|
||||
go_between,
|
||||
context,
|
||||
frame,
|
||||
inlined_frame_index);
|
||||
inlined_jsframe_index);
|
||||
|
||||
if (additional_context->IsJSObject()) {
|
||||
Handle<JSObject> extension = Handle<JSObject>::cast(additional_context);
|
||||
@ -12227,8 +12240,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugEvaluate) {
|
||||
|
||||
Handle<Object> arguments = GetArgumentsObject(isolate,
|
||||
frame,
|
||||
inlined_frame_index,
|
||||
function,
|
||||
&frame_inspector,
|
||||
scope_info,
|
||||
function_context);
|
||||
|
||||
|
@ -1547,6 +1547,7 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
|
||||
__ bind(&invoke);
|
||||
__ call(rdx);
|
||||
|
||||
masm->isolate()->heap()->SetArgumentsAdaptorDeoptPCOffset(masm->pc_offset());
|
||||
// Leave frame and return.
|
||||
LeaveArgumentsAdaptorFrame(masm);
|
||||
__ ret(0);
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2011 the V8 project authors. All rights reserved.
|
||||
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
@ -206,12 +206,13 @@ void Deoptimizer::DoComputeOsrOutputFrame() {
|
||||
ASSERT(Translation::BEGIN == opcode);
|
||||
USE(opcode);
|
||||
int count = iterator.Next();
|
||||
iterator.Skip(1); // Drop JS frame count.
|
||||
ASSERT(count == 1);
|
||||
USE(count);
|
||||
|
||||
opcode = static_cast<Translation::Opcode>(iterator.Next());
|
||||
USE(opcode);
|
||||
ASSERT(Translation::FRAME == opcode);
|
||||
ASSERT(Translation::JS_FRAME == opcode);
|
||||
unsigned node_id = iterator.Next();
|
||||
USE(node_id);
|
||||
ASSERT(node_id == ast_id);
|
||||
@ -247,9 +248,7 @@ void Deoptimizer::DoComputeOsrOutputFrame() {
|
||||
output_ = new FrameDescription*[1];
|
||||
output_[0] = new(output_frame_size) FrameDescription(
|
||||
output_frame_size, function_);
|
||||
#ifdef DEBUG
|
||||
output_[0]->SetKind(Code::OPTIMIZED_FUNCTION);
|
||||
#endif
|
||||
output_[0]->SetFrameType(StackFrame::JAVA_SCRIPT);
|
||||
|
||||
// Clear the incoming parameters in the optimized frame to avoid
|
||||
// confusing the garbage collector.
|
||||
@ -338,13 +337,117 @@ void Deoptimizer::DoComputeOsrOutputFrame() {
|
||||
}
|
||||
|
||||
|
||||
void Deoptimizer::DoComputeFrame(TranslationIterator* iterator,
|
||||
void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator,
|
||||
int frame_index) {
|
||||
JSFunction* function = JSFunction::cast(ComputeLiteral(iterator->Next()));
|
||||
unsigned height = iterator->Next();
|
||||
unsigned height_in_bytes = height * kPointerSize;
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" translating arguments adaptor => height=%d\n", height_in_bytes);
|
||||
}
|
||||
|
||||
unsigned fixed_frame_size = ArgumentsAdaptorFrameConstants::kFrameSize;
|
||||
unsigned input_frame_size = input_->GetFrameSize();
|
||||
unsigned output_frame_size = height_in_bytes + fixed_frame_size;
|
||||
|
||||
// Allocate and store the output frame description.
|
||||
FrameDescription* output_frame =
|
||||
new(output_frame_size) FrameDescription(output_frame_size, function);
|
||||
output_frame->SetFrameType(StackFrame::ARGUMENTS_ADAPTOR);
|
||||
|
||||
// Arguments adaptor can not be topmost or bottommost.
|
||||
ASSERT(frame_index > 0 && frame_index < output_count_ - 1);
|
||||
ASSERT(output_[frame_index] == NULL);
|
||||
output_[frame_index] = output_frame;
|
||||
|
||||
// The top address of the frame is computed from the previous
|
||||
// frame's top and this frame's size.
|
||||
intptr_t top_address;
|
||||
top_address = output_[frame_index - 1]->GetTop() - output_frame_size;
|
||||
output_frame->SetTop(top_address);
|
||||
|
||||
// Compute the incoming parameter translation.
|
||||
int parameter_count = height;
|
||||
unsigned output_offset = output_frame_size;
|
||||
unsigned input_offset = input_frame_size;
|
||||
for (int i = 0; i < parameter_count; ++i) {
|
||||
output_offset -= kPointerSize;
|
||||
DoTranslateCommand(iterator, frame_index, output_offset);
|
||||
}
|
||||
input_offset -= (parameter_count * kPointerSize);
|
||||
|
||||
// Read caller's PC from the previous frame.
|
||||
output_offset -= kPointerSize;
|
||||
input_offset -= kPointerSize;
|
||||
intptr_t callers_pc = output_[frame_index - 1]->GetPc();
|
||||
output_frame->SetFrameSlot(output_offset, callers_pc);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
|
||||
V8PRIxPTR " ; caller's pc\n",
|
||||
top_address + output_offset, output_offset, callers_pc);
|
||||
}
|
||||
|
||||
// Read caller's FP from the previous frame, and set this frame's FP.
|
||||
output_offset -= kPointerSize;
|
||||
input_offset -= kPointerSize;
|
||||
intptr_t value = output_[frame_index - 1]->GetFp();
|
||||
output_frame->SetFrameSlot(output_offset, value);
|
||||
intptr_t fp_value = top_address + output_offset;
|
||||
output_frame->SetFp(fp_value);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
|
||||
V8PRIxPTR " ; caller's fp\n",
|
||||
fp_value, output_offset, value);
|
||||
}
|
||||
|
||||
// A marker value is used in place of the context.
|
||||
output_offset -= kPointerSize;
|
||||
input_offset -= kPointerSize;
|
||||
intptr_t context = reinterpret_cast<intptr_t>(
|
||||
Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
|
||||
output_frame->SetFrameSlot(output_offset, context);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
|
||||
V8PRIxPTR " ; context (adaptor sentinel)\n",
|
||||
top_address + output_offset, output_offset, context);
|
||||
}
|
||||
|
||||
// The function was mentioned explicitly in the ARGUMENTS_ADAPTOR_FRAME.
|
||||
output_offset -= kPointerSize;
|
||||
input_offset -= kPointerSize;
|
||||
value = reinterpret_cast<intptr_t>(function);
|
||||
output_frame->SetFrameSlot(output_offset, value);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
|
||||
V8PRIxPTR " ; function\n",
|
||||
top_address + output_offset, output_offset, value);
|
||||
}
|
||||
|
||||
// Number of incoming arguments.
|
||||
output_offset -= kPointerSize;
|
||||
input_offset -= kPointerSize;
|
||||
value = reinterpret_cast<intptr_t>(Smi::FromInt(height - 1));
|
||||
output_frame->SetFrameSlot(output_offset, value);
|
||||
if (FLAG_trace_deopt) {
|
||||
PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
|
||||
V8PRIxPTR " ; argc (%d)\n",
|
||||
top_address + output_offset, output_offset, value, height - 1);
|
||||
}
|
||||
|
||||
ASSERT(0 == output_offset);
|
||||
|
||||
Builtins* builtins = isolate_->builtins();
|
||||
Code* adaptor_trampoline =
|
||||
builtins->builtin(Builtins::kArgumentsAdaptorTrampoline);
|
||||
intptr_t pc_value = reinterpret_cast<intptr_t>(
|
||||
adaptor_trampoline->instruction_start() +
|
||||
isolate_->heap()->arguments_adaptor_deopt_pc_offset()->value());
|
||||
output_frame->SetPc(pc_value);
|
||||
}
|
||||
|
||||
|
||||
void Deoptimizer::DoComputeJSFrame(TranslationIterator* iterator,
|
||||
int frame_index) {
|
||||
// Read the ast node id, function, and frame height for this output frame.
|
||||
Translation::Opcode opcode =
|
||||
static_cast<Translation::Opcode>(iterator->Next());
|
||||
USE(opcode);
|
||||
ASSERT(Translation::FRAME == opcode);
|
||||
int node_id = iterator->Next();
|
||||
JSFunction* function = JSFunction::cast(ComputeLiteral(iterator->Next()));
|
||||
unsigned height = iterator->Next();
|
||||
@ -364,9 +467,7 @@ void Deoptimizer::DoComputeFrame(TranslationIterator* iterator,
|
||||
// Allocate and store the output frame description.
|
||||
FrameDescription* output_frame =
|
||||
new(output_frame_size) FrameDescription(output_frame_size, function);
|
||||
#ifdef DEBUG
|
||||
output_frame->SetKind(Code::FUNCTION);
|
||||
#endif
|
||||
output_frame->SetFrameType(StackFrame::JAVA_SCRIPT);
|
||||
|
||||
bool is_bottommost = (0 == frame_index);
|
||||
bool is_topmost = (output_count_ - 1 == frame_index);
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2011 the V8 project authors. All rights reserved.
|
||||
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
@ -87,6 +87,9 @@ class ExitFrameConstants : public AllStatic {
|
||||
|
||||
class StandardFrameConstants : public AllStatic {
|
||||
public:
|
||||
// Fixed part of the frame consists of return address, caller fp,
|
||||
// context and function.
|
||||
static const int kFixedFrameSize = 4 * kPointerSize;
|
||||
static const int kExpressionsOffset = -3 * kPointerSize;
|
||||
static const int kMarkerOffset = -2 * kPointerSize;
|
||||
static const int kContextOffset = -1 * kPointerSize;
|
||||
@ -112,6 +115,8 @@ class JavaScriptFrameConstants : public AllStatic {
|
||||
class ArgumentsAdaptorFrameConstants : public AllStatic {
|
||||
public:
|
||||
static const int kLengthOffset = StandardFrameConstants::kExpressionsOffset;
|
||||
static const int kFrameSize =
|
||||
StandardFrameConstants::kFixedFrameSize + kPointerSize;
|
||||
};
|
||||
|
||||
|
||||
|
@ -368,7 +368,11 @@ void LCodeGen::WriteTranslation(LEnvironment* environment,
|
||||
|
||||
WriteTranslation(environment->outer(), translation);
|
||||
int closure_id = DefineDeoptimizationLiteral(environment->closure());
|
||||
translation->BeginFrame(environment->ast_id(), closure_id, height);
|
||||
if (environment->is_arguments_adaptor()) {
|
||||
translation->BeginArgumentsAdaptorFrame(closure_id, translation_size);
|
||||
} else {
|
||||
translation->BeginJSFrame(environment->ast_id(), closure_id, height);
|
||||
}
|
||||
for (int i = 0; i < translation_size; ++i) {
|
||||
LOperand* value = environment->values()->at(i);
|
||||
// spilled_registers_ and spilled_double_registers_ are either
|
||||
@ -504,10 +508,14 @@ void LCodeGen::RegisterEnvironmentForDeoptimization(LEnvironment* environment,
|
||||
// |>------------ translation_size ------------<|
|
||||
|
||||
int frame_count = 0;
|
||||
int jsframe_count = 0;
|
||||
for (LEnvironment* e = environment; e != NULL; e = e->outer()) {
|
||||
++frame_count;
|
||||
if (!e->is_arguments_adaptor()) {
|
||||
++jsframe_count;
|
||||
}
|
||||
Translation translation(&translations_, frame_count);
|
||||
}
|
||||
Translation translation(&translations_, frame_count, jsframe_count);
|
||||
WriteTranslation(environment, &translation);
|
||||
int deoptimization_index = deoptimizations_.length();
|
||||
int pc_offset = masm()->pc_offset();
|
||||
|
@ -1000,14 +1000,16 @@ LEnvironment* LChunkBuilder::CreateEnvironment(
|
||||
LEnvironment* outer =
|
||||
CreateEnvironment(hydrogen_env->outer(), argument_index_accumulator);
|
||||
int ast_id = hydrogen_env->ast_id();
|
||||
ASSERT(ast_id != AstNode::kNoNumber);
|
||||
ASSERT(ast_id != AstNode::kNoNumber || hydrogen_env->is_arguments_adaptor());
|
||||
int value_count = hydrogen_env->length();
|
||||
LEnvironment* result = new LEnvironment(hydrogen_env->closure(),
|
||||
hydrogen_env->is_arguments_adaptor(),
|
||||
ast_id,
|
||||
hydrogen_env->parameter_count(),
|
||||
argument_count_,
|
||||
value_count,
|
||||
outer);
|
||||
int argument_index = *argument_index_accumulator;
|
||||
for (int i = 0; i < value_count; ++i) {
|
||||
if (hydrogen_env->is_special_index(i)) continue;
|
||||
|
||||
@ -1016,13 +1018,17 @@ LEnvironment* LChunkBuilder::CreateEnvironment(
|
||||
if (value->IsArgumentsObject()) {
|
||||
op = NULL;
|
||||
} else if (value->IsPushArgument()) {
|
||||
op = new LArgument((*argument_index_accumulator)++);
|
||||
op = new LArgument(argument_index++);
|
||||
} else {
|
||||
op = UseAny(value);
|
||||
}
|
||||
result->AddValue(op, value->representation());
|
||||
}
|
||||
|
||||
if (!hydrogen_env->is_arguments_adaptor()) {
|
||||
*argument_index_accumulator = argument_index;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -2245,6 +2251,7 @@ LInstruction* LChunkBuilder::DoEnterInlined(HEnterInlined* instr) {
|
||||
HEnvironment* outer = current_block_->last_environment();
|
||||
HConstant* undefined = graph()->GetConstantUndefined();
|
||||
HEnvironment* inner = outer->CopyForInlining(instr->closure(),
|
||||
instr->arguments_count(),
|
||||
instr->function(),
|
||||
undefined,
|
||||
instr->call_kind());
|
||||
@ -2255,7 +2262,8 @@ LInstruction* LChunkBuilder::DoEnterInlined(HEnterInlined* instr) {
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoLeaveInlined(HLeaveInlined* instr) {
|
||||
HEnvironment* outer = current_block_->last_environment()->outer();
|
||||
HEnvironment* outer = current_block_->last_environment()->
|
||||
DiscardInlined(false);
|
||||
current_block_->UpdateEnvironment(outer);
|
||||
return NULL;
|
||||
}
|
||||
|
62
test/mjsunit/compiler/inline-arity-mismatch.js
Normal file
62
test/mjsunit/compiler/inline-arity-mismatch.js
Normal file
@ -0,0 +1,62 @@
|
||||
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Flags: --allow-natives-syntax
|
||||
|
||||
// Test inlining at call sites with mismatched arity.
|
||||
|
||||
function f(a) {
|
||||
return a.x;
|
||||
}
|
||||
|
||||
function g(a, b) {
|
||||
return a.x;
|
||||
}
|
||||
|
||||
function h1(a, b) {
|
||||
return f(a, a) * g(b);
|
||||
}
|
||||
|
||||
function h2(a, b) {
|
||||
return f(a, a) * g(b);
|
||||
}
|
||||
|
||||
|
||||
var o = {x: 2};
|
||||
|
||||
assertEquals(4, h1(o, o));
|
||||
assertEquals(4, h1(o, o));
|
||||
assertEquals(4, h2(o, o));
|
||||
assertEquals(4, h2(o, o));
|
||||
%OptimizeFunctionOnNextCall(h1);
|
||||
%OptimizeFunctionOnNextCall(h2);
|
||||
assertEquals(4, h1(o, o));
|
||||
assertEquals(4, h2(o, o));
|
||||
|
||||
var u = {y:0, x:1};
|
||||
assertEquals(2, h1(u, o));
|
||||
assertEquals(2, h2(o, u));
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2010 the V8 project authors. All rights reserved.
|
||||
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
@ -25,6 +25,8 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Flags: --allow-natives-syntax
|
||||
|
||||
// Test function.arguments.
|
||||
|
||||
function A() {}
|
||||
@ -60,13 +62,16 @@ function hej(x) {
|
||||
return o.g(x, "z");
|
||||
}
|
||||
|
||||
function stress() {
|
||||
for (var i=0; i<5000000; i++) o.g(i, "g");
|
||||
for (var j=0; j<5000000; j++) hej(j);
|
||||
function opt() {
|
||||
for (var k=0; k<2; k++) {
|
||||
for (var i=0; i<5; i++) o.g(i, "g");
|
||||
for (var j=0; j<5; j++) hej(j);
|
||||
}
|
||||
%OptimizeFunctionOnNextCall(o.g);
|
||||
%OptimizeFunctionOnNextCall(hej);
|
||||
}
|
||||
|
||||
stress();
|
||||
|
||||
opt();
|
||||
assertArrayEquals([0, "g"], o.g(0, "g"));
|
||||
assertArrayEquals([1, "f"], o.g(1, "g"));
|
||||
assertArrayEquals([0, "h"], hej(0));
|
||||
@ -74,8 +79,7 @@ assertArrayEquals([1, "f"], hej(1));
|
||||
|
||||
o = new B();
|
||||
|
||||
stress();
|
||||
|
||||
opt();
|
||||
assertArrayEquals([0, "f"], o.g(0, "g"));
|
||||
assertArrayEquals([1, "g"], o.g(1, "g"));
|
||||
assertArrayEquals([0, "f"], hej(0));
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2008 the V8 project authors. All rights reserved.
|
||||
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
@ -34,6 +34,26 @@ var exception = false;
|
||||
|
||||
var testingConstructCall = false;
|
||||
|
||||
var input = [
|
||||
{a: 1, b: 2},
|
||||
{a: 3, b: 4},
|
||||
{a: 5, b: 6},
|
||||
{a: 7, b: 8},
|
||||
{a: 9, b: 10}
|
||||
];
|
||||
|
||||
var expected = [
|
||||
{ locals: {a0: 1.01, b0: 2.02}, args: { names: ["i", "x0", "y0"], values: [0, 3.03, 4.04] } },
|
||||
{ locals: {a1: 3.03, b1: 4.04}, args: { names: ["i", "x1", "y1"], values: [1, 5.05, 6.06] } },
|
||||
{ locals: {a2: 5.05, b2: 6.06}, args: { names: ["i"], values: [2] } },
|
||||
{ locals: {a3: 7.07, b3: 8.08}, args: { names: ["i", "x3", "y3", "z3"],
|
||||
values: [3, 9.09, 10.10, undefined] } },
|
||||
{ locals: {a4: 9.09, b4: 10.10}, args: { names: ["i", "x4", "y4"], values: [4, 11.11, 12.12] } }
|
||||
];
|
||||
|
||||
function arraySum(arr) {
|
||||
return arr.reduce(function (a, b) { return a + b; }, 0);
|
||||
}
|
||||
|
||||
function listener(event, exec_state, event_data, data) {
|
||||
try {
|
||||
@ -44,42 +64,63 @@ function listener(event, exec_state, event_data, data) {
|
||||
for (var i = 0; i < exec_state.frameCount(); i++) {
|
||||
var frame = exec_state.frame(i);
|
||||
if (i < exec_state.frameCount() - 1) {
|
||||
var expected_a = i * 2 + 1 + (i * 2 + 1) / 100;
|
||||
var expected_b = i * 2 + 2 + (i * 2 + 2) / 100;
|
||||
var expected_x = (i + 1) * 2 + 1 + ((i + 1) * 2 + 1) / 100;
|
||||
var expected_y = (i + 1) * 2 + 2 + ((i + 1) * 2 + 2) / 100;
|
||||
var expected_args = expected[i].args;
|
||||
var expected_locals = expected[i].locals;
|
||||
|
||||
// All frames except the bottom one has normal variables a and b.
|
||||
var a = ('a' === frame.localName(0)) ? 0 : 1;
|
||||
var b = 1 - a;
|
||||
assertEquals('a', frame.localName(a));
|
||||
assertEquals('b', frame.localName(b));
|
||||
assertEquals(expected_a, frame.localValue(a).value());
|
||||
assertEquals(expected_b, frame.localValue(b).value());
|
||||
// All frames except the bottom one have expected locals.
|
||||
var locals = {};
|
||||
for (var j = 0; j < frame.localCount(); j++) {
|
||||
locals[frame.localName(j)] = frame.localValue(j).value();
|
||||
}
|
||||
assertPropertiesEqual(expected_locals, locals);
|
||||
|
||||
// All frames except the bottom one has arguments variables x and y.
|
||||
assertEquals('x', frame.argumentName(0));
|
||||
assertEquals('y', frame.argumentName(1));
|
||||
assertEquals(expected_x, frame.argumentValue(0).value());
|
||||
assertEquals(expected_y, frame.argumentValue(1).value());
|
||||
// All frames except the bottom one have expected arguments.
|
||||
for (var j = 0; j < expected_args.names.length; j++) {
|
||||
assertEquals(expected_args.names[j], frame.argumentName(j));
|
||||
assertEquals(expected_args.values[j], frame.argumentValue(j).value());
|
||||
}
|
||||
|
||||
// All frames except the bottom one have two scopes.
|
||||
assertEquals(2, frame.scopeCount());
|
||||
assertEquals(debug.ScopeType.Local, frame.scope(0).scopeType());
|
||||
assertEquals(debug.ScopeType.Global, frame.scope(1).scopeType());
|
||||
assertEquals(expected_a, frame.scope(0).scopeObject().value()['a']);
|
||||
assertEquals(expected_b, frame.scope(0).scopeObject().value()['b']);
|
||||
assertEquals(expected_x, frame.scope(0).scopeObject().value()['x']);
|
||||
assertEquals(expected_y, frame.scope(0).scopeObject().value()['y']);
|
||||
|
||||
Object.keys(expected_locals).forEach(function (name) {
|
||||
assertEquals(expected_locals[name], frame.scope(0).scopeObject().value()[name]);
|
||||
});
|
||||
|
||||
for (var j = 0; j < expected_args.names.length; j++) {
|
||||
var arg_name = expected_args.names[j];
|
||||
var arg_value = expected_args.values[j];
|
||||
assertEquals(arg_value, frame.scope(0).scopeObject().value()[arg_name]);
|
||||
}
|
||||
|
||||
// Evaluate in the inlined frame.
|
||||
assertEquals(expected_a, frame.evaluate('a').value());
|
||||
assertEquals(expected_x, frame.evaluate('x').value());
|
||||
assertEquals(expected_x, frame.evaluate('arguments[0]').value());
|
||||
assertEquals(expected_a + expected_b + expected_x + expected_y,
|
||||
frame.evaluate('a + b + x + y').value());
|
||||
assertEquals(expected_x + expected_y,
|
||||
frame.evaluate('arguments[0] + arguments[1]').value());
|
||||
Object.keys(expected_locals).forEach(function (name) {
|
||||
assertEquals(expected_locals[name], frame.evaluate(name).value());
|
||||
});
|
||||
|
||||
for (var j = 0; j < expected_args.names.length; j++) {
|
||||
var arg_name = expected_args.names[j];
|
||||
var arg_value = expected_args.values[j];
|
||||
assertEquals(arg_value, frame.evaluate(arg_name).value());
|
||||
assertEquals(arg_value, frame.evaluate('arguments['+j+']').value());
|
||||
}
|
||||
|
||||
var expected_args_sum = arraySum(expected_args.values);
|
||||
var expected_locals_sum =
|
||||
arraySum(Object.keys(expected_locals).
|
||||
map(function (k) { return expected_locals[k]; }));
|
||||
|
||||
assertEquals(expected_locals_sum + expected_args_sum,
|
||||
frame.evaluate(Object.keys(expected_locals).join('+') + ' + ' +
|
||||
expected_args.names.join('+')).value());
|
||||
|
||||
var arguments_sum = expected_args.names.map(function(_, idx) {
|
||||
return "arguments[" + idx + "]";
|
||||
}).join('+');
|
||||
assertEquals(expected_args_sum,
|
||||
frame.evaluate(arguments_sum).value());
|
||||
} else {
|
||||
// The bottom frame only have the global scope.
|
||||
assertEquals(1, frame.scopeCount());
|
||||
@ -121,62 +162,64 @@ function listener(event, exec_state, event_data, data) {
|
||||
listenerComplete = true;
|
||||
}
|
||||
} catch (e) {
|
||||
exception = e
|
||||
exception = e.toString() + e.stack;
|
||||
};
|
||||
};
|
||||
|
||||
f();f();f();
|
||||
for (var i = 0; i < 4; i++) f(input.length - 1, 11.11, 12.12);
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
f();
|
||||
f(input.length - 1, 11.11, 12.12);
|
||||
|
||||
// Add the debug event listener.
|
||||
Debug.setListener(listener);
|
||||
|
||||
function h(x, y) {
|
||||
var a = 1;
|
||||
var b = 2;
|
||||
a = a + a / 100;
|
||||
b = b + b / 100;
|
||||
function h(i, x0, y0) {
|
||||
var a0 = input[i].a;
|
||||
var b0 = input[i].b;
|
||||
a0 = a0 + a0 / 100;
|
||||
b0 = b0 + b0 / 100;
|
||||
debugger; // Breakpoint.
|
||||
};
|
||||
|
||||
function g3(x, y) {
|
||||
var a = 3;
|
||||
var b = 4;
|
||||
a = a + a / 100;
|
||||
b = b + b / 100;
|
||||
h(a, b);
|
||||
return a+b;
|
||||
function g3(i, x1, y1) {
|
||||
var a1 = input[i].a;
|
||||
var b1 = input[i].b;
|
||||
a1 = a1 + a1 / 100;
|
||||
b1 = b1 + b1 / 100;
|
||||
h(i - 1, a1, b1);
|
||||
return a1+b1;
|
||||
};
|
||||
|
||||
function g2(x, y) {
|
||||
var a = 5;
|
||||
var b = 6;
|
||||
a = a + a / 100;
|
||||
b = b + b / 100;
|
||||
g3(a, b);
|
||||
function g2(i) {
|
||||
var a2 = input[i].a;
|
||||
var b2 = input[i].b;
|
||||
a2 = a2 + a2 / 100;
|
||||
b2 = b2 + b2 / 100;
|
||||
g3(i - 1, a2, b2);
|
||||
};
|
||||
|
||||
function g1(x, y) {
|
||||
var a = 7;
|
||||
var b = 8;
|
||||
a = a + a / 100;
|
||||
b = b + b / 100;
|
||||
g2(a, b);
|
||||
function g1(i, x3, y3, z3) {
|
||||
var a3 = input[i].a;
|
||||
var b3 = input[i].b;
|
||||
a3 = a3 + a3 / 100;
|
||||
b3 = b3 + b3 / 100;
|
||||
g2(i - 1, a3, b3);
|
||||
};
|
||||
|
||||
function f(x, y) {
|
||||
var a = 9;
|
||||
var b = 10;
|
||||
a = a + a / 100;
|
||||
b = b + b / 100;
|
||||
g1(a, b);
|
||||
function f(i, x4, y4) {
|
||||
var a4 = input[i].a;
|
||||
var b4 = input[i].b;
|
||||
a4 = a4 + a4 / 100;
|
||||
b4 = b4 + b4 / 100;
|
||||
g1(i - 1, a4, b4);
|
||||
};
|
||||
|
||||
// Test calling f normally and as a constructor.
|
||||
f(11.11, 12.12);
|
||||
f(input.length - 1, 11.11, 12.12);
|
||||
f(input.length - 1, 11.11, 12.12, "");
|
||||
testingConstructCall = true;
|
||||
new f(11.11, 12.12);
|
||||
new f(input.length - 1, 11.11, 12.12);
|
||||
new f(input.length - 1, 11.11, 12.12, "");
|
||||
|
||||
// Make sure that the debug event listener vas invoked.
|
||||
assertFalse(exception, "exception in listener " + exception)
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2008 the V8 project authors. All rights reserved.
|
||||
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
@ -34,6 +34,17 @@ var exception = false;
|
||||
|
||||
var testingConstructCall = false;
|
||||
|
||||
var expected = [
|
||||
{ locals: {a0: 1, b0: 2}, args: { names: ["i", "x0", "y0"], values: [0, 3, 4] } },
|
||||
{ locals: {a1: 3, b1: 4}, args: { names: ["i", "x1", "y1"], values: [1, 5, 6] } },
|
||||
{ locals: {a2: 5, b2: 6}, args: { names: ["i"], values: [2] } },
|
||||
{ locals: {a3: 7, b3: 8}, args: { names: ["i", "x3", "y3", "z3"], values: [3, 9, 10, undefined] } },
|
||||
{ locals: {a4: 9, b4: 10}, args: { names: ["i", "x4", "y4"], values: [4, 11, 12] } }
|
||||
];
|
||||
|
||||
function arraySum(arr) {
|
||||
return arr.reduce(function (a, b) { return a + b; }, 0);
|
||||
}
|
||||
|
||||
function listener(event, exec_state, event_data, data) {
|
||||
try {
|
||||
@ -44,42 +55,63 @@ function listener(event, exec_state, event_data, data) {
|
||||
for (var i = 0; i < exec_state.frameCount(); i++) {
|
||||
var frame = exec_state.frame(i);
|
||||
if (i < exec_state.frameCount() - 1) {
|
||||
var expected_a = i * 2 + 1;
|
||||
var expected_b = i * 2 + 2;
|
||||
var expected_x = (i + 1) * 2 + 1;
|
||||
var expected_y = (i + 1) * 2 + 2;
|
||||
var expected_args = expected[i].args;
|
||||
var expected_locals = expected[i].locals;
|
||||
|
||||
// All frames except the bottom one has normal variables a and b.
|
||||
var a = ('a' === frame.localName(0)) ? 0 : 1;
|
||||
var b = 1 - a;
|
||||
assertEquals('a', frame.localName(a));
|
||||
assertEquals('b', frame.localName(b));
|
||||
assertEquals(expected_a, frame.localValue(a).value());
|
||||
assertEquals(expected_b, frame.localValue(b).value());
|
||||
// All frames except the bottom one have expected locals.
|
||||
var locals = {};
|
||||
for (var j = 0; j < frame.localCount(); j++) {
|
||||
locals[frame.localName(j)] = frame.localValue(j).value();
|
||||
}
|
||||
assertPropertiesEqual(expected_locals, locals);
|
||||
|
||||
// All frames except the bottom one has arguments variables x and y.
|
||||
assertEquals('x', frame.argumentName(0));
|
||||
assertEquals('y', frame.argumentName(1));
|
||||
assertEquals(expected_x, frame.argumentValue(0).value());
|
||||
assertEquals(expected_y, frame.argumentValue(1).value());
|
||||
// All frames except the bottom one have expected arguments.
|
||||
for (var j = 0; j < expected_args.names.length; j++) {
|
||||
assertEquals(expected_args.names[j], frame.argumentName(j));
|
||||
assertEquals(expected_args.values[j], frame.argumentValue(j).value());
|
||||
}
|
||||
|
||||
// All frames except the bottom one have two scopes.
|
||||
assertEquals(2, frame.scopeCount());
|
||||
assertEquals(debug.ScopeType.Local, frame.scope(0).scopeType());
|
||||
assertEquals(debug.ScopeType.Global, frame.scope(1).scopeType());
|
||||
assertEquals(expected_a, frame.scope(0).scopeObject().value()['a']);
|
||||
assertEquals(expected_b, frame.scope(0).scopeObject().value()['b']);
|
||||
assertEquals(expected_x, frame.scope(0).scopeObject().value()['x']);
|
||||
assertEquals(expected_y, frame.scope(0).scopeObject().value()['y']);
|
||||
|
||||
Object.keys(expected_locals).forEach(function (name) {
|
||||
assertEquals(expected_locals[name], frame.scope(0).scopeObject().value()[name]);
|
||||
});
|
||||
|
||||
for (var j = 0; j < expected_args.names.length; j++) {
|
||||
var arg_name = expected_args.names[j];
|
||||
var arg_value = expected_args.values[j];
|
||||
assertEquals(arg_value, frame.scope(0).scopeObject().value()[arg_name]);
|
||||
}
|
||||
|
||||
// Evaluate in the inlined frame.
|
||||
assertEquals(expected_a, frame.evaluate('a').value());
|
||||
assertEquals(expected_x, frame.evaluate('x').value());
|
||||
assertEquals(expected_x, frame.evaluate('arguments[0]').value());
|
||||
assertEquals(expected_a + expected_b + expected_x + expected_y,
|
||||
frame.evaluate('a + b + x + y').value());
|
||||
assertEquals(expected_x + expected_y,
|
||||
frame.evaluate('arguments[0] + arguments[1]').value());
|
||||
Object.keys(expected_locals).forEach(function (name) {
|
||||
assertEquals(expected_locals[name], frame.evaluate(name).value());
|
||||
});
|
||||
|
||||
for (var j = 0; j < expected_args.names.length; j++) {
|
||||
var arg_name = expected_args.names[j];
|
||||
var arg_value = expected_args.values[j];
|
||||
assertEquals(arg_value, frame.evaluate(arg_name).value());
|
||||
assertEquals(arg_value, frame.evaluate('arguments['+j+']').value());
|
||||
}
|
||||
|
||||
var expected_args_sum = arraySum(expected_args.values);
|
||||
var expected_locals_sum =
|
||||
arraySum(Object.keys(expected_locals).
|
||||
map(function (k) { return expected_locals[k]; }));
|
||||
|
||||
assertEquals(expected_locals_sum + expected_args_sum,
|
||||
frame.evaluate(Object.keys(expected_locals).join('+') + ' + ' +
|
||||
expected_args.names.join('+')).value());
|
||||
|
||||
var arguments_sum = expected_args.names.map(function(_, idx) {
|
||||
return "arguments[" + idx + "]";
|
||||
}).join('+');
|
||||
assertEquals(expected_args_sum,
|
||||
frame.evaluate(arguments_sum).value());
|
||||
} else {
|
||||
// The bottom frame only have the global scope.
|
||||
assertEquals(1, frame.scopeCount());
|
||||
@ -121,51 +153,53 @@ function listener(event, exec_state, event_data, data) {
|
||||
listenerComplete = true;
|
||||
}
|
||||
} catch (e) {
|
||||
exception = e.stack;
|
||||
exception = e.toString() + e.stack;
|
||||
};
|
||||
};
|
||||
|
||||
f();f();f();
|
||||
for (var i = 0; i < 4; i++) f(expected.length - 1, 11, 12);
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
f();
|
||||
f(expected.length - 1, 11, 12);
|
||||
|
||||
// Add the debug event listener.
|
||||
Debug.setListener(listener);
|
||||
|
||||
function h(x, y) {
|
||||
var a = 1;
|
||||
var b = 2;
|
||||
function h(i, x0, y0) {
|
||||
var a0 = expected[i].locals.a0;
|
||||
var b0 = expected[i].locals.b0;
|
||||
debugger; // Breakpoint.
|
||||
};
|
||||
}
|
||||
|
||||
function g3(x, y) {
|
||||
var a = 3;
|
||||
var b = 4;
|
||||
h(a, b);
|
||||
};
|
||||
function g3(i, x1, y1) {
|
||||
var a1 = expected[i].locals.a1;
|
||||
var b1 = expected[i].locals.b1;
|
||||
h(i - 1, a1, b1);
|
||||
}
|
||||
|
||||
function g2(x, y) {
|
||||
var a = 5;
|
||||
var b = 6;
|
||||
g3(a, b);
|
||||
};
|
||||
function g2(i) {
|
||||
var a2 = expected[i].locals.a2;
|
||||
var b2 = expected[i].locals.b2;
|
||||
g3(i - 1, a2, b2);
|
||||
}
|
||||
|
||||
function g1(x, y) {
|
||||
var a = 7;
|
||||
var b = 8;
|
||||
g2(a, b);
|
||||
};
|
||||
function g1(i, x3, y3, z3) {
|
||||
var a3 = expected[i].locals.a3;
|
||||
var b3 = expected[i].locals.b3;
|
||||
g2(i - 1, a3, b3);
|
||||
}
|
||||
|
||||
function f(x, y) {
|
||||
var a = 9;
|
||||
var b = 10;
|
||||
g1(a, b);
|
||||
};
|
||||
function f(i, x4, y4) {
|
||||
var a4 = expected[i].locals.a4;
|
||||
var b4 = expected[i].locals.b4;
|
||||
g1(i - 1, a4, b4);
|
||||
}
|
||||
|
||||
// Test calling f normally and as a constructor.
|
||||
f(11, 12);
|
||||
f(expected.length - 1, 11, 12);
|
||||
f(expected.length - 1, 11, 12, 0);
|
||||
testingConstructCall = true;
|
||||
new f(11, 12);
|
||||
new f(expected.length - 1, 11, 12);
|
||||
new f(expected.length - 1, 11, 12, 0);
|
||||
|
||||
// Make sure that the debug event listener vas invoked.
|
||||
assertFalse(exception, "exception in listener " + exception)
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2011 the V8 project authors. All rights reserved.
|
||||
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
@ -29,59 +29,116 @@
|
||||
|
||||
// Check that %NewObjectFromBound works correctly when called from optimized
|
||||
// frame.
|
||||
function foo(x, y, z) {
|
||||
function foo1(x, y, z) {
|
||||
assertEquals(1, x);
|
||||
assertEquals(2, y);
|
||||
assertEquals(3, z);
|
||||
}
|
||||
|
||||
var foob = foo.bind({}, 1);
|
||||
function foo2(x, y, z) {
|
||||
assertEquals(1, x);
|
||||
assertEquals(2, y);
|
||||
assertEquals(undefined, z);
|
||||
}
|
||||
|
||||
function f(y, z) {
|
||||
return %NewObjectFromBound(foob);
|
||||
function foo3(x, y, z) {
|
||||
assertEquals(1, x);
|
||||
assertEquals(2, y);
|
||||
assertEquals(3, z);
|
||||
}
|
||||
|
||||
|
||||
var foob1 = foo1.bind({}, 1);
|
||||
var foob2 = foo2.bind({}, 1);
|
||||
var foob3 = foo3.bind({}, 1);
|
||||
|
||||
|
||||
function f1(y, z) {
|
||||
return %NewObjectFromBound(foob1);
|
||||
}
|
||||
|
||||
function f2(y, z) {
|
||||
return %NewObjectFromBound(foob2);
|
||||
}
|
||||
|
||||
function f3(y, z) {
|
||||
return %NewObjectFromBound(foob3);
|
||||
}
|
||||
|
||||
// Check that %NewObjectFromBound looks at correct frame for inlined function.
|
||||
function g(z, y) {
|
||||
return f(y, z); /* f should be inlined into g, note rotated arguments */
|
||||
function g1(z, y) {
|
||||
return f1(y, z); /* f should be inlined into g, note rotated arguments */
|
||||
}
|
||||
|
||||
function g2(z, y, x) {
|
||||
return f2(y); /* f should be inlined into g, note argument count mismatch */
|
||||
}
|
||||
|
||||
function g3(z, y, x) {
|
||||
return f3(x, y, z); /* f should be inlined into g, note argument count mismatch */
|
||||
}
|
||||
|
||||
// Check that %NewObjectFromBound looks at correct frame for inlined function.
|
||||
function ff(x) { }
|
||||
function h(z2, y2) {
|
||||
function h1(z2, y2) {
|
||||
var local_z = z2 >> 1;
|
||||
ff(local_z);
|
||||
var local_y = y2 >> 1;
|
||||
ff(local_y);
|
||||
return f(local_y, local_z); /* f should be inlined into h */
|
||||
return f1(local_y, local_z); /* f should be inlined into h */
|
||||
}
|
||||
|
||||
for (var i = 0; i < 5; i++) f(2, 3);
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
f(2, 3);
|
||||
function h2(z2, y2, x2) {
|
||||
var local_z = z2 >> 1;
|
||||
ff(local_z);
|
||||
var local_y = y2 >> 1;
|
||||
ff(local_y);
|
||||
return f2(local_y); /* f should be inlined into h */
|
||||
}
|
||||
|
||||
for (var i = 0; i < 5; i++) g(3, 2);
|
||||
%OptimizeFunctionOnNextCall(g);
|
||||
g(3, 2);
|
||||
function h3(z2, y2, x2) {
|
||||
var local_z = z2 >> 1;
|
||||
ff(local_z);
|
||||
var local_y = y2 >> 1;
|
||||
ff(local_y);
|
||||
var local_x = x2 >> 1;
|
||||
ff(local_x);
|
||||
return f3(local_x, local_y, local_z); /* f should be inlined into h */
|
||||
}
|
||||
|
||||
for (var i = 0; i < 5; i++) h(6, 4);
|
||||
%OptimizeFunctionOnNextCall(h);
|
||||
h(6, 4);
|
||||
|
||||
function invoke(f, args) {
|
||||
for (var i = 0; i < 5; i++) f.apply(this, args);
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
f.apply(this, args);
|
||||
}
|
||||
|
||||
invoke(f1, [2, 3]);
|
||||
invoke(f2, [2]);
|
||||
invoke(f3, [2, 3, 4]);
|
||||
invoke(g1, [3, 2]);
|
||||
invoke(g2, [3, 2, 4]);
|
||||
invoke(g3, [4, 3, 2]);
|
||||
invoke(h1, [6, 4]);
|
||||
invoke(h2, [6, 4, 8]);
|
||||
invoke(h3, [8, 6, 4]);
|
||||
|
||||
// Check that %_IsConstructCall returns correct value when inlined
|
||||
var NON_CONSTRUCT_MARKER = {};
|
||||
var CONSTRUCT_MARKER = {};
|
||||
function baz() {
|
||||
function baz(x) {
|
||||
return (!%_IsConstructCall()) ? NON_CONSTRUCT_MARKER : CONSTRUCT_MARKER;
|
||||
}
|
||||
|
||||
function bar(x, y, z) {
|
||||
var non_construct = baz(0); /* baz should be inlined */
|
||||
assertEquals(non_construct, NON_CONSTRUCT_MARKER);
|
||||
var non_construct = baz(); /* baz should be inlined */
|
||||
assertEquals(non_construct, NON_CONSTRUCT_MARKER);
|
||||
var construct = new baz();
|
||||
var non_construct = baz(0, 0); /* baz should be inlined */
|
||||
assertEquals(non_construct, NON_CONSTRUCT_MARKER);
|
||||
var construct = new baz(0);
|
||||
assertEquals(construct, CONSTRUCT_MARKER);
|
||||
}
|
||||
|
||||
for (var i = 0; i < 5; i++) new bar(1, 2, 3);
|
||||
%OptimizeFunctionOnNextCall(bar);
|
||||
bar(1, 2, 3);
|
||||
invoke(bar, [1, 2, 3]);
|
||||
|
Loading…
Reference in New Issue
Block a user