-// 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:
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,
- inlined_frame_index,
- &args_slots);
+ Vector<SlotRef> args_slots =
+ SlotRef::ComputeSlotMappingForArguments(
+ frame,
+ inlined_frame_index,
+ 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);
array->set(i, *value);
}
arguments->set_elements(*array);
+ args_slots.Dispose();
// Return the freshly allocated arguments object.
return *arguments;
__ bind(&invoke);
__ Call(r3);
+ masm->isolate()->heap()->SetArgumentsAdaptorDeoptPCOffset(masm->pc_offset());
// Exit frame and return.
LeaveArgumentsAdaptorFrame(masm);
__ Jump(lr);
-// 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:
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);
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.
}
+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,
- int frame_index) {
+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();
// 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);
-// 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:
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;
class ArgumentsAdaptorFrameConstants : public AllStatic {
public:
static const int kLengthOffset = StandardFrameConstants::kExpressionsOffset;
+ static const int kFrameSize =
+ StandardFrameConstants::kFixedFrameSize + kPointerSize;
};
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;
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;
}
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());
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;
}
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
// |>------------ 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();
-// 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:
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());
// 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;
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),
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);
}
// 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) {
// 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.
#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);
}
}
}
switch (opcode) {
case Translation::BEGIN:
- case Translation::FRAME:
+ case Translation::JS_FRAME:
+ case Translation::ARGUMENTS_ADAPTOR_FRAME:
case Translation::DUPLICATE:
UNREACHABLE();
return;
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 ": ",
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) {
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",
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;
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,
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());
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);
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;
}
}
-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() {
- return function_->shared()->formal_parameter_count();
+ 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));
}
}
-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);
case ARGUMENTS_OBJECT:
case DUPLICATE:
return 0;
- case BEGIN:
case REGISTER:
case INT32_REGISTER:
case DOUBLE_REGISTER:
case DOUBLE_STACK_SLOT:
case LITERAL:
return 1;
- case FRAME:
+ case BEGIN:
+ case ARGUMENTS_ADAPTOR_FRAME:
+ return 2;
+ case JS_FRAME:
return 3;
}
UNREACHABLE();
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:
switch (opcode) {
case Translation::BEGIN:
- case Translation::FRAME:
+ case Translation::JS_FRAME:
+ case Translation::ARGUMENTS_ADAPTOR_FRAME:
// Peeled off before getting here.
break;
}
-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 =
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());
- // Skip over operands to advance to the next opcode.
- it.Skip(Translation::NumberOfOperandsFor(opcode));
- if (opcode == Translation::FRAME) {
- if (frames_to_skip == 0) {
+ 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));
+
// 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);
- }
- return;
+ // 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;
}
- 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));
}
}
-// 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:
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,
// 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);
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);
int count_;
};
+ int ConvertJSFrameIndexToFrameIndex(int jsframe_index);
+
private:
static const int kNumberOfEntries = 8192;
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);
FrameDescription* input_;
// Number of output frames.
int output_count_;
+ // Number of output js frames.
+ int jsframe_count_;
// Array of output frame descriptions.
FrameDescription** output_;
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);
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_);
intptr_t top_;
intptr_t pc_;
intptr_t fp_;
+ StackFrame::Type type_;
Smi* state_;
#ifdef DEBUG
Code::Kind kind_;
return reinterpret_cast<intptr_t*>(
reinterpret_cast<Address>(this) + frame_content_offset() + offset);
}
+
+ int ComputeFixedSize();
};
public:
enum Opcode {
BEGIN,
- FRAME,
+ JS_FRAME,
+ ARGUMENTS_ADAPTOR_FRAME,
REGISTER,
INT32_REGISTER,
DOUBLE_REGISTER,
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);
}
}
- static void ComputeSlotMappingForArguments(JavaScriptFrame* frame,
- int inlined_frame_index,
- Vector<SlotRef>* args_slots);
+ static Vector<SlotRef> ComputeSlotMappingForArguments(
+ JavaScriptFrame* frame,
+ int inlined_frame_index,
+ int formal_parameter_count);
private:
Address addr_;
static SlotRef ComputeSlotForNextArgument(TranslationIterator* iterator,
DeoptimizationInputData* data,
JavaScriptFrame* frame);
+
+ static void ComputeSlotsForArguments(
+ Vector<SlotRef>* args_slots,
+ TranslationIterator* iterator,
+ DeoptimizationInputData* data,
+ JavaScriptFrame* frame);
};
// 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.
-// 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:
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();
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;
}
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.
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) \
// 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]); \
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();
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) {
}
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_; }
private:
Handle<JSFunction> closure_;
+ int arguments_count_;
FunctionLiteral* function_;
CallKind call_kind_;
};
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);
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);
for_typeof_(false) {
owner->set_ast_context(this); // Push.
#ifdef DEBUG
+ ASSERT(!owner->environment()->is_arguments_adaptor());
original_length_ = owner->environment()->length();
#endif
}
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()));
}
TraceInline(target, caller, "inline depth limit reached");
return false;
}
- current_level++;
+ if (!env->outer()->is_arguments_adaptor()) {
+ current_level++;
+ }
env = env->outer();
}
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;
}
HConstant* undefined = graph()->GetConstantUndefined();
HEnvironment* inner_env =
environment()->CopyForInlining(target,
+ expr->arguments()->length(),
function,
undefined,
call_kind);
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());
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);
}
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) {
pop_count_ = other->pop_count_;
push_count_ = other->push_count_;
ast_id_ = other->ast_id_;
+ arguments_adaptor_ = other->arguments_adaptor_;
}
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
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);
}
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);
}
stream->Add("\n");
}
+ PrintF("\n");
}
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_; }
// 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;
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;
int pop_count_;
int push_count_;
int ast_id_;
+ bool arguments_adaptor_;
};
{
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);
__ mov(edi, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
__ call(edx);
+ masm->isolate()->heap()->SetArgumentsAdaptorDeoptPCOffset(masm->pc_offset());
// Leave frame and return.
LeaveArgumentsAdaptorFrame(masm);
__ ret(0);
-// 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:
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);
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.
}
-void Deoptimizer::DoComputeFrame(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);
+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) {
int node_id = iterator->Next();
JSFunction* function = JSFunction::cast(ComputeLiteral(iterator->Next()));
unsigned height = iterator->Next();
// 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);
-// 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:
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;
class ArgumentsAdaptorFrameConstants : public AllStatic {
public:
static const int kLengthOffset = StandardFrameConstants::kExpressionsOffset;
+ static const int kFrameSize =
+ StandardFrameConstants::kFixedFrameSize + kPointerSize;
};
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
// |>------------ 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();
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;
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;
}
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());
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;
}
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),
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_;
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 !=
UNREACHABLE();
break;
- case Translation::FRAME: {
+ case Translation::JS_FRAME: {
int ast_id = iterator.Next();
int function_id = iterator.Next();
JSFunction* function =
break;
}
+ case Translation::ARGUMENTS_ADAPTOR_FRAME: {
+ unsigned height = iterator.Next();
+ PrintF(out, "{arguments adaptor, height=%d}", height);
+ break;
+ }
+
case Translation::DUPLICATE:
break;
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(
Handle<Object> val = args_slots[i].GetValue();
param_data[prefix_argc + i] = val;
}
+
+ args_slots.Dispose();
+
return param_data;
} else {
it.AdvanceToArgumentsFrame();
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());
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();
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()) {
}
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.
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());
// 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());
}
// 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());
}
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 +
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));
}
// 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 {
// 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.
// 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>());
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>());
}
+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,
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) {
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()));
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_;
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.
// 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++;
}
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());
// 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
}
}
- 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);
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;
}
}
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]);
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
#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
go_between,
context,
frame,
- inlined_frame_index);
+ inlined_jsframe_index);
if (additional_context->IsJSObject()) {
Handle<JSObject> extension = Handle<JSObject>::cast(additional_context);
Handle<Object> arguments = GetArgumentsObject(isolate,
frame,
- inlined_frame_index,
- function,
+ &frame_inspector,
scope_info,
function_context);
__ bind(&invoke);
__ call(rdx);
+ masm->isolate()->heap()->SetArgumentsAdaptorDeoptPCOffset(masm->pc_offset());
// Leave frame and return.
LeaveArgumentsAdaptorFrame(masm);
__ ret(0);
-// 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:
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);
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.
}
-void Deoptimizer::DoComputeFrame(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);
+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) {
int node_id = iterator->Next();
JSFunction* function = JSFunction::cast(ComputeLiteral(iterator->Next()));
unsigned height = iterator->Next();
// 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);
-// 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:
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;
class ArgumentsAdaptorFrameConstants : public AllStatic {
public:
static const int kLengthOffset = StandardFrameConstants::kExpressionsOffset;
+ static const int kFrameSize =
+ StandardFrameConstants::kFixedFrameSize + kPointerSize;
};
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
// |>------------ 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();
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;
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;
}
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());
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;
}
--- /dev/null
+// 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));
-// 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:
// (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() {}
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));
o = new B();
-stress();
-
+opt();
assertArrayEquals([0, "f"], o.g(0, "g"));
assertArrayEquals([1, "g"], o.g(1, "g"));
assertArrayEquals([0, "f"], hej(0));
-// 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:
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 {
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;
-
- // 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 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());
+ var expected_args = expected[i].args;
+ var expected_locals = expected[i].locals;
+
+ // 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 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());
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)
-// 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:
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 {
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;
-
- // 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 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());
+ var expected_args = expected[i].args;
+ var expected_locals = expected[i].locals;
+
+ // 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 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());
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 g2(x, y) {
- var a = 5;
- var b = 6;
- g3(a, b);
-};
-
-function g1(x, y) {
- var a = 7;
- var b = 8;
- g2(a, b);
-};
-
-function f(x, y) {
- var a = 9;
- var b = 10;
- g1(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(i) {
+ var a2 = expected[i].locals.a2;
+ var b2 = expected[i].locals.b2;
+ g3(i - 1, a2, b2);
+}
+
+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(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)
-// 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:
// 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 */
+}
+
+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++) g(3, 2);
-%OptimizeFunctionOnNextCall(g);
-g(3, 2);
-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]);