Commit 99ce5a24 authored by jarin@chromium.org's avatar jarin@chromium.org

The current

version is passing all the existing test + a bunch of new tests
(packaged in the change list, too).

The patch extends the SlotRef object to describe captured and duplicated
objects. Since the SlotRefs are not independent of each other anymore,
there is a new SlotRefValueBuilder class that stores the SlotRefs and
later materializes the objects from the SlotRefs.

Note that unlike the previous implementation of SlotRefs, we now build
the SlotRef entries for the entire frame, not just the particular
function.  This is because duplicate objects might refer to previous
captured objects (that might live inside other inlined function's part
of the frame).

We also need to store the materialized objects between other potential
invocations of the same arguments object so that we materialize each
captured object at most once.  The materialized objects of frames live
in the new MaterielizedObjectStore object (contained in Isolate),
indexed by the frame's FP address.  Each argument materialization (and
deoptimization) tries to lookup its captured objects in the store before
building new ones.  Deoptimization also removes the materialized objects
from the store. We also schedule a lazy deopt to be sure that we always
get rid of the materialized objects and that the optmized function
adopts the materialized objects (instead of happily computing with its
captured representations).

Concerns:

- Is the FP address the right key for a frame? (Note that deoptimizer's
representation of frame is different from the argument object
materializer's one - it is not easy to find common ground.)

- Performance is suboptimal in several places, but a quick local run of
benchmarks does not seem to show a perf hit. Examples of possible
improvements: smarter generation of SlotRefs (build other functions'
SlotRefs only for captured objects and only if necessary), smarter
lookup of stored materialized objects.

- Ideally, we would like to share the code for argument materialization
with deoptimizer's materializer.  However, the supporting data structures
(mainly the frame descriptor) are quite different in each case, so it
looks more like a separate project.

Thanks for any feedback.

R=danno@chromium.org, mstarzinger@chromium.org
LOG=N
BUG=

Committed: https://code.google.com/p/v8/source/detail?r=18918

Review URL: https://codereview.chromium.org/103243005

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@18936 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 32f45eb7
...@@ -5398,7 +5398,7 @@ class Internals { ...@@ -5398,7 +5398,7 @@ class Internals {
static const int kNullValueRootIndex = 7; static const int kNullValueRootIndex = 7;
static const int kTrueValueRootIndex = 8; static const int kTrueValueRootIndex = 8;
static const int kFalseValueRootIndex = 9; static const int kFalseValueRootIndex = 9;
static const int kEmptyStringRootIndex = 145; static const int kEmptyStringRootIndex = 146;
static const int kNodeClassIdOffset = 1 * kApiPointerSize; static const int kNodeClassIdOffset = 1 * kApiPointerSize;
static const int kNodeFlagsOffset = 1 * kApiPointerSize + 3; static const int kNodeFlagsOffset = 1 * kApiPointerSize + 3;
......
...@@ -706,21 +706,22 @@ static MaybeObject* ConstructArgumentsObjectForInlinedFunction( ...@@ -706,21 +706,22 @@ static MaybeObject* ConstructArgumentsObjectForInlinedFunction(
int inlined_frame_index) { int inlined_frame_index) {
Isolate* isolate = inlined_function->GetIsolate(); Isolate* isolate = inlined_function->GetIsolate();
Factory* factory = isolate->factory(); Factory* factory = isolate->factory();
Vector<SlotRef> args_slots = SlotRefValueBuilder slot_refs(
SlotRef::ComputeSlotMappingForArguments(
frame, frame,
inlined_frame_index, inlined_frame_index,
inlined_function->shared()->formal_parameter_count()); inlined_function->shared()->formal_parameter_count());
int args_count = args_slots.length();
int args_count = slot_refs.args_length();
Handle<JSObject> arguments = Handle<JSObject> arguments =
factory->NewArgumentsObject(inlined_function, args_count); factory->NewArgumentsObject(inlined_function, args_count);
Handle<FixedArray> array = factory->NewFixedArray(args_count); Handle<FixedArray> array = factory->NewFixedArray(args_count);
slot_refs.Prepare(isolate);
for (int i = 0; i < args_count; ++i) { for (int i = 0; i < args_count; ++i) {
Handle<Object> value = args_slots[i].GetValue(isolate); Handle<Object> value = slot_refs.GetNext(isolate, 0);
array->set(i, *value); array->set(i, *value);
} }
slot_refs.Finish(isolate);
arguments->set_elements(*array); arguments->set_elements(*array);
args_slots.Dispose();
// Return the freshly allocated arguments object. // Return the freshly allocated arguments object.
return *arguments; return *arguments;
......
...@@ -773,6 +773,11 @@ void Deoptimizer::DoComputeOutputFrames() { ...@@ -773,6 +773,11 @@ void Deoptimizer::DoComputeOutputFrames() {
} }
output_count_ = count; output_count_ = count;
Register fp_reg = JavaScriptFrame::fp_register();
stack_fp_ = reinterpret_cast<Address>(
input_->GetRegister(fp_reg.code()) +
has_alignment_padding_ * kPointerSize);
// Translate each output frame. // Translate each output frame.
for (int i = 0; i < count; ++i) { for (int i = 0; i < count; ++i) {
// Read the ast node id, function, and frame height for this output frame. // Read the ast node id, function, and frame height for this output frame.
...@@ -1777,14 +1782,24 @@ Handle<Object> Deoptimizer::MaterializeNextHeapObject() { ...@@ -1777,14 +1782,24 @@ Handle<Object> Deoptimizer::MaterializeNextHeapObject() {
// Reuse the HeapNumber value directly as it is already properly // Reuse the HeapNumber value directly as it is already properly
// tagged and skip materializing the HeapNumber explicitly. // tagged and skip materializing the HeapNumber explicitly.
Handle<Object> object = MaterializeNextValue(); Handle<Object> object = MaterializeNextValue();
if (object_index < prev_materialized_count_) {
materialized_objects_->Add(Handle<Object>(
previously_materialized_objects_->get(object_index), isolate_));
} else {
materialized_objects_->Add(object); materialized_objects_->Add(object);
}
materialization_value_index_ += kDoubleSize / kPointerSize - 1; materialization_value_index_ += kDoubleSize / kPointerSize - 1;
break; break;
} }
case JS_OBJECT_TYPE: { case JS_OBJECT_TYPE: {
Handle<JSObject> object = Handle<JSObject> object =
isolate_->factory()->NewJSObjectFromMap(map, NOT_TENURED, false); isolate_->factory()->NewJSObjectFromMap(map, NOT_TENURED, false);
if (object_index < prev_materialized_count_) {
materialized_objects_->Add(Handle<Object>(
previously_materialized_objects_->get(object_index), isolate_));
} else {
materialized_objects_->Add(object); materialized_objects_->Add(object);
}
Handle<Object> properties = MaterializeNextValue(); Handle<Object> properties = MaterializeNextValue();
Handle<Object> elements = MaterializeNextValue(); Handle<Object> elements = MaterializeNextValue();
object->set_properties(FixedArray::cast(*properties)); object->set_properties(FixedArray::cast(*properties));
...@@ -1798,7 +1813,12 @@ Handle<Object> Deoptimizer::MaterializeNextHeapObject() { ...@@ -1798,7 +1813,12 @@ Handle<Object> Deoptimizer::MaterializeNextHeapObject() {
case JS_ARRAY_TYPE: { case JS_ARRAY_TYPE: {
Handle<JSArray> object = Handle<JSArray> object =
isolate_->factory()->NewJSArray(0, map->elements_kind()); isolate_->factory()->NewJSArray(0, map->elements_kind());
if (object_index < prev_materialized_count_) {
materialized_objects_->Add(Handle<Object>(
previously_materialized_objects_->get(object_index), isolate_));
} else {
materialized_objects_->Add(object); materialized_objects_->Add(object);
}
Handle<Object> properties = MaterializeNextValue(); Handle<Object> properties = MaterializeNextValue();
Handle<Object> elements = MaterializeNextValue(); Handle<Object> elements = MaterializeNextValue();
Handle<Object> length = MaterializeNextValue(); Handle<Object> length = MaterializeNextValue();
...@@ -1831,6 +1851,12 @@ Handle<Object> Deoptimizer::MaterializeNextValue() { ...@@ -1831,6 +1851,12 @@ Handle<Object> Deoptimizer::MaterializeNextValue() {
void Deoptimizer::MaterializeHeapObjects(JavaScriptFrameIterator* it) { void Deoptimizer::MaterializeHeapObjects(JavaScriptFrameIterator* it) {
ASSERT_NE(DEBUGGER, bailout_type_); ASSERT_NE(DEBUGGER, bailout_type_);
MaterializedObjectStore* materialized_store =
isolate_->materialized_object_store();
previously_materialized_objects_ = materialized_store->Get(stack_fp_);
prev_materialized_count_ = previously_materialized_objects_.is_null() ?
0 : previously_materialized_objects_->length();
// Walk all JavaScript output frames with the given frame iterator. // Walk all JavaScript output frames with the given frame iterator.
for (int frame_index = 0; frame_index < jsframe_count(); ++frame_index) { for (int frame_index = 0; frame_index < jsframe_count(); ++frame_index) {
if (frame_index != 0) it->Advance(); if (frame_index != 0) it->Advance();
...@@ -1920,6 +1946,10 @@ void Deoptimizer::MaterializeHeapObjects(JavaScriptFrameIterator* it) { ...@@ -1920,6 +1946,10 @@ void Deoptimizer::MaterializeHeapObjects(JavaScriptFrameIterator* it) {
ASSERT(materialization_object_index_ == materialized_objects_->length()); ASSERT(materialization_object_index_ == materialized_objects_->length());
ASSERT(materialization_value_index_ == materialized_values_->length()); ASSERT(materialization_value_index_ == materialized_values_->length());
} }
if (prev_materialized_count_ > 0) {
materialized_store->Remove(stack_fp_);
}
} }
...@@ -2948,12 +2978,11 @@ const char* Translation::StringFor(Opcode opcode) { ...@@ -2948,12 +2978,11 @@ const char* Translation::StringFor(Opcode opcode) {
// We can't intermix stack decoding and allocations because // We can't intermix stack decoding and allocations because
// deoptimization infrastracture is not GC safe. // deoptimization infrastracture is not GC safe.
// Thus we build a temporary structure in malloced space. // Thus we build a temporary structure in malloced space.
SlotRef SlotRef::ComputeSlotForNextArgument(TranslationIterator* iterator, SlotRef SlotRefValueBuilder::ComputeSlotForNextArgument(
Translation::Opcode opcode,
TranslationIterator* iterator,
DeoptimizationInputData* data, DeoptimizationInputData* data,
JavaScriptFrame* frame) { JavaScriptFrame* frame) {
Translation::Opcode opcode =
static_cast<Translation::Opcode>(iterator->Next());
switch (opcode) { switch (opcode) {
case Translation::BEGIN: case Translation::BEGIN:
case Translation::JS_FRAME: case Translation::JS_FRAME:
...@@ -2964,12 +2993,18 @@ SlotRef SlotRef::ComputeSlotForNextArgument(TranslationIterator* iterator, ...@@ -2964,12 +2993,18 @@ SlotRef SlotRef::ComputeSlotForNextArgument(TranslationIterator* iterator,
// Peeled off before getting here. // Peeled off before getting here.
break; break;
case Translation::DUPLICATED_OBJECT: case Translation::DUPLICATED_OBJECT: {
return SlotRef::NewDuplicateObject(iterator->Next());
}
case Translation::ARGUMENTS_OBJECT: case Translation::ARGUMENTS_OBJECT:
case Translation::CAPTURED_OBJECT:
// This can be only emitted for local slots not for argument slots. // This can be only emitted for local slots not for argument slots.
break; break;
case Translation::CAPTURED_OBJECT: {
return SlotRef::NewDeferredObject(iterator->Next());
}
case Translation::REGISTER: case Translation::REGISTER:
case Translation::INT32_REGISTER: case Translation::INT32_REGISTER:
case Translation::UINT32_REGISTER: case Translation::UINT32_REGISTER:
...@@ -3019,28 +3054,12 @@ SlotRef SlotRef::ComputeSlotForNextArgument(TranslationIterator* iterator, ...@@ -3019,28 +3054,12 @@ SlotRef SlotRef::ComputeSlotForNextArgument(TranslationIterator* iterator,
} }
void SlotRef::ComputeSlotsForArguments(Vector<SlotRef>* args_slots, SlotRefValueBuilder::SlotRefValueBuilder(JavaScriptFrame* frame,
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 inlined_jsframe_index,
int formal_parameter_count) { int formal_parameter_count)
: current_slot_(0), args_length_(-1), first_slot_index_(-1) {
DisallowHeapAllocation no_gc; DisallowHeapAllocation no_gc;
int deopt_index = Safepoint::kNoDeoptimizationIndex; int deopt_index = Safepoint::kNoDeoptimizationIndex;
DeoptimizationInputData* data = DeoptimizationInputData* data =
static_cast<OptimizedFrame*>(frame)->GetDeoptimizationData(&deopt_index); static_cast<OptimizedFrame*>(frame)->GetDeoptimizationData(&deopt_index);
...@@ -3049,12 +3068,18 @@ Vector<SlotRef> SlotRef::ComputeSlotMappingForArguments( ...@@ -3049,12 +3068,18 @@ Vector<SlotRef> SlotRef::ComputeSlotMappingForArguments(
Translation::Opcode opcode = static_cast<Translation::Opcode>(it.Next()); Translation::Opcode opcode = static_cast<Translation::Opcode>(it.Next());
ASSERT(opcode == Translation::BEGIN); ASSERT(opcode == Translation::BEGIN);
it.Next(); // Drop frame count. it.Next(); // Drop frame count.
stack_frame_id_ = frame->fp();
int jsframe_count = it.Next(); int jsframe_count = it.Next();
USE(jsframe_count); USE(jsframe_count);
ASSERT(jsframe_count > inlined_jsframe_index); ASSERT(jsframe_count > inlined_jsframe_index);
int jsframes_to_skip = inlined_jsframe_index; int jsframes_to_skip = inlined_jsframe_index;
while (true) { int number_of_slots = -1; // Number of slots inside our frame (yet unknown)
bool should_deopt = false;
while (number_of_slots != 0) {
opcode = static_cast<Translation::Opcode>(it.Next()); opcode = static_cast<Translation::Opcode>(it.Next());
bool processed = false;
if (opcode == Translation::ARGUMENTS_ADAPTOR_FRAME) { if (opcode == Translation::ARGUMENTS_ADAPTOR_FRAME) {
if (jsframes_to_skip == 0) { if (jsframes_to_skip == 0) {
ASSERT(Translation::NumberOfOperandsFor(opcode) == 2); ASSERT(Translation::NumberOfOperandsFor(opcode) == 2);
...@@ -3062,36 +3087,339 @@ Vector<SlotRef> SlotRef::ComputeSlotMappingForArguments( ...@@ -3062,36 +3087,339 @@ Vector<SlotRef> SlotRef::ComputeSlotMappingForArguments(
it.Skip(1); // literal id it.Skip(1); // literal id
int height = it.Next(); int height = it.Next();
// Skip the translation command for the receiver.
it.Skip(Translation::NumberOfOperandsFor(
static_cast<Translation::Opcode>(it.Next())));
// We reached the arguments adaptor frame corresponding to the // We reached the arguments adaptor frame corresponding to the
// inlined function in question. Number of arguments is height - 1. // inlined function in question. Number of arguments is height - 1.
Vector<SlotRef> args_slots = first_slot_index_ = slot_refs_.length();
Vector<SlotRef>::New(height - 1); // Minus receiver. args_length_ = height - 1;
ComputeSlotsForArguments(&args_slots, &it, data, frame); number_of_slots = height - 1;
return args_slots; processed = true;
} }
} else if (opcode == Translation::JS_FRAME) { } else if (opcode == Translation::JS_FRAME) {
if (jsframes_to_skip == 0) { if (jsframes_to_skip == 0) {
// Skip over operands to advance to the next opcode. // Skip over operands to advance to the next opcode.
it.Skip(Translation::NumberOfOperandsFor(opcode)); it.Skip(Translation::NumberOfOperandsFor(opcode));
// Skip the translation command for the receiver.
it.Skip(Translation::NumberOfOperandsFor(
static_cast<Translation::Opcode>(it.Next())));
// We reached the frame corresponding to the inlined function // We reached the frame corresponding to the inlined function
// in question. Process the translation commands for the // in question. Process the translation commands for the
// arguments. Number of arguments is equal to the number of // arguments. Number of arguments is equal to the number of
// format parameter count. // format parameter count.
Vector<SlotRef> args_slots = first_slot_index_ = slot_refs_.length();
Vector<SlotRef>::New(formal_parameter_count); args_length_ = formal_parameter_count;
ComputeSlotsForArguments(&args_slots, &it, data, frame); number_of_slots = formal_parameter_count;
return args_slots; processed = true;
} }
jsframes_to_skip--; jsframes_to_skip--;
} else if (opcode != Translation::BEGIN &&
opcode != Translation::CONSTRUCT_STUB_FRAME &&
opcode != Translation::GETTER_STUB_FRAME &&
opcode != Translation::SETTER_STUB_FRAME &&
opcode != Translation::COMPILED_STUB_FRAME) {
slot_refs_.Add(ComputeSlotForNextArgument(opcode, &it, data, frame));
if (first_slot_index_ >= 0) {
// We have found the beginning of our frame -> make sure we count
// the nested slots of captured objects
number_of_slots--;
SlotRef& slot = slot_refs_.last();
if (slot.Representation() == SlotRef::DEFERRED_OBJECT) {
number_of_slots += slot.DeferredObjectLength();
}
if (slot.Representation() == SlotRef::DEFERRED_OBJECT ||
slot.Representation() == SlotRef::DUPLICATE_OBJECT) {
should_deopt = true;
}
} }
processed = true;
}
if (!processed) {
// Skip over operands to advance to the next opcode. // Skip over operands to advance to the next opcode.
it.Skip(Translation::NumberOfOperandsFor(opcode)); it.Skip(Translation::NumberOfOperandsFor(opcode));
} }
}
if (should_deopt) {
List<JSFunction*> functions(2);
frame->GetFunctions(&functions);
Deoptimizer::DeoptimizeFunction(functions[0]);
}
}
Handle<Object> SlotRef::GetValue(Isolate* isolate) {
switch (representation_) {
case TAGGED:
return Handle<Object>(Memory::Object_at(addr_), isolate);
case INT32: {
int value = Memory::int32_at(addr_);
if (Smi::IsValid(value)) {
return Handle<Object>(Smi::FromInt(value), isolate);
} else {
return isolate->factory()->NewNumberFromInt(value);
}
}
case UINT32: {
uint32_t value = Memory::uint32_at(addr_);
if (value <= static_cast<uint32_t>(Smi::kMaxValue)) {
return Handle<Object>(Smi::FromInt(static_cast<int>(value)), isolate);
} else {
return isolate->factory()->NewNumber(static_cast<double>(value));
}
}
case DOUBLE: {
double value = read_double_value(addr_);
return isolate->factory()->NewNumber(value);
}
case LITERAL:
return literal_;
default:
UNREACHABLE();
return Handle<Object>::null();
}
}
void SlotRefValueBuilder::Prepare(Isolate* isolate) {
MaterializedObjectStore* materialized_store =
isolate->materialized_object_store();
previously_materialized_objects_ = materialized_store->Get(stack_frame_id_);
prev_materialized_count_ = previously_materialized_objects_.is_null()
? 0 : previously_materialized_objects_->length();
// Skip any materialized objects of the inlined "parent" frames.
// (Note that we still need to materialize them because they might be
// referred to as duplicated objects.)
while (current_slot_ < first_slot_index_) {
GetNext(isolate, 0);
}
ASSERT(current_slot_ == first_slot_index_);
}
Handle<Object> SlotRefValueBuilder::GetPreviouslyMaterialized(
Isolate* isolate, int length) {
int object_index = materialized_objects_.length();
Handle<Object> return_value = Handle<Object>(
previously_materialized_objects_->get(object_index), isolate);
materialized_objects_.Add(return_value);
// Now need to skip all nested objects (and possibly read them from
// the materialization store, too)
for (int i = 0; i < length; i++) {
SlotRef& slot = slot_refs_[current_slot_];
current_slot_++;
// For nested deferred objects, we need to read its properties
if (slot.Representation() == SlotRef::DEFERRED_OBJECT) {
length += slot.DeferredObjectLength();
}
// For nested deferred and duplicate objects, we need to put them into
// our materialization array
if (slot.Representation() == SlotRef::DEFERRED_OBJECT ||
slot.Representation() == SlotRef::DUPLICATE_OBJECT) {
int nested_object_index = materialized_objects_.length();
Handle<Object> nested_object = Handle<Object>(
previously_materialized_objects_->get(nested_object_index),
isolate);
materialized_objects_.Add(nested_object);
}
}
return return_value;
}
Handle<Object> SlotRefValueBuilder::GetNext(Isolate* isolate, int lvl) {
SlotRef& slot = slot_refs_[current_slot_];
current_slot_++;
switch (slot.Representation()) {
case SlotRef::TAGGED:
case SlotRef::INT32:
case SlotRef::UINT32:
case SlotRef::DOUBLE:
case SlotRef::LITERAL: {
return slot.GetValue(isolate);
}
case SlotRef::DEFERRED_OBJECT: {
int length = slot.DeferredObjectLength();
ASSERT(slot_refs_[current_slot_].Representation() == SlotRef::LITERAL ||
slot_refs_[current_slot_].Representation() == SlotRef::TAGGED);
int object_index = materialized_objects_.length();
if (object_index < prev_materialized_count_) {
return GetPreviouslyMaterialized(isolate, length);
}
Handle<Object> map_object = slot_refs_[current_slot_].GetValue(isolate);
Handle<Map> map = Map::GeneralizeAllFieldRepresentations(
Handle<Map>::cast(map_object), Representation::Tagged());
current_slot_++;
// TODO(jarin) this should be unified with the code in
// Deoptimizer::MaterializeNextHeapObject()
switch (map->instance_type()) {
case HEAP_NUMBER_TYPE: {
// Reuse the HeapNumber value directly as it is already properly
// tagged and skip materializing the HeapNumber explicitly.
Handle<Object> object = GetNext(isolate, lvl + 1);
materialized_objects_.Add(object);
return object;
}
case JS_OBJECT_TYPE: {
Handle<JSObject> object =
isolate->factory()->NewJSObjectFromMap(map, NOT_TENURED, false);
materialized_objects_.Add(object);
Handle<Object> properties = GetNext(isolate, lvl + 1);
Handle<Object> elements = GetNext(isolate, lvl + 1);
object->set_properties(FixedArray::cast(*properties));
object->set_elements(FixedArrayBase::cast(*elements));
for (int i = 0; i < length - 3; ++i) {
Handle<Object> value = GetNext(isolate, lvl + 1);
object->FastPropertyAtPut(i, *value);
}
return object;
}
case JS_ARRAY_TYPE: {
Handle<JSArray> object =
isolate->factory()->NewJSArray(0, map->elements_kind());
materialized_objects_.Add(object);
Handle<Object> properties = GetNext(isolate, lvl + 1);
Handle<Object> elements = GetNext(isolate, lvl + 1);
Handle<Object> length = GetNext(isolate, lvl + 1);
object->set_properties(FixedArray::cast(*properties));
object->set_elements(FixedArrayBase::cast(*elements));
object->set_length(*length);
return object;
}
default:
PrintF(stderr,
"[couldn't handle instance type %d]\n", map->instance_type());
UNREACHABLE();
break;
}
UNREACHABLE();
}
case SlotRef::DUPLICATE_OBJECT: {
int object_index = slot.DuplicateObjectId();
Handle<Object> object = materialized_objects_[object_index];
materialized_objects_.Add(object);
return object;
}
default:
UNREACHABLE();
break;
}
UNREACHABLE(); UNREACHABLE();
return Vector<SlotRef>(); return Handle<Object>::null();
}
void SlotRefValueBuilder::Finish(Isolate* isolate) {
// We should have processed all slot
ASSERT(slot_refs_.length() == current_slot_);
if (materialized_objects_.length() > prev_materialized_count_) {
// We have materialized some new objects, so we have to store them
// to prevent duplicate materialization
Handle<FixedArray> array = isolate->factory()->NewFixedArray(
materialized_objects_.length());
for (int i = 0; i < materialized_objects_.length(); i++) {
array->set(i, *(materialized_objects_.at(i)));
}
isolate->materialized_object_store()->Set(stack_frame_id_, array);
}
}
Handle<FixedArray> MaterializedObjectStore::Get(Address fp) {
int index = StackIdToIndex(fp);
if (index == -1) {
return Handle<FixedArray>::null();
}
Handle<FixedArray> array = GetStackEntries();
ASSERT(array->length() > index);
return Handle<FixedArray>::cast(Handle<Object>(array->get(index),
isolate()));
}
void MaterializedObjectStore::Set(Address fp,
Handle<FixedArray> materialized_objects) {
int index = StackIdToIndex(fp);
if (index == -1) {
index = frame_fps_.length();
frame_fps_.Add(fp);
}
Handle<FixedArray> array = EnsureStackEntries(index + 1);
array->set(index, *materialized_objects);
}
void MaterializedObjectStore::Remove(Address fp) {
int index = StackIdToIndex(fp);
ASSERT(index >= 0);
frame_fps_.Remove(index);
Handle<FixedArray> array = GetStackEntries();
ASSERT(array->length() > index);
for (int i = index; i < frame_fps_.length(); i++) {
array->set(i, array->get(i + 1));
}
array->set(frame_fps_.length(), isolate()->heap()->undefined_value());
}
int MaterializedObjectStore::StackIdToIndex(Address fp) {
for (int i = 0; i < frame_fps_.length(); i++) {
if (frame_fps_[i] == fp) {
return i;
}
}
return -1;
}
Handle<FixedArray> MaterializedObjectStore::GetStackEntries() {
return Handle<FixedArray>(isolate()->heap()->materialized_objects());
}
Handle<FixedArray> MaterializedObjectStore::EnsureStackEntries(int length) {
Handle<FixedArray> array = GetStackEntries();
if (array->length() >= length) {
return array;
}
int new_length = length > 10 ? length : 10;
if (new_length < 2 * array->length()) {
new_length = 2 * array->length();
}
Handle<FixedArray> new_array =
isolate()->factory()->NewFixedArray(new_length, TENURED);
for (int i = 0; i < array->length(); i++) {
new_array->set(i, array->get(i));
}
for (int i = array->length(); i < length; i++) {
new_array->set(i, isolate()->heap()->undefined_value());
}
isolate()->heap()->public_set_materialized_objects(*new_array);
return new_array;
} }
#ifdef ENABLE_DEBUGGER_SUPPORT #ifdef ENABLE_DEBUGGER_SUPPORT
......
...@@ -435,6 +435,11 @@ class Deoptimizer : public Malloced { ...@@ -435,6 +435,11 @@ class Deoptimizer : public Malloced {
List<ObjectMaterializationDescriptor> deferred_objects_; List<ObjectMaterializationDescriptor> deferred_objects_;
List<HeapNumberMaterializationDescriptor<Address> > deferred_heap_numbers_; List<HeapNumberMaterializationDescriptor<Address> > deferred_heap_numbers_;
// Key for lookup of previously materialized objects
Address stack_fp_;
Handle<FixedArray> previously_materialized_objects_;
int prev_materialized_count_;
// Output frame information. Only used during heap object materialization. // Output frame information. Only used during heap object materialization.
List<Handle<JSFunction> > jsframe_functions_; List<Handle<JSFunction> > jsframe_functions_;
List<bool> jsframe_has_adapted_arguments_; List<bool> jsframe_has_adapted_arguments_;
...@@ -783,7 +788,13 @@ class SlotRef BASE_EMBEDDED { ...@@ -783,7 +788,13 @@ class SlotRef BASE_EMBEDDED {
INT32, INT32,
UINT32, UINT32,
DOUBLE, DOUBLE,
LITERAL LITERAL,
DEFERRED_OBJECT, // Object captured by the escape analysis.
// The number of nested objects can be obtained
// with the DeferredObjectLength() method
// (the SlotRefs of the nested objects follow
// this SlotRef in the depth-first order.)
DUPLICATE_OBJECT // Duplicated object of a deferred object.
}; };
SlotRef() SlotRef()
...@@ -795,52 +806,66 @@ class SlotRef BASE_EMBEDDED { ...@@ -795,52 +806,66 @@ class SlotRef BASE_EMBEDDED {
SlotRef(Isolate* isolate, Object* literal) SlotRef(Isolate* isolate, Object* literal)
: literal_(literal, isolate), representation_(LITERAL) { } : literal_(literal, isolate), representation_(LITERAL) { }
Handle<Object> GetValue(Isolate* isolate) { static SlotRef NewDeferredObject(int length) {
switch (representation_) { SlotRef slot;
case TAGGED: slot.representation_ = DEFERRED_OBJECT;
return Handle<Object>(Memory::Object_at(addr_), isolate); slot.deferred_object_length_ = length;
return slot;
case INT32: {
int value = Memory::int32_at(addr_);
if (Smi::IsValid(value)) {
return Handle<Object>(Smi::FromInt(value), isolate);
} else {
return isolate->factory()->NewNumberFromInt(value);
}
} }
case UINT32: { SlotRepresentation Representation() { return representation_; }
uint32_t value = Memory::uint32_at(addr_);
if (value <= static_cast<uint32_t>(Smi::kMaxValue)) {
return Handle<Object>(Smi::FromInt(static_cast<int>(value)), isolate);
} else {
return isolate->factory()->NewNumber(static_cast<double>(value));
}
}
case DOUBLE: { static SlotRef NewDuplicateObject(int id) {
double value = read_double_value(addr_); SlotRef slot;
return isolate->factory()->NewNumber(value); slot.representation_ = DUPLICATE_OBJECT;
slot.duplicate_object_id_ = id;
return slot;
} }
case LITERAL: int DeferredObjectLength() { return deferred_object_length_; }
return literal_;
default: int DuplicateObjectId() { return duplicate_object_id_; }
UNREACHABLE();
return Handle<Object>::null();
}
}
static Vector<SlotRef> ComputeSlotMappingForArguments( Handle<Object> GetValue(Isolate* isolate);
JavaScriptFrame* frame,
int inlined_frame_index,
int formal_parameter_count);
private: private:
Address addr_; Address addr_;
Handle<Object> literal_; Handle<Object> literal_;
SlotRepresentation representation_; SlotRepresentation representation_;
int deferred_object_length_;
int duplicate_object_id_;
};
class SlotRefValueBuilder BASE_EMBEDDED {
public:
SlotRefValueBuilder(
JavaScriptFrame* frame,
int inlined_frame_index,
int formal_parameter_count);
void Prepare(Isolate* isolate);
Handle<Object> GetNext(Isolate* isolate, int level);
void Finish(Isolate* isolate);
int args_length() { return args_length_; }
private:
List<Handle<Object> > materialized_objects_;
Handle<FixedArray> previously_materialized_objects_;
int prev_materialized_count_;
Address stack_frame_id_;
List<SlotRef> slot_refs_;
int current_slot_;
int args_length_;
int first_slot_index_;
static SlotRef ComputeSlotForNextArgument(
Translation::Opcode opcode,
TranslationIterator* iterator,
DeoptimizationInputData* data,
JavaScriptFrame* frame);
Handle<Object> GetPreviouslyMaterialized(Isolate* isolate, int length);
static Address SlotAddress(JavaScriptFrame* frame, int slot_index) { static Address SlotAddress(JavaScriptFrame* frame, int slot_index) {
if (slot_index >= 0) { if (slot_index >= 0) {
...@@ -852,15 +877,27 @@ class SlotRef BASE_EMBEDDED { ...@@ -852,15 +877,27 @@ class SlotRef BASE_EMBEDDED {
} }
} }
static SlotRef ComputeSlotForNextArgument(TranslationIterator* iterator, Handle<Object> GetDeferredObject(Isolate* isolate);
DeoptimizationInputData* data, };
JavaScriptFrame* frame);
static void ComputeSlotsForArguments( class MaterializedObjectStore {
Vector<SlotRef>* args_slots, public:
TranslationIterator* iterator, explicit MaterializedObjectStore(Isolate* isolate) : isolate_(isolate) {
DeoptimizationInputData* data, }
JavaScriptFrame* frame);
Handle<FixedArray> Get(Address fp);
void Set(Address fp, Handle<FixedArray> materialized_objects);
void Remove(Address fp);
private:
Isolate* isolate() { return isolate_; }
Handle<FixedArray> GetStackEntries();
Handle<FixedArray> EnsureStackEntries(int size);
int StackIdToIndex(Address fp);
Isolate* isolate_;
List<Address> frame_fps_;
}; };
......
...@@ -3290,6 +3290,11 @@ bool Heap::CreateInitialObjects() { ...@@ -3290,6 +3290,11 @@ bool Heap::CreateInitialObjects() {
Symbol::cast(obj)->set_is_private(true); Symbol::cast(obj)->set_is_private(true);
set_observed_symbol(Symbol::cast(obj)); set_observed_symbol(Symbol::cast(obj));
{ MaybeObject* maybe_obj = AllocateFixedArray(0, TENURED);
if (!maybe_obj->ToObject(&obj)) return false;
}
set_materialized_objects(FixedArray::cast(obj));
// Handling of script id generation is in Factory::NewScript. // Handling of script id generation is in Factory::NewScript.
set_last_script_id(Smi::FromInt(v8::Script::kNoScriptId)); set_last_script_id(Smi::FromInt(v8::Script::kNoScriptId));
......
...@@ -201,7 +201,8 @@ namespace internal { ...@@ -201,7 +201,8 @@ namespace internal {
V(Symbol, elements_transition_symbol, ElementsTransitionSymbol) \ V(Symbol, elements_transition_symbol, ElementsTransitionSymbol) \
V(SeededNumberDictionary, empty_slow_element_dictionary, \ V(SeededNumberDictionary, empty_slow_element_dictionary, \
EmptySlowElementDictionary) \ EmptySlowElementDictionary) \
V(Symbol, observed_symbol, ObservedSymbol) V(Symbol, observed_symbol, ObservedSymbol) \
V(FixedArray, materialized_objects, MaterializedObjects)
#define ROOT_LIST(V) \ #define ROOT_LIST(V) \
STRONG_ROOT_LIST(V) \ STRONG_ROOT_LIST(V) \
...@@ -1367,6 +1368,10 @@ class Heap { ...@@ -1367,6 +1368,10 @@ class Heap {
roots_[kStoreBufferTopRootIndex] = reinterpret_cast<Smi*>(top); roots_[kStoreBufferTopRootIndex] = reinterpret_cast<Smi*>(top);
} }
void public_set_materialized_objects(FixedArray* objects) {
roots_[kMaterializedObjectsRootIndex] = objects;
}
// Generated code can embed this address to get access to the roots. // Generated code can embed this address to get access to the roots.
Object** roots_array_start() { return roots_; } Object** roots_array_start() { return roots_; }
......
...@@ -1529,6 +1529,7 @@ Isolate::Isolate() ...@@ -1529,6 +1529,7 @@ Isolate::Isolate()
stats_table_(NULL), stats_table_(NULL),
stub_cache_(NULL), stub_cache_(NULL),
deoptimizer_data_(NULL), deoptimizer_data_(NULL),
materialized_object_store_(NULL),
capture_stack_trace_for_uncaught_exceptions_(false), capture_stack_trace_for_uncaught_exceptions_(false),
stack_trace_for_uncaught_exceptions_frame_limit_(0), stack_trace_for_uncaught_exceptions_frame_limit_(0),
stack_trace_for_uncaught_exceptions_options_(StackTrace::kOverview), stack_trace_for_uncaught_exceptions_options_(StackTrace::kOverview),
...@@ -1777,6 +1778,9 @@ Isolate::~Isolate() { ...@@ -1777,6 +1778,9 @@ Isolate::~Isolate() {
delete stats_table_; delete stats_table_;
stats_table_ = NULL; stats_table_ = NULL;
delete materialized_object_store_;
materialized_object_store_ = NULL;
delete logger_; delete logger_;
logger_ = NULL; logger_ = NULL;
...@@ -1947,6 +1951,7 @@ bool Isolate::Init(Deserializer* des) { ...@@ -1947,6 +1951,7 @@ bool Isolate::Init(Deserializer* des) {
bootstrapper_ = new Bootstrapper(this); bootstrapper_ = new Bootstrapper(this);
handle_scope_implementer_ = new HandleScopeImplementer(this); handle_scope_implementer_ = new HandleScopeImplementer(this);
stub_cache_ = new StubCache(this); stub_cache_ = new StubCache(this);
materialized_object_store_ = new MaterializedObjectStore(this);
regexp_stack_ = new RegExpStack(); regexp_stack_ = new RegExpStack();
regexp_stack_->isolate_ = this; regexp_stack_->isolate_ = this;
date_cache_ = new DateCache(); date_cache_ = new DateCache();
......
...@@ -51,12 +51,13 @@ namespace v8 { ...@@ -51,12 +51,13 @@ namespace v8 {
namespace internal { namespace internal {
class Bootstrapper; class Bootstrapper;
struct CallInterfaceDescriptor;
class CodeGenerator; class CodeGenerator;
class CodeRange; class CodeRange;
struct CodeStubInterfaceDescriptor; struct CodeStubInterfaceDescriptor;
struct CallInterfaceDescriptor;
class CodeTracer; class CodeTracer;
class CompilationCache; class CompilationCache;
class ConsStringIteratorOp;
class ContextSlotCache; class ContextSlotCache;
class Counters; class Counters;
class CpuFeatures; class CpuFeatures;
...@@ -73,19 +74,19 @@ class HeapProfiler; ...@@ -73,19 +74,19 @@ class HeapProfiler;
class HStatistics; class HStatistics;
class HTracer; class HTracer;
class InlineRuntimeFunctionsTable; class InlineRuntimeFunctionsTable;
class NoAllocationStringAllocator;
class InnerPointerToCodeCache; class InnerPointerToCodeCache;
class MaterializedObjectStore;
class NoAllocationStringAllocator;
class RandomNumberGenerator; class RandomNumberGenerator;
class RegExpStack; class RegExpStack;
class SaveContext; class SaveContext;
class UnicodeCache;
class ConsStringIteratorOp;
class StringTracker; class StringTracker;
class StubCache; class StubCache;
class SweeperThread; class SweeperThread;
class ThreadManager; class ThreadManager;
class ThreadState; class ThreadState;
class ThreadVisitor; // Defined in v8threads.h class ThreadVisitor; // Defined in v8threads.h
class UnicodeCache;
template <StateTag Tag> class VMState; template <StateTag Tag> class VMState;
// 'void function pointer', used to roundtrip the // 'void function pointer', used to roundtrip the
...@@ -869,6 +870,9 @@ class Isolate { ...@@ -869,6 +870,9 @@ class Isolate {
StubCache* stub_cache() { return stub_cache_; } StubCache* stub_cache() { return stub_cache_; }
DeoptimizerData* deoptimizer_data() { return deoptimizer_data_; } DeoptimizerData* deoptimizer_data() { return deoptimizer_data_; }
ThreadLocalTop* thread_local_top() { return &thread_local_top_; } ThreadLocalTop* thread_local_top() { return &thread_local_top_; }
MaterializedObjectStore* materialized_object_store() {
return materialized_object_store_;
}
MemoryAllocator* memory_allocator() { MemoryAllocator* memory_allocator() {
return memory_allocator_; return memory_allocator_;
...@@ -1275,6 +1279,7 @@ class Isolate { ...@@ -1275,6 +1279,7 @@ class Isolate {
StatsTable* stats_table_; StatsTable* stats_table_;
StubCache* stub_cache_; StubCache* stub_cache_;
DeoptimizerData* deoptimizer_data_; DeoptimizerData* deoptimizer_data_;
MaterializedObjectStore* materialized_object_store_;
ThreadLocalTop thread_local_top_; ThreadLocalTop thread_local_top_;
bool capture_stack_trace_for_uncaught_exceptions_; bool capture_stack_trace_for_uncaught_exceptions_;
int stack_trace_for_uncaught_exceptions_frame_limit_; int stack_trace_for_uncaught_exceptions_frame_limit_;
......
...@@ -8049,23 +8049,22 @@ static SmartArrayPointer<Handle<Object> > GetCallerArguments( ...@@ -8049,23 +8049,22 @@ static SmartArrayPointer<Handle<Object> > GetCallerArguments(
if (functions.length() > 1) { if (functions.length() > 1) {
int inlined_jsframe_index = functions.length() - 1; int inlined_jsframe_index = functions.length() - 1;
JSFunction* inlined_function = functions[inlined_jsframe_index]; JSFunction* inlined_function = functions[inlined_jsframe_index];
Vector<SlotRef> args_slots = SlotRefValueBuilder slot_refs(
SlotRef::ComputeSlotMappingForArguments(
frame, frame,
inlined_jsframe_index, inlined_jsframe_index,
inlined_function->shared()->formal_parameter_count()); inlined_function->shared()->formal_parameter_count());
int args_count = args_slots.length(); int args_count = slot_refs.args_length();
*total_argc = prefix_argc + args_count; *total_argc = prefix_argc + args_count;
SmartArrayPointer<Handle<Object> > param_data( SmartArrayPointer<Handle<Object> > param_data(
NewArray<Handle<Object> >(*total_argc)); NewArray<Handle<Object> >(*total_argc));
slot_refs.Prepare(isolate);
for (int i = 0; i < args_count; i++) { for (int i = 0; i < args_count; i++) {
Handle<Object> val = args_slots[i].GetValue(isolate); Handle<Object> val = slot_refs.GetNext(isolate, 0);
param_data[prefix_argc + i] = val; param_data[prefix_argc + i] = val;
} }
slot_refs.Finish(isolate);
args_slots.Dispose();
return param_data; return param_data;
} else { } else {
......
// Copyright 2013 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 --use-escape-analysis --expose-gc
// Simple test of capture
(function testCapturedArguments() {
function h() {
return g.arguments[0];
}
function g(x) {
return h();
}
function f() {
var l = { y : { z : 4 }, x : 2 }
var r = g(l);
assertEquals(2, r.x);
assertEquals(2, l.x);
l.x = 3;
l.y.z = 5;
// Test that the arguments object is properly
// aliased
assertEquals(3, r.x);
assertEquals(3, l.x);
assertEquals(5, r.y.z);
}
f(); f(); f();
%OptimizeFunctionOnNextCall(f);
f();
})();
// Get the arguments object twice, test aliasing
(function testTwoCapturedArguments() {
function h() {
return g.arguments[0];
}
function i() {
return g.arguments[0];
}
function g(x) {
return {h : h() , i : i()};
}
function f() {
var l = { y : { z : 4 }, x : 2 }
var r = g(l);
assertEquals(2, r.h.x)
l.y.z = 3;
assertEquals(3, r.h.y.z);
assertEquals(3, r.i.y.z);
}
f(); f(); f();
%OptimizeFunctionOnNextCall(f);
f();
})();
// Nested arguments object test
(function testTwoCapturedArgumentsNested() {
function i() {
return { gx : g.arguments[0], hx : h.arguments[0] };
}
function h(x) {
return i();
}
function g(x) {
return h(x.y);
}
function f() {
var l = { y : { z : 4 }, x : 2 }
var r = g(l);
assertEquals(2, r.gx.x)
assertEquals(4, r.gx.y.z)
assertEquals(4, r.hx.z)
l.y.z = 3;
assertEquals(3, r.gx.y.z)
assertEquals(3, r.hx.z)
assertEquals(3, l.y.z)
}
f(); f(); f();
%OptimizeFunctionOnNextCall(f);
f(); f();
%OptimizeFunctionOnNextCall(f);
f(); f();
})();
// Nested arguments object test with different inlining
(function testTwoCapturedArgumentsNested2() {
function i() {
return { gx : g.arguments[0], hx : h.arguments[0] };
}
function h(x) {
return i();
}
function g(x) {
return h(x.y);
}
function f() {
var l = { y : { z : 4 }, x : 2 }
var r = g(l);
assertEquals(2, r.gx.x)
assertEquals(4, r.gx.y.z)
assertEquals(4, r.hx.z)
l.y.z = 3;
assertEquals(3, r.gx.y.z)
assertEquals(3, r.hx.z)
assertEquals(3, l.y.z)
}
%NeverOptimizeFunction(i);
f(); f(); f();
%OptimizeFunctionOnNextCall(f);
f(); f();
%OptimizeFunctionOnNextCall(f);
f(); f();
})();
// Multiple captured argument test
(function testTwoArgumentsCapture() {
function h() {
return { a : g.arguments[1], b : g.arguments[0] };
}
function g(x, y) {
return h();
}
function f() {
var l = { y : { z : 4 }, x : 2 }
var k = { t : { u : 3 } };
var r = g(k, l);
assertEquals(2, r.a.x)
assertEquals(4, r.a.y.z)
assertEquals(3, r.b.t.u)
l.y.z = 6;
r.b.t.u = 7;
assertEquals(6, r.a.y.z)
assertEquals(7, k.t.u)
}
f(); f(); f();
%OptimizeFunctionOnNextCall(f);
f(); f();
%OptimizeFunctionOnNextCall(f);
f(); f();
})();
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