Commit ae65366f authored by vegorov@chromium.org's avatar vegorov@chromium.org

Fix SlotRef::SlotAddress for parameters indices.

Fix %NewObjectFromBound to correctly handle optimized frames (including those with inlined functions).

Fix %_IsConstructCall handling in hydrogen: when called from inlined function return false constant directly instead of emiting HIsConstructCall.

Fix success case in TraceInline.

BUG=v8:1229
TEST=test/mjsunit/regress/regress-1229.js

Review URL: http://codereview.chromium.org/6740023

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@7472 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 8d4303e0
......@@ -568,172 +568,6 @@ const AccessorDescriptor Accessors::FunctionName = {
// Accessors::FunctionArguments
//
static Address SlotAddress(JavaScriptFrame* frame, int slot_index) {
if (slot_index >= 0) {
const int offset = JavaScriptFrameConstants::kLocal0Offset;
return frame->fp() + offset - (slot_index * kPointerSize);
} else {
const int offset = JavaScriptFrameConstants::kReceiverOffset;
return frame->caller_sp() + offset + (slot_index * kPointerSize);
}
}
// We can't intermix stack decoding and allocations because
// deoptimization infrastracture is not GC safe.
// Thus we build a temporary structure in malloced space.
class SlotRef BASE_EMBEDDED {
public:
enum SlotRepresentation {
UNKNOWN,
TAGGED,
INT32,
DOUBLE,
LITERAL
};
SlotRef()
: addr_(NULL), representation_(UNKNOWN) { }
SlotRef(Address addr, SlotRepresentation representation)
: addr_(addr), representation_(representation) { }
explicit SlotRef(Object* literal)
: literal_(literal), representation_(LITERAL) { }
Handle<Object> GetValue() {
switch (representation_) {
case TAGGED:
return Handle<Object>(Memory::Object_at(addr_));
case INT32: {
int value = Memory::int32_at(addr_);
if (Smi::IsValid(value)) {
return Handle<Object>(Smi::FromInt(value));
} else {
return Isolate::Current()->factory()->NewNumberFromInt(value);
}
}
case DOUBLE: {
double value = Memory::double_at(addr_);
return Isolate::Current()->factory()->NewNumber(value);
}
case LITERAL:
return literal_;
default:
UNREACHABLE();
return Handle<Object>::null();
}
}
private:
Address addr_;
Handle<Object> literal_;
SlotRepresentation representation_;
};
static SlotRef ComputeSlotForNextArgument(TranslationIterator* iterator,
DeoptimizationInputData* data,
JavaScriptFrame* frame) {
Translation::Opcode opcode =
static_cast<Translation::Opcode>(iterator->Next());
switch (opcode) {
case Translation::BEGIN:
case Translation::FRAME:
// Peeled off before getting here.
break;
case Translation::ARGUMENTS_OBJECT:
// This can be only emitted for local slots not for argument slots.
break;
case Translation::REGISTER:
case Translation::INT32_REGISTER:
case Translation::DOUBLE_REGISTER:
case Translation::DUPLICATE:
// We are at safepoint which corresponds to call. All registers are
// saved by caller so there would be no live registers at this
// point. Thus these translation commands should not be used.
break;
case Translation::STACK_SLOT: {
int slot_index = iterator->Next();
Address slot_addr = SlotAddress(frame, slot_index);
return SlotRef(slot_addr, SlotRef::TAGGED);
}
case Translation::INT32_STACK_SLOT: {
int slot_index = iterator->Next();
Address slot_addr = SlotAddress(frame, slot_index);
return SlotRef(slot_addr, SlotRef::INT32);
}
case Translation::DOUBLE_STACK_SLOT: {
int slot_index = iterator->Next();
Address slot_addr = SlotAddress(frame, slot_index);
return SlotRef(slot_addr, SlotRef::DOUBLE);
}
case Translation::LITERAL: {
int literal_index = iterator->Next();
return SlotRef(data->LiteralArray()->get(literal_index));
}
}
UNREACHABLE();
return SlotRef();
}
static void ComputeSlotMappingForArguments(JavaScriptFrame* frame,
int inlined_frame_index,
Vector<SlotRef>* args_slots) {
AssertNoAllocation no_gc;
int deopt_index = AstNode::kNoNumber;
DeoptimizationInputData* data =
static_cast<OptimizedFrame*>(frame)->GetDeoptimizationData(&deopt_index);
TranslationIterator it(data->TranslationByteArray(),
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;
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) {
// 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;
}
frames_to_skip--;
}
}
UNREACHABLE();
}
static MaybeObject* ConstructArgumentsObjectForInlinedFunction(
JavaScriptFrame* frame,
......@@ -742,7 +576,9 @@ static MaybeObject* ConstructArgumentsObjectForInlinedFunction(
Factory* factory = Isolate::Current()->factory();
int args_count = inlined_function->shared()->formal_parameter_count();
ScopedVector<SlotRef> args_slots(args_count);
ComputeSlotMappingForArguments(frame, inlined_frame_index, &args_slots);
SlotRef::ComputeSlotMappingForArguments(frame,
inlined_frame_index,
&args_slots);
Handle<JSObject> arguments =
factory->NewArgumentsObject(inlined_function, args_count);
Handle<FixedArray> array = factory->NewFixedArray(args_count);
......
......@@ -136,7 +136,7 @@ class JavaScriptFrameConstants : public AllStatic {
public:
// FP-relative.
static const int kLocal0Offset = StandardFrameConstants::kExpressionsOffset;
static const int kSavedRegistersOffset = +2 * kPointerSize;
static const int kLastParameterOffset = +2 * kPointerSize;
static const int kFunctionOffset = StandardFrameConstants::kMarkerOffset;
// Caller SP-relative.
......
......@@ -1194,4 +1194,103 @@ DeoptimizingCodeListNode::~DeoptimizingCodeListNode() {
}
// We can't intermix stack decoding and allocations because
// deoptimization infrastracture is not GC safe.
// Thus we build a temporary structure in malloced space.
SlotRef SlotRef::ComputeSlotForNextArgument(TranslationIterator* iterator,
DeoptimizationInputData* data,
JavaScriptFrame* frame) {
Translation::Opcode opcode =
static_cast<Translation::Opcode>(iterator->Next());
switch (opcode) {
case Translation::BEGIN:
case Translation::FRAME:
// Peeled off before getting here.
break;
case Translation::ARGUMENTS_OBJECT:
// This can be only emitted for local slots not for argument slots.
break;
case Translation::REGISTER:
case Translation::INT32_REGISTER:
case Translation::DOUBLE_REGISTER:
case Translation::DUPLICATE:
// We are at safepoint which corresponds to call. All registers are
// saved by caller so there would be no live registers at this
// point. Thus these translation commands should not be used.
break;
case Translation::STACK_SLOT: {
int slot_index = iterator->Next();
Address slot_addr = SlotAddress(frame, slot_index);
return SlotRef(slot_addr, SlotRef::TAGGED);
}
case Translation::INT32_STACK_SLOT: {
int slot_index = iterator->Next();
Address slot_addr = SlotAddress(frame, slot_index);
return SlotRef(slot_addr, SlotRef::INT32);
}
case Translation::DOUBLE_STACK_SLOT: {
int slot_index = iterator->Next();
Address slot_addr = SlotAddress(frame, slot_index);
return SlotRef(slot_addr, SlotRef::DOUBLE);
}
case Translation::LITERAL: {
int literal_index = iterator->Next();
return SlotRef(data->LiteralArray()->get(literal_index));
}
}
UNREACHABLE();
return SlotRef();
}
void SlotRef::ComputeSlotMappingForArguments(JavaScriptFrame* frame,
int inlined_frame_index,
Vector<SlotRef>* args_slots) {
AssertNoAllocation no_gc;
int deopt_index = AstNode::kNoNumber;
DeoptimizationInputData* data =
static_cast<OptimizedFrame*>(frame)->GetDeoptimizationData(&deopt_index);
TranslationIterator it(data->TranslationByteArray(),
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;
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) {
// 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;
}
frames_to_skip--;
}
}
UNREACHABLE();
}
} } // namespace v8::internal
......@@ -552,6 +552,78 @@ class DeoptimizingCodeListNode : public Malloced {
};
class SlotRef BASE_EMBEDDED {
public:
enum SlotRepresentation {
UNKNOWN,
TAGGED,
INT32,
DOUBLE,
LITERAL
};
SlotRef()
: addr_(NULL), representation_(UNKNOWN) { }
SlotRef(Address addr, SlotRepresentation representation)
: addr_(addr), representation_(representation) { }
explicit SlotRef(Object* literal)
: literal_(literal), representation_(LITERAL) { }
Handle<Object> GetValue() {
switch (representation_) {
case TAGGED:
return Handle<Object>(Memory::Object_at(addr_));
case INT32: {
int value = Memory::int32_at(addr_);
if (Smi::IsValid(value)) {
return Handle<Object>(Smi::FromInt(value));
} else {
return Isolate::Current()->factory()->NewNumberFromInt(value);
}
}
case DOUBLE: {
double value = Memory::double_at(addr_);
return Isolate::Current()->factory()->NewNumber(value);
}
case LITERAL:
return literal_;
default:
UNREACHABLE();
return Handle<Object>::null();
}
}
static void ComputeSlotMappingForArguments(JavaScriptFrame* frame,
int inlined_frame_index,
Vector<SlotRef>* args_slots);
private:
Address addr_;
Handle<Object> literal_;
SlotRepresentation representation_;
static Address SlotAddress(JavaScriptFrame* frame, int slot_index) {
if (slot_index >= 0) {
const int offset = JavaScriptFrameConstants::kLocal0Offset;
return frame->fp() + offset - (slot_index * kPointerSize);
} else {
const int offset = JavaScriptFrameConstants::kLastParameterOffset;
return frame->fp() + offset - ((slot_index + 1) * kPointerSize);
}
}
static SlotRef ComputeSlotForNextArgument(TranslationIterator* iterator,
DeoptimizationInputData* data,
JavaScriptFrame* frame);
};
} } // namespace v8::internal
#endif // V8_DEOPTIMIZER_H_
......@@ -617,7 +617,7 @@ Object* JavaScriptFrame::GetParameter(int index) const {
int JavaScriptFrame::ComputeParametersCount() const {
Address base = caller_sp() + JavaScriptFrameConstants::kReceiverOffset;
Address limit = fp() + JavaScriptFrameConstants::kSavedRegistersOffset;
Address limit = fp() + JavaScriptFrameConstants::kLastParameterOffset;
return static_cast<int>((base - limit) / kPointerSize);
}
......@@ -1084,7 +1084,7 @@ void JavaScriptFrame::Iterate(ObjectVisitor* v) const {
void JavaScriptFrame::IterateArguments(ObjectVisitor* v) const {
// Traverse callee-saved registers, receiver, and parameters.
const int kBaseOffset = JavaScriptFrameConstants::kSavedRegistersOffset;
const int kBaseOffset = JavaScriptFrameConstants::kLastParameterOffset;
const int kLimitOffset = JavaScriptFrameConstants::kReceiverOffset;
Object** base = &Memory::Object_at(fp() + kBaseOffset);
Object** limit = &Memory::Object_at(caller_sp() + kLimitOffset) + 1;
......
......@@ -3852,12 +3852,18 @@ void HGraphBuilder::HandlePolymorphicCallNamed(Call* expr,
void HGraphBuilder::TraceInline(Handle<JSFunction> target, const char* reason) {
if (FLAG_trace_inlining) {
SmartPointer<char> callee = target->shared()->DebugName()->ToCString();
SmartPointer<char> caller =
info()->function()->debug_name()->ToCString();
if (reason == NULL) {
// We are currently in the context of inlined function thus we have
// to go to an outer FunctionState to get caller.
SmartPointer<char> callee = target->shared()->DebugName()->ToCString();
SmartPointer<char> caller =
function_state()->outer()->compilation_info()->function()->
debug_name()->ToCString();
PrintF("Inlined %s called from %s.\n", *callee, *caller);
} else {
SmartPointer<char> callee = target->shared()->DebugName()->ToCString();
SmartPointer<char> caller =
info()->function()->debug_name()->ToCString();
PrintF("Did not inline %s called from %s (%s).\n",
*callee, *caller, reason);
}
......@@ -5117,7 +5123,14 @@ void HGraphBuilder::GenerateIsStringWrapperSafeForDefaultValueOf(
// Support for construct call checks.
void HGraphBuilder::GenerateIsConstructCall(CallRuntime* call) {
ASSERT(call->arguments()->length() == 0);
ast_context()->ReturnInstruction(new HIsConstructCall, call->id());
if (function_state()->outer() != NULL) {
// We are generating graph for inlined function. Currently
// constructor inlining is not supported and we can just return
// false from %_IsConstructCall().
ast_context()->ReturnValue(graph()->GetConstantFalse());
} else {
ast_context()->ReturnInstruction(new HIsConstructCall, call->id());
}
}
......
......@@ -544,6 +544,8 @@ class FunctionState BASE_EMBEDDED {
test_context_ = NULL;
}
FunctionState* outer() { return outer_; }
private:
HGraphBuilder* owner_;
......
......@@ -108,7 +108,7 @@ class JavaScriptFrameConstants : public AllStatic {
public:
// FP-relative.
static const int kLocal0Offset = StandardFrameConstants::kExpressionsOffset;
static const int kSavedRegistersOffset = +2 * kPointerSize;
static const int kLastParameterOffset = +2 * kPointerSize;
static const int kFunctionOffset = StandardFrameConstants::kMarkerOffset;
// Caller SP-relative.
......
......@@ -147,7 +147,7 @@ class JavaScriptFrameConstants : public AllStatic {
public:
// FP-relative.
static const int kLocal0Offset = StandardFrameConstants::kExpressionsOffset;
static const int kSavedRegistersOffset = +2 * kPointerSize;
static const int kLastParameterOffset = +2 * kPointerSize;
static const int kFunctionOffset = StandardFrameConstants::kMarkerOffset;
// Caller SP-relative.
......
......@@ -7066,6 +7066,46 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_NewClosure) {
return *result;
}
static SmartPointer<Object**> GetNonBoundArguments(int bound_argc,
int* total_argc) {
// Find frame containing arguments passed to the caller.
JavaScriptFrameIterator it;
JavaScriptFrame* frame = it.frame();
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);
*total_argc = bound_argc + args_count;
SmartPointer<Object**> param_data(NewArray<Object**>(*total_argc));
for (int i = 0; i < args_count; i++) {
Handle<Object> val = args_slots[i].GetValue();
param_data[bound_argc + i] = val.location();
}
return param_data;
} else {
it.AdvanceToArgumentsFrame();
frame = it.frame();
int args_count = frame->ComputeParametersCount();
*total_argc = bound_argc + args_count;
SmartPointer<Object**> param_data(NewArray<Object**>(*total_argc));
for (int i = 0; i < args_count; i++) {
Handle<Object> val = Handle<Object>(frame->GetParameter(i));
param_data[bound_argc + i] = val.location();
}
return param_data;
}
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_NewObjectFromBound) {
HandleScope scope(isolate);
ASSERT(args.length() == 2);
......@@ -7073,34 +7113,22 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_NewObjectFromBound) {
CONVERT_ARG_CHECKED(JSFunction, function, 0);
// Second argument is either null or an array of bound arguments.
FixedArray* bound_args = NULL;
Handle<FixedArray> bound_args;
int bound_argc = 0;
if (!args[1]->IsNull()) {
CONVERT_ARG_CHECKED(JSArray, params, 1);
RUNTIME_ASSERT(params->HasFastElements());
bound_args = FixedArray::cast(params->elements());
bound_args = Handle<FixedArray>(FixedArray::cast(params->elements()));
bound_argc = Smi::cast(params->length())->value();
}
// Find frame containing arguments passed to the caller.
JavaScriptFrameIterator it;
JavaScriptFrame* frame = it.frame();
ASSERT(!frame->is_optimized());
it.AdvanceToArgumentsFrame();
frame = it.frame();
int argc = frame->ComputeParametersCount();
// Prepend bound arguments to caller's arguments.
int total_argc = bound_argc + argc;
SmartPointer<Object**> param_data(NewArray<Object**>(total_argc));
int total_argc = 0;
SmartPointer<Object**> param_data =
GetNonBoundArguments(bound_argc, &total_argc);
for (int i = 0; i < bound_argc; i++) {
Handle<Object> val = Handle<Object>(bound_args->get(i));
param_data[i] = val.location();
}
for (int i = 0; i < argc; i++) {
Handle<Object> val = Handle<Object>(frame->GetParameter(i));
param_data[bound_argc + i] = val.location();
}
bool exception = false;
Handle<Object> result =
......
......@@ -99,7 +99,7 @@ class JavaScriptFrameConstants : public AllStatic {
public:
// FP-relative.
static const int kLocal0Offset = StandardFrameConstants::kExpressionsOffset;
static const int kSavedRegistersOffset = +2 * kPointerSize;
static const int kLastParameterOffset = +2 * kPointerSize;
static const int kFunctionOffset = StandardFrameConstants::kMarkerOffset;
// Caller SP-relative.
......
// Copyright 2011 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
// Check that %NewObjectFromBound works correctly when called from optimized
// frame.
function foo(x, y, z) {
assertEquals(1, x);
assertEquals(2, y);
assertEquals(3, z);
}
var bound_arg = [1];
function f(y, z) {
return %NewObjectFromBound(foo, bound_arg);
}
// 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 */
}
// Check that %NewObjectFromBound looks at correct frame for inlined function.
function ff(x) { }
function h(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 */
}
for (var i = 0; i < 100000; i++) f(2, 3);
for (var i = 0; i < 100000; i++) g(3, 2);
for (var i = 0; i < 100000; i++) h(6, 4);
// Check that %_IsConstructCall returns correct value when inlined
var NON_CONSTRUCT_MARKER = {};
var CONSTRUCT_MARKER = {};
function baz() {
return (!%_IsConstructCall()) ? NON_CONSTRUCT_MARKER : CONSTRUCT_MARKER;
}
function bar(x, y, z) {
var non_construct = baz(); /* baz should be inlined */
assertEquals(non_construct, NON_CONSTRUCT_MARKER);
var construct = new baz();
assertEquals(construct, CONSTRUCT_MARKER);
}
for (var i = 0; i < 100000; i++) new bar(1, 2, 3);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment