Commit 1da91b83 authored by Jaroslav Sevcik's avatar Jaroslav Sevcik Committed by Commit Bot

Reland "[deoptimizer] Staged materialization of objects."

This relands commit e71b8022.

This can now back in as the fix for chromium:787301 had enough time to
be tested in Canary.

Original change's description:
> [deoptimizer] Staged materialization of objects.
>
> The existing object materialization in the deoptimizer has the following problems:
>
> - Objects do not necessarily verify during materialization (because during the
>   depth first walk we might have inconsistent objects).
>
> - Stack can overflow (because we just materialize using recursive calls).
>
> - We generalize object fields.
>
>
> This CL re-implements the materialization algorithm to solve this problem. The
> new implementation creates the objects in two steps:
>
> 1. We allocate space for all the objects. In general, we allocate ByteArrays
>    of the right size. For leaf objects that cannot participate in cycles,
>    we build and initialize the materialized objects completely.
>
>    For JS objects, we insert markers into the byte array at the positions
>    where unboxed doubles are expected.
>
> 2. We initialize all the objects with the proper field values and change the
>    map from the ByteArray map to the correct map. This requires some sync
>    with the concurrent marker (Heap::NotifyObjectLayoutChange).
>
>    When initializing the JS object fields, we make sure that we respect
>    the unboxed double marker.
>
> Bug: chromium:770106, v8:3836
> Change-Id: I1ec466a9d19db9538df4ba915516d4c3ca825632
> Reviewed-on: https://chromium-review.googlesource.com/777559
> Commit-Queue: Jaroslav Sevcik <jarin@chromium.org>
> Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
> Reviewed-by: Michael Starzinger <mstarzinger@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#49821}

Bug: chromium:770106, v8:3836
Change-Id: Ied6c4e0fbae52713e55ae6dc13794a7521dbb8a5
Reviewed-on: https://chromium-review.googlesource.com/817745Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#49982}
parent 6ed2690e
...@@ -19,6 +19,8 @@ ...@@ -19,6 +19,8 @@
#include "src/tracing/trace-event.h" #include "src/tracing/trace-event.h"
#include "src/v8.h" #include "src/v8.h"
// Has to be the last include (doesn't have include guards)
#include "src/objects/object-macros.h"
namespace v8 { namespace v8 {
namespace internal { namespace internal {
...@@ -1673,6 +1675,8 @@ void Deoptimizer::MaterializeHeapObjects() { ...@@ -1673,6 +1675,8 @@ void Deoptimizer::MaterializeHeapObjects() {
reinterpret_cast<intptr_t>(*value); reinterpret_cast<intptr_t>(*value);
} }
translated_state_.VerifyMaterializedObjects();
isolate_->materialized_object_store()->Remove( isolate_->materialized_object_store()->Remove(
reinterpret_cast<Address>(stack_fp_)); reinterpret_cast<Address>(stack_fp_));
} }
...@@ -2395,9 +2399,8 @@ int TranslatedValue::object_index() const { ...@@ -2395,9 +2399,8 @@ int TranslatedValue::object_index() const {
Object* TranslatedValue::GetRawValue() const { Object* TranslatedValue::GetRawValue() const {
// If we have a value, return it. // If we have a value, return it.
Handle<Object> result_handle; if (materialization_state() == kFinished) {
if (value_.ToHandle(&result_handle)) { return *storage_;
return *result_handle;
} }
// Otherwise, do a best effort to get the value without allocation. // Otherwise, do a best effort to get the value without allocation.
...@@ -2439,11 +2442,15 @@ Object* TranslatedValue::GetRawValue() const { ...@@ -2439,11 +2442,15 @@ Object* TranslatedValue::GetRawValue() const {
return isolate()->heap()->arguments_marker(); return isolate()->heap()->arguments_marker();
} }
void TranslatedValue::set_initialized_storage(Handle<Object> storage) {
DCHECK_EQ(kUninitialized, materialization_state());
storage_ = storage;
materialization_state_ = kFinished;
}
Handle<Object> TranslatedValue::GetValue() { Handle<Object> TranslatedValue::GetValue() {
Handle<Object> result;
// If we already have a value, then get it. // If we already have a value, then get it.
if (value_.ToHandle(&result)) return result; if (materialization_state() == kFinished) return storage_;
// Otherwise we have to materialize. // Otherwise we have to materialize.
switch (kind()) { switch (kind()) {
...@@ -2454,12 +2461,27 @@ Handle<Object> TranslatedValue::GetValue() { ...@@ -2454,12 +2461,27 @@ Handle<Object> TranslatedValue::GetValue() {
case TranslatedValue::kFloat: case TranslatedValue::kFloat:
case TranslatedValue::kDouble: { case TranslatedValue::kDouble: {
MaterializeSimple(); MaterializeSimple();
return value_.ToHandleChecked(); return storage_;
} }
case TranslatedValue::kCapturedObject: case TranslatedValue::kCapturedObject:
case TranslatedValue::kDuplicatedObject: case TranslatedValue::kDuplicatedObject: {
return container_->MaterializeObjectAt(object_index()); // We need to materialize the object (or possibly even object graphs).
// To make the object verifier happy, we materialize in two steps.
// 1. Allocate storage for reachable objects. This makes sure that for
// each object we have allocated space on heap. The space will be
// a byte array that will be later initialized, or a fully
// initialized object if it is safe to allocate one that will
// pass the verifier.
container_->EnsureObjectAllocatedAt(this);
// 2. Initialize the objects. If we have allocated only byte arrays
// for some objects, we now overwrite the byte arrays with the
// correct object fields. Note that this phase does not allocate
// any new objects, so it does not trigger the object verifier.
return container_->InitializeObjectAt(this);
}
case TranslatedValue::kInvalid: case TranslatedValue::kInvalid:
FATAL("unexpected case"); FATAL("unexpected case");
...@@ -2470,36 +2492,39 @@ Handle<Object> TranslatedValue::GetValue() { ...@@ -2470,36 +2492,39 @@ Handle<Object> TranslatedValue::GetValue() {
return Handle<Object>::null(); return Handle<Object>::null();
} }
void TranslatedValue::MaterializeSimple() { void TranslatedValue::MaterializeSimple() {
// If we already have materialized, return. // If we already have materialized, return.
if (!value_.is_null()) return; if (materialization_state() == kFinished) return;
Object* raw_value = GetRawValue(); Object* raw_value = GetRawValue();
if (raw_value != isolate()->heap()->arguments_marker()) { if (raw_value != isolate()->heap()->arguments_marker()) {
// We can get the value without allocation, just return it here. // We can get the value without allocation, just return it here.
value_ = Handle<Object>(raw_value, isolate()); set_initialized_storage(Handle<Object>(raw_value, isolate()));
return; return;
} }
switch (kind()) { switch (kind()) {
case kInt32: case kInt32:
value_ = Handle<Object>(isolate()->factory()->NewNumber(int32_value())); set_initialized_storage(
Handle<Object>(isolate()->factory()->NewNumber(int32_value())));
return; return;
case kUInt32: case kUInt32:
value_ = Handle<Object>(isolate()->factory()->NewNumber(uint32_value())); set_initialized_storage(
Handle<Object>(isolate()->factory()->NewNumber(uint32_value())));
return; return;
case kFloat: { case kFloat: {
double scalar_value = float_value().get_scalar(); double scalar_value = float_value().get_scalar();
value_ = Handle<Object>(isolate()->factory()->NewNumber(scalar_value)); set_initialized_storage(
Handle<Object>(isolate()->factory()->NewNumber(scalar_value)));
return; return;
} }
case kDouble: { case kDouble: {
double scalar_value = double_value().get_scalar(); double scalar_value = double_value().get_scalar();
value_ = Handle<Object>(isolate()->factory()->NewNumber(scalar_value)); set_initialized_storage(
Handle<Object>(isolate()->factory()->NewNumber(scalar_value)));
return; return;
} }
...@@ -2561,7 +2586,7 @@ Float64 TranslatedState::GetDoubleSlot(Address fp, int slot_offset) { ...@@ -2561,7 +2586,7 @@ Float64 TranslatedState::GetDoubleSlot(Address fp, int slot_offset) {
void TranslatedValue::Handlify() { void TranslatedValue::Handlify() {
if (kind() == kTagged) { if (kind() == kTagged) {
value_ = Handle<Object>(raw_literal(), isolate()); set_initialized_storage(Handle<Object>(raw_literal(), isolate()));
raw_literal_ = nullptr; raw_literal_ = nullptr;
} }
} }
...@@ -3224,544 +3249,439 @@ void TranslatedState::Prepare(Address stack_frame_pointer) { ...@@ -3224,544 +3249,439 @@ void TranslatedState::Prepare(Address stack_frame_pointer) {
UpdateFromPreviouslyMaterializedObjects(); UpdateFromPreviouslyMaterializedObjects();
} }
class TranslatedState::CapturedObjectMaterializer { TranslatedValue* TranslatedState::GetValueByObjectIndex(int object_index) {
public: CHECK_LT(static_cast<size_t>(object_index), object_positions_.size());
CapturedObjectMaterializer(TranslatedState* state, int frame_index, TranslatedState::ObjectPosition pos = object_positions_[object_index];
int field_count) return &(frames_[pos.frame_index_].values_[pos.value_index_]);
: state_(state), frame_index_(frame_index), field_count_(field_count) {} }
// Ensure the properties never contain mutable heap numbers. This is necessary Handle<Object> TranslatedState::InitializeObjectAt(TranslatedValue* slot) {
// because the deoptimizer generalizes all maps to tagged representation slot = ResolveCapturedObject(slot);
// fields (so mutable heap numbers are not allowed).
static void EnsurePropertiesGeneralized(Handle<Object> properties_or_hash) { DisallowHeapAllocation no_allocation;
if (properties_or_hash->IsPropertyArray()) { if (slot->materialization_state() != TranslatedValue::kFinished) {
Handle<PropertyArray> properties = std::stack<int> worklist;
Handle<PropertyArray>::cast(properties_or_hash); worklist.push(slot->object_index());
int length = properties->length(); slot->mark_finished();
for (int i = 0; i < length; i++) {
if (properties->get(i)->IsMutableHeapNumber()) { while (!worklist.empty()) {
Handle<HeapObject> box(HeapObject::cast(properties->get(i))); int index = worklist.top();
box->set_map(properties->GetIsolate()->heap()->heap_number_map()); worklist.pop();
} InitializeCapturedObjectAt(index, &worklist, no_allocation);
}
}
return slot->GetStorage();
}
void TranslatedState::InitializeCapturedObjectAt(
int object_index, std::stack<int>* worklist,
const DisallowHeapAllocation& no_allocation) {
CHECK_LT(static_cast<size_t>(object_index), object_positions_.size());
TranslatedState::ObjectPosition pos = object_positions_[object_index];
int value_index = pos.value_index_;
TranslatedFrame* frame = &(frames_[pos.frame_index_]);
TranslatedValue* slot = &(frame->values_[value_index]);
value_index++;
CHECK_EQ(TranslatedValue::kFinished, slot->materialization_state());
CHECK_EQ(TranslatedValue::kCapturedObject, slot->kind());
// Ensure all fields are initialized.
int children_init_index = value_index;
for (int i = 0; i < slot->GetChildrenCount(); i++) {
// If the field is an object that has not been initialized yet, queue it
// for initialization (and mark it as such).
TranslatedValue* child_slot = frame->ValueAt(children_init_index);
if (child_slot->kind() == TranslatedValue::kCapturedObject ||
child_slot->kind() == TranslatedValue::kDuplicatedObject) {
child_slot = ResolveCapturedObject(child_slot);
if (child_slot->materialization_state() != TranslatedValue::kFinished) {
DCHECK_EQ(TranslatedValue::kAllocated,
child_slot->materialization_state());
worklist->push(child_slot->object_index());
child_slot->mark_finished();
} }
} }
SkipSlots(1, frame, &children_init_index);
} }
Handle<Object> FieldAt(int* value_index) { // Read the map.
CHECK_GT(field_count_, 0); // The map should never be materialized, so let us check we already have
--field_count_; // an existing object here.
Handle<Object> object = state_->MaterializeAt(frame_index_, value_index); CHECK_EQ(frame->values_[value_index].kind(), TranslatedValue::kTagged);
// This is a big hammer to make sure that the materialized objects do not Handle<Map> map = Handle<Map>::cast(frame->values_[value_index].GetValue());
// have property arrays with mutable heap numbers (mutable heap numbers are CHECK(map->IsMap());
// bad because we generalize maps for all materialized objects). value_index++;
EnsurePropertiesGeneralized(object);
return object; // Handle the special cases.
switch (map->instance_type()) {
case MUTABLE_HEAP_NUMBER_TYPE:
case FIXED_DOUBLE_ARRAY_TYPE:
return;
case FIXED_ARRAY_TYPE:
case HASH_TABLE_TYPE:
case PROPERTY_ARRAY_TYPE:
case CONTEXT_EXTENSION_TYPE:
InitializeObjectWithTaggedFieldsAt(frame, &value_index, slot, map,
no_allocation);
break;
default:
CHECK(map->IsJSObjectMap());
InitializeJSObjectAt(frame, &value_index, slot, map, no_allocation);
break;
} }
CHECK_EQ(value_index, children_init_index);
}
~CapturedObjectMaterializer() { CHECK_EQ(0, field_count_); } void TranslatedState::EnsureObjectAllocatedAt(TranslatedValue* slot) {
slot = ResolveCapturedObject(slot);
private: if (slot->materialization_state() == TranslatedValue::kUninitialized) {
TranslatedState* state_; std::stack<int> worklist;
int frame_index_; worklist.push(slot->object_index());
int field_count_; slot->mark_allocated();
while (!worklist.empty()) {
int index = worklist.top();
worklist.pop();
EnsureCapturedObjectAllocatedAt(index, &worklist);
}
}
}
void TranslatedState::MaterializeFixedDoubleArray(TranslatedFrame* frame,
int* value_index,
TranslatedValue* slot,
Handle<Map> map) {
int length = Smi::cast(frame->values_[*value_index].GetRawValue())->value();
(*value_index)++;
Handle<FixedDoubleArray> array = Handle<FixedDoubleArray>::cast(
isolate()->factory()->NewFixedDoubleArray(length));
CHECK_GT(length, 0);
for (int i = 0; i < length; i++) {
CHECK_NE(TranslatedValue::kCapturedObject,
frame->values_[*value_index].kind());
Handle<Object> value = frame->values_[*value_index].GetValue();
if (value->IsNumber()) {
array->set(i, value->Number());
} else {
CHECK(value.is_identical_to(isolate()->factory()->the_hole_value()));
array->set_the_hole(isolate(), i);
}
(*value_index)++;
}
slot->set_storage(array);
}
void TranslatedState::MaterializeMutableHeapNumber(TranslatedFrame* frame,
int* value_index,
TranslatedValue* slot) {
CHECK_NE(TranslatedValue::kCapturedObject,
frame->values_[*value_index].kind());
Handle<Object> value = frame->values_[*value_index].GetValue();
Handle<HeapNumber> box;
CHECK(value->IsNumber());
box = isolate()->factory()->NewHeapNumber(value->Number(), MUTABLE);
(*value_index)++;
slot->set_storage(box);
}
namespace {
enum DoubleStorageKind : uint8_t {
kStoreTagged,
kStoreUnboxedDouble,
kStoreMutableHeapNumber,
}; };
Handle<Object> TranslatedState::MaterializeCapturedObjectAt( } // namespace
TranslatedValue* slot, int frame_index, int* value_index) {
int length = slot->GetChildrenCount();
CapturedObjectMaterializer materializer(this, frame_index, length); void TranslatedState::SkipSlots(int slots_to_skip, TranslatedFrame* frame,
int* value_index) {
while (slots_to_skip > 0) {
TranslatedValue* slot = &(frame->values_[*value_index]);
(*value_index)++;
slots_to_skip--;
Handle<Object> result; if (slot->kind() == TranslatedValue::kCapturedObject) {
if (slot->value_.ToHandle(&result)) { slots_to_skip += slot->GetChildrenCount();
// This has been previously materialized, return the previous value.
// We still need to skip all the nested objects.
for (int i = 0; i < length; i++) {
materializer.FieldAt(value_index);
} }
return result;
} }
}
Handle<Object> map_object = materializer.FieldAt(value_index); void TranslatedState::EnsureCapturedObjectAllocatedAt(
Handle<Map> map = Map::GeneralizeAllFields(Handle<Map>::cast(map_object)); int object_index, std::stack<int>* worklist) {
CHECK_LT(static_cast<size_t>(object_index), object_positions_.size());
TranslatedState::ObjectPosition pos = object_positions_[object_index];
int value_index = pos.value_index_;
TranslatedFrame* frame = &(frames_[pos.frame_index_]);
TranslatedValue* slot = &(frame->values_[value_index]);
value_index++;
CHECK_EQ(TranslatedValue::kAllocated, slot->materialization_state());
CHECK_EQ(TranslatedValue::kCapturedObject, slot->kind());
// Read the map.
// The map should never be materialized, so let us check we already have
// an existing object here.
CHECK_EQ(frame->values_[value_index].kind(), TranslatedValue::kTagged);
Handle<Map> map = Handle<Map>::cast(frame->values_[value_index].GetValue());
CHECK(map->IsMap());
value_index++;
// Handle the special cases.
switch (map->instance_type()) { switch (map->instance_type()) {
case FIXED_DOUBLE_ARRAY_TYPE:
// Materialize (i.e. allocate&initialize) the array and return since
// there is no need to process the children.
return MaterializeFixedDoubleArray(frame, &value_index, slot, map);
case MUTABLE_HEAP_NUMBER_TYPE: case MUTABLE_HEAP_NUMBER_TYPE:
case HEAP_NUMBER_TYPE: { // Materialize (i.e. allocate&initialize) the heap number and return.
// Reuse the HeapNumber value directly as it is already properly // There is no need to process the children.
// tagged and skip materializing the HeapNumber explicitly. return MaterializeMutableHeapNumber(frame, &value_index, slot);
Handle<Object> object = materializer.FieldAt(value_index);
slot->value_ = object; case FIXED_ARRAY_TYPE:
// On 32-bit architectures, there is an extra slot there because case HASH_TABLE_TYPE: {
// the escape analysis calculates the number of slots as // Check we have the right size.
// object-size/pointer-size. To account for this, we read out int array_length =
// any extra slots. Smi::cast(frame->values_[value_index].GetRawValue())->value();
for (int i = 0; i < length - 2; i++) { int instance_size = FixedArray::SizeFor(array_length);
materializer.FieldAt(value_index); CHECK_EQ(instance_size, slot->GetChildrenCount() * kPointerSize);
}
return object; slot->set_storage(AllocateStorageFor(slot));
} break;
case JS_OBJECT_TYPE: }
case JS_ERROR_TYPE:
case JS_ARGUMENTS_TYPE: {
Handle<JSObject> object =
isolate_->factory()->NewJSObjectFromMap(map, NOT_TENURED);
slot->value_ = object;
Handle<Object> properties = materializer.FieldAt(value_index);
Handle<Object> elements = materializer.FieldAt(value_index);
object->set_raw_properties_or_hash(*properties);
object->set_elements(FixedArrayBase::cast(*elements));
int in_object_properties = map->GetInObjectProperties();
for (int i = 0; i < in_object_properties; ++i) {
Handle<Object> value = materializer.FieldAt(value_index);
FieldIndex index = FieldIndex::ForPropertyIndex(object->map(), i);
object->FastPropertyAtPut(index, *value);
}
return object;
}
case JS_SET_KEY_VALUE_ITERATOR_TYPE:
case JS_SET_VALUE_ITERATOR_TYPE: {
Handle<JSSetIterator> object = Handle<JSSetIterator>::cast(
isolate_->factory()->NewJSObjectFromMap(map, NOT_TENURED));
slot->value_ = object;
Handle<Object> properties = materializer.FieldAt(value_index);
Handle<Object> elements = materializer.FieldAt(value_index);
Handle<Object> table = materializer.FieldAt(value_index);
Handle<Object> index = materializer.FieldAt(value_index);
object->set_raw_properties_or_hash(*properties);
object->set_elements(FixedArrayBase::cast(*elements));
object->set_table(*table);
object->set_index(*index);
return object;
}
case JS_MAP_KEY_ITERATOR_TYPE:
case JS_MAP_KEY_VALUE_ITERATOR_TYPE:
case JS_MAP_VALUE_ITERATOR_TYPE: {
Handle<JSMapIterator> object = Handle<JSMapIterator>::cast(
isolate_->factory()->NewJSObjectFromMap(map, NOT_TENURED));
slot->value_ = object;
Handle<Object> properties = materializer.FieldAt(value_index);
Handle<Object> elements = materializer.FieldAt(value_index);
Handle<Object> table = materializer.FieldAt(value_index);
Handle<Object> index = materializer.FieldAt(value_index);
object->set_raw_properties_or_hash(*properties);
object->set_elements(FixedArrayBase::cast(*elements));
object->set_table(*table);
object->set_index(*index);
return object;
}
#define ARRAY_ITERATOR_CASE(type) case type:
ARRAY_ITERATOR_TYPE_LIST(ARRAY_ITERATOR_CASE)
#undef ARRAY_ITERATOR_CASE
{
Handle<JSArrayIterator> object = Handle<JSArrayIterator>::cast(
isolate_->factory()->NewJSObjectFromMap(map, NOT_TENURED));
slot->value_ = object;
// Initialize the index to zero to make the heap verifier happy.
object->set_index(Smi::FromInt(0));
Handle<Object> properties = materializer.FieldAt(value_index);
Handle<Object> elements = materializer.FieldAt(value_index);
Handle<Object> iterated_object = materializer.FieldAt(value_index);
Handle<Object> next_index = materializer.FieldAt(value_index);
Handle<Object> iterated_object_map = materializer.FieldAt(value_index);
object->set_raw_properties_or_hash(*properties);
object->set_elements(FixedArrayBase::cast(*elements));
object->set_object(*iterated_object);
object->set_index(*next_index);
object->set_object_map(*iterated_object_map);
return object;
}
case JS_STRING_ITERATOR_TYPE: {
Handle<JSStringIterator> object = Handle<JSStringIterator>::cast(
isolate_->factory()->NewJSObjectFromMap(map, NOT_TENURED));
slot->value_ = object;
// Initialize the index to zero to make the heap verifier happy.
object->set_index(0);
Handle<Object> properties = materializer.FieldAt(value_index);
Handle<Object> elements = materializer.FieldAt(value_index);
Handle<Object> iterated_string = materializer.FieldAt(value_index);
Handle<Object> next_index = materializer.FieldAt(value_index);
object->set_raw_properties_or_hash(*properties);
object->set_elements(FixedArrayBase::cast(*elements));
CHECK(iterated_string->IsString());
object->set_string(String::cast(*iterated_string));
CHECK(next_index->IsSmi());
object->set_index(Smi::ToInt(*next_index));
return object;
}
case JS_ASYNC_FROM_SYNC_ITERATOR_TYPE: {
Handle<JSAsyncFromSyncIterator> object =
Handle<JSAsyncFromSyncIterator>::cast(
isolate_->factory()->NewJSObjectFromMap(map, NOT_TENURED));
slot->value_ = object;
Handle<Object> properties = materializer.FieldAt(value_index);
Handle<Object> elements = materializer.FieldAt(value_index);
Handle<Object> sync_iterator = materializer.FieldAt(value_index);
object->set_raw_properties_or_hash(*properties);
object->set_elements(FixedArrayBase::cast(*elements));
object->set_sync_iterator(JSReceiver::cast(*sync_iterator));
return object;
}
case JS_ARRAY_TYPE: {
Handle<JSArray> object = Handle<JSArray>::cast(
isolate_->factory()->NewJSObjectFromMap(map, NOT_TENURED));
slot->value_ = object;
Handle<Object> properties = materializer.FieldAt(value_index);
Handle<Object> elements = materializer.FieldAt(value_index);
Handle<Object> array_length = materializer.FieldAt(value_index);
object->set_raw_properties_or_hash(*properties);
object->set_elements(FixedArrayBase::cast(*elements));
object->set_length(*array_length);
int in_object_properties = map->GetInObjectProperties();
for (int i = 0; i < in_object_properties; ++i) {
Handle<Object> value = materializer.FieldAt(value_index);
FieldIndex index = FieldIndex::ForPropertyIndex(object->map(), i);
object->FastPropertyAtPut(index, *value);
}
return object;
}
case JS_BOUND_FUNCTION_TYPE: {
Handle<JSBoundFunction> object = Handle<JSBoundFunction>::cast(
isolate_->factory()->NewJSObjectFromMap(map, NOT_TENURED));
slot->value_ = object;
Handle<Object> properties = materializer.FieldAt(value_index);
Handle<Object> elements = materializer.FieldAt(value_index);
Handle<Object> bound_target_function = materializer.FieldAt(value_index);
Handle<Object> bound_this = materializer.FieldAt(value_index);
Handle<Object> bound_arguments = materializer.FieldAt(value_index);
object->set_raw_properties_or_hash(*properties);
object->set_elements(FixedArrayBase::cast(*elements));
object->set_bound_target_function(
JSReceiver::cast(*bound_target_function));
object->set_bound_this(*bound_this);
object->set_bound_arguments(FixedArray::cast(*bound_arguments));
return object;
}
case JS_FUNCTION_TYPE: {
Handle<JSFunction> object = isolate_->factory()->NewFunction(
map, handle(isolate_->object_function()->shared()),
handle(isolate_->context()), NOT_TENURED);
slot->value_ = object;
// We temporarily allocated a JSFunction for the {Object} function
// within the current context, to break cycles in the object graph.
// The correct function and context will be set below once available.
STATIC_ASSERT(JSFunction::kSizeWithoutPrototype == 7 * kPointerSize);
Handle<Object> properties = materializer.FieldAt(value_index);
Handle<Object> elements = materializer.FieldAt(value_index);
Handle<Object> shared = materializer.FieldAt(value_index);
Handle<Object> context = materializer.FieldAt(value_index);
Handle<Object> vector_cell = materializer.FieldAt(value_index);
Handle<Object> code = materializer.FieldAt(value_index);
bool has_prototype_slot = map->has_prototype_slot();
Handle<Object> prototype;
if (has_prototype_slot) {
prototype = materializer.FieldAt(value_index);
}
object->set_map(*map);
object->set_raw_properties_or_hash(*properties);
object->set_elements(FixedArrayBase::cast(*elements));
object->set_shared(SharedFunctionInfo::cast(*shared));
object->set_context(Context::cast(*context));
object->set_feedback_vector_cell(Cell::cast(*vector_cell));
object->set_code(Code::cast(*code));
if (has_prototype_slot) {
object->set_prototype_or_initial_map(*prototype);
}
int in_object_properties = map->GetInObjectProperties();
for (int i = 0; i < in_object_properties; ++i) {
Handle<Object> value = materializer.FieldAt(value_index);
FieldIndex index = FieldIndex::ForPropertyIndex(object->map(), i);
object->FastPropertyAtPut(index, *value);
}
return object;
}
case JS_ASYNC_GENERATOR_OBJECT_TYPE:
case JS_GENERATOR_OBJECT_TYPE: {
Handle<JSGeneratorObject> object = Handle<JSGeneratorObject>::cast(
isolate_->factory()->NewJSObjectFromMap(map, NOT_TENURED));
slot->value_ = object;
Handle<Object> properties = materializer.FieldAt(value_index);
Handle<Object> elements = materializer.FieldAt(value_index);
Handle<Object> function = materializer.FieldAt(value_index);
Handle<Object> context = materializer.FieldAt(value_index);
Handle<Object> receiver = materializer.FieldAt(value_index);
Handle<Object> input_or_debug_pos = materializer.FieldAt(value_index);
Handle<Object> resume_mode = materializer.FieldAt(value_index);
Handle<Object> continuation_offset = materializer.FieldAt(value_index);
Handle<Object> register_file = materializer.FieldAt(value_index);
object->set_raw_properties_or_hash(*properties);
object->set_elements(FixedArrayBase::cast(*elements));
object->set_function(JSFunction::cast(*function));
object->set_context(Context::cast(*context));
object->set_receiver(*receiver);
object->set_input_or_debug_pos(*input_or_debug_pos);
object->set_resume_mode(Smi::ToInt(*resume_mode));
object->set_continuation(Smi::ToInt(*continuation_offset));
object->set_register_file(FixedArray::cast(*register_file));
if (object->IsJSAsyncGeneratorObject()) {
auto generator = Handle<JSAsyncGeneratorObject>::cast(object);
Handle<Object> queue = materializer.FieldAt(value_index);
Handle<Object> awaited_promise = materializer.FieldAt(value_index);
generator->set_queue(HeapObject::cast(*queue));
generator->set_awaited_promise(HeapObject::cast(*awaited_promise));
}
int in_object_properties = map->GetInObjectProperties(); case PROPERTY_ARRAY_TYPE: {
for (int i = 0; i < in_object_properties; ++i) { // Check we have the right size.
Handle<Object> value = materializer.FieldAt(value_index); int length_or_hash =
FieldIndex index = FieldIndex::ForPropertyIndex(object->map(), i); Smi::cast(frame->values_[value_index].GetRawValue())->value();
object->FastPropertyAtPut(index, *value); int array_length = PropertyArray::LengthField::decode(length_or_hash);
} int instance_size = PropertyArray::SizeFor(array_length);
return object; CHECK_EQ(instance_size, slot->GetChildrenCount() * kPointerSize);
slot->set_storage(AllocateStorageFor(slot));
break;
} }
case CONTEXT_EXTENSION_TYPE: { case CONTEXT_EXTENSION_TYPE: {
Handle<ContextExtension> object = CHECK_EQ(map->instance_size(), slot->GetChildrenCount() * kPointerSize);
isolate_->factory()->NewContextExtension( slot->set_storage(AllocateStorageFor(slot));
isolate_->factory()->NewScopeInfo(1), break;
isolate_->factory()->undefined_value());
slot->value_ = object;
Handle<Object> scope_info = materializer.FieldAt(value_index);
Handle<Object> extension = materializer.FieldAt(value_index);
object->set_scope_info(ScopeInfo::cast(*scope_info));
object->set_extension(*extension);
return object;
}
case HASH_TABLE_TYPE:
case FIXED_ARRAY_TYPE: {
Handle<Object> lengthObject = materializer.FieldAt(value_index);
int32_t array_length = 0;
CHECK(lengthObject->ToInt32(&array_length));
Handle<FixedArray> object =
isolate_->factory()->NewFixedArray(array_length);
// We need to set the map, because the fixed array we are
// materializing could be a context or an arguments object,
// in which case we must retain that information.
object->set_map(*map);
slot->value_ = object;
for (int i = 0; i < array_length; ++i) {
Handle<Object> value = materializer.FieldAt(value_index);
object->set(i, *value);
}
return object;
} }
case PROPERTY_ARRAY_TYPE: {
DCHECK_EQ(*map, isolate_->heap()->property_array_map()); default:
Handle<Object> lengthObject = materializer.FieldAt(value_index); CHECK(map->IsJSObjectMap());
int32_t array_length = 0; EnsureJSObjectAllocated(slot, map);
CHECK(lengthObject->ToInt32(&array_length)); TranslatedValue* properties_slot = &(frame->values_[value_index]);
Handle<PropertyArray> object = if (properties_slot->kind() == TranslatedValue::kCapturedObject) {
isolate_->factory()->NewPropertyArray(array_length); // If we are materializing the property array, make sure we put
slot->value_ = object; // the mutable heap numbers at the right places.
for (int i = 0; i < array_length; ++i) { EnsurePropertiesAllocatedAndMarked(properties_slot, map);
Handle<Object> value = materializer.FieldAt(value_index); value_index++;
object->set(i, *value); EnsureChildrenAllocated(properties_slot->GetChildrenCount(), frame,
} &value_index, worklist);
return object;
}
case FIXED_DOUBLE_ARRAY_TYPE: {
DCHECK_EQ(*map, isolate_->heap()->fixed_double_array_map());
Handle<Object> lengthObject = materializer.FieldAt(value_index);
int32_t array_length = 0;
CHECK(lengthObject->ToInt32(&array_length));
Handle<FixedArrayBase> object =
isolate_->factory()->NewFixedDoubleArray(array_length);
slot->value_ = object;
if (array_length > 0) {
Handle<FixedDoubleArray> double_array =
Handle<FixedDoubleArray>::cast(object);
for (int i = 0; i < array_length; ++i) {
Handle<Object> value = materializer.FieldAt(value_index);
if (value.is_identical_to(isolate_->factory()->the_hole_value())) {
double_array->set_the_hole(isolate_, i);
} else {
CHECK(value->IsNumber());
double_array->set(i, value->Number());
}
}
} }
return object;
}
case JS_REGEXP_TYPE: {
Handle<JSRegExp> object = Handle<JSRegExp>::cast(
isolate_->factory()->NewJSObjectFromMap(map, NOT_TENURED));
slot->value_ = object;
Handle<Object> properties = materializer.FieldAt(value_index);
Handle<Object> elements = materializer.FieldAt(value_index);
Handle<Object> data = materializer.FieldAt(value_index);
Handle<Object> source = materializer.FieldAt(value_index);
Handle<Object> flags = materializer.FieldAt(value_index);
Handle<Object> last_index = materializer.FieldAt(value_index);
object->set_raw_properties_or_hash(*properties);
object->set_elements(FixedArrayBase::cast(*elements));
object->set_data(*data);
object->set_source(*source);
object->set_flags(*flags);
object->set_last_index(*last_index);
return object;
}
case STRING_TYPE:
case ONE_BYTE_STRING_TYPE:
case CONS_STRING_TYPE:
case CONS_ONE_BYTE_STRING_TYPE:
case SLICED_STRING_TYPE:
case SLICED_ONE_BYTE_STRING_TYPE:
case EXTERNAL_STRING_TYPE:
case EXTERNAL_ONE_BYTE_STRING_TYPE:
case EXTERNAL_STRING_WITH_ONE_BYTE_DATA_TYPE:
case SHORT_EXTERNAL_STRING_TYPE:
case SHORT_EXTERNAL_ONE_BYTE_STRING_TYPE:
case SHORT_EXTERNAL_STRING_WITH_ONE_BYTE_DATA_TYPE:
case THIN_STRING_TYPE:
case THIN_ONE_BYTE_STRING_TYPE:
case INTERNALIZED_STRING_TYPE:
case ONE_BYTE_INTERNALIZED_STRING_TYPE:
case EXTERNAL_INTERNALIZED_STRING_TYPE:
case EXTERNAL_ONE_BYTE_INTERNALIZED_STRING_TYPE:
case EXTERNAL_INTERNALIZED_STRING_WITH_ONE_BYTE_DATA_TYPE:
case SHORT_EXTERNAL_INTERNALIZED_STRING_TYPE:
case SHORT_EXTERNAL_ONE_BYTE_INTERNALIZED_STRING_TYPE:
case SHORT_EXTERNAL_INTERNALIZED_STRING_WITH_ONE_BYTE_DATA_TYPE:
case SYMBOL_TYPE:
case ODDBALL_TYPE:
case JS_GLOBAL_OBJECT_TYPE:
case JS_GLOBAL_PROXY_TYPE:
case JS_API_OBJECT_TYPE:
case JS_SPECIAL_API_OBJECT_TYPE:
case JS_VALUE_TYPE:
case JS_MESSAGE_OBJECT_TYPE:
case JS_DATE_TYPE:
case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
case JS_MODULE_NAMESPACE_TYPE:
case JS_ARRAY_BUFFER_TYPE:
case JS_TYPED_ARRAY_TYPE:
case JS_DATA_VIEW_TYPE:
case JS_SET_TYPE:
case JS_MAP_TYPE:
case JS_WEAK_MAP_TYPE:
case JS_WEAK_SET_TYPE:
case JS_PROMISE_TYPE:
case JS_PROXY_TYPE:
case MAP_TYPE:
case ALLOCATION_SITE_TYPE:
case ACCESSOR_INFO_TYPE:
case SHARED_FUNCTION_INFO_TYPE:
case FUNCTION_TEMPLATE_INFO_TYPE:
case ACCESSOR_PAIR_TYPE:
case BYTE_ARRAY_TYPE:
case BYTECODE_ARRAY_TYPE:
case DESCRIPTOR_ARRAY_TYPE:
case TRANSITION_ARRAY_TYPE:
case FEEDBACK_VECTOR_TYPE:
case FOREIGN_TYPE:
case SCRIPT_TYPE:
case CODE_TYPE:
case PROPERTY_CELL_TYPE:
case BIGINT_TYPE:
case MODULE_TYPE:
case MODULE_INFO_ENTRY_TYPE:
case FREE_SPACE_TYPE:
#define FIXED_TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
case FIXED_##TYPE##_ARRAY_TYPE:
TYPED_ARRAYS(FIXED_TYPED_ARRAY_CASE)
#undef FIXED_TYPED_ARRAY_CASE
case FILLER_TYPE:
case ACCESS_CHECK_INFO_TYPE:
case INTERCEPTOR_INFO_TYPE:
case OBJECT_TEMPLATE_INFO_TYPE:
case ALLOCATION_MEMENTO_TYPE:
case ALIASED_ARGUMENTS_ENTRY_TYPE:
case PROMISE_RESOLVE_THENABLE_JOB_INFO_TYPE:
case PROMISE_REACTION_JOB_INFO_TYPE:
case DEBUG_INFO_TYPE:
case STACK_FRAME_INFO_TYPE:
case CELL_TYPE:
case WEAK_CELL_TYPE:
case SMALL_ORDERED_HASH_MAP_TYPE:
case SMALL_ORDERED_HASH_SET_TYPE:
case CODE_DATA_CONTAINER_TYPE:
case PROTOTYPE_INFO_TYPE:
case TUPLE2_TYPE:
case TUPLE3_TYPE:
case LOAD_HANDLER_TYPE:
case STORE_HANDLER_TYPE:
case ASYNC_GENERATOR_REQUEST_TYPE:
case WASM_MODULE_TYPE:
case WASM_INSTANCE_TYPE:
case WASM_MEMORY_TYPE:
case WASM_TABLE_TYPE:
OFStream os(stderr);
os << "[couldn't handle instance type " << map->instance_type() << "]"
<< std::endl;
UNREACHABLE();
break; break;
} }
UNREACHABLE();
EnsureChildrenAllocated(slot->GetChildrenCount() - 1, frame, &value_index,
worklist);
}
void TranslatedState::EnsureChildrenAllocated(int count, TranslatedFrame* frame,
int* value_index,
std::stack<int>* worklist) {
// Ensure all children are allocated.
for (int i = 0; i < count; i++) {
// If the field is an object that has not been allocated yet, queue it
// for initialization (and mark it as such).
TranslatedValue* child_slot = frame->ValueAt(*value_index);
if (child_slot->kind() == TranslatedValue::kCapturedObject ||
child_slot->kind() == TranslatedValue::kDuplicatedObject) {
child_slot = ResolveCapturedObject(child_slot);
if (child_slot->materialization_state() ==
TranslatedValue::kUninitialized) {
worklist->push(child_slot->object_index());
child_slot->mark_allocated();
}
} else {
// Make sure the simple values (heap numbers, etc.) are properly
// initialized.
child_slot->MaterializeSimple();
}
SkipSlots(1, frame, value_index);
}
} }
Handle<Object> TranslatedState::MaterializeAt(int frame_index, void TranslatedState::EnsurePropertiesAllocatedAndMarked(
int* value_index) { TranslatedValue* properties_slot, Handle<Map> map) {
CHECK_LT(static_cast<size_t>(frame_index), frames().size()); CHECK_EQ(TranslatedValue::kUninitialized,
TranslatedFrame* frame = &(frames_[frame_index]); properties_slot->materialization_state());
CHECK_LT(static_cast<size_t>(*value_index), frame->values_.size());
TranslatedValue* slot = &(frame->values_[*value_index]); Handle<ByteArray> object_storage = AllocateStorageFor(properties_slot);
(*value_index)++; properties_slot->mark_allocated();
properties_slot->set_storage(object_storage);
switch (slot->kind()) { // Set markers for the double properties.
case TranslatedValue::kTagged: Handle<DescriptorArray> descriptors(map->instance_descriptors());
case TranslatedValue::kInt32: int field_count = map->NumberOfOwnDescriptors();
case TranslatedValue::kUInt32: for (int i = 0; i < field_count; i++) {
case TranslatedValue::kBoolBit: FieldIndex index = FieldIndex::ForDescriptor(*map, i);
case TranslatedValue::kFloat: if (descriptors->GetDetails(i).representation().IsDouble() &&
case TranslatedValue::kDouble: { !index.is_inobject()) {
slot->MaterializeSimple(); CHECK(!map->IsUnboxedDoubleField(index));
Handle<Object> value = slot->GetValue(); int outobject_index = index.outobject_array_index();
if (value->IsMutableHeapNumber()) { int array_index = outobject_index * kPointerSize;
HeapNumber::cast(*value)->set_map(isolate()->heap()->heap_number_map()); object_storage->set(array_index, kStoreMutableHeapNumber);
}
return value;
} }
}
}
case TranslatedValue::kCapturedObject: { Handle<ByteArray> TranslatedState::AllocateStorageFor(TranslatedValue* slot) {
// The map must be a tagged object. int allocate_size =
CHECK_EQ(frame->values_[*value_index].kind(), TranslatedValue::kTagged); ByteArray::LengthFor(slot->GetChildrenCount() * kPointerSize);
CHECK(frame->values_[*value_index].GetValue()->IsMap()); // It is important to allocate all the objects tenured so that the marker
return MaterializeCapturedObjectAt(slot, frame_index, value_index); // does not visit them.
Handle<ByteArray> object_storage =
isolate()->factory()->NewByteArray(allocate_size, TENURED);
for (int i = 0; i < object_storage->length(); i++) {
object_storage->set(i, kStoreTagged);
}
return object_storage;
}
void TranslatedState::EnsureJSObjectAllocated(TranslatedValue* slot,
Handle<Map> map) {
CHECK_EQ(map->instance_size(), slot->GetChildrenCount() * kPointerSize);
Handle<ByteArray> object_storage = AllocateStorageFor(slot);
// Now we handle the interesting (JSObject) case.
Handle<DescriptorArray> descriptors(map->instance_descriptors());
int field_count = map->NumberOfOwnDescriptors();
// Set markers for the double properties.
for (int i = 0; i < field_count; i++) {
FieldIndex index = FieldIndex::ForDescriptor(*map, i);
if (descriptors->GetDetails(i).representation().IsDouble() &&
index.is_inobject()) {
CHECK_GE(index.index(), FixedArray::kHeaderSize / kPointerSize);
int array_index = index.index() * kPointerSize - FixedArray::kHeaderSize;
uint8_t marker = map->IsUnboxedDoubleField(index)
? kStoreUnboxedDouble
: kStoreMutableHeapNumber;
object_storage->set(array_index, marker);
} }
case TranslatedValue::kDuplicatedObject: { }
int object_index = slot->object_index(); slot->set_storage(object_storage);
TranslatedState::ObjectPosition pos = object_positions_[object_index]; }
Handle<Object> TranslatedState::GetValueAndAdvance(TranslatedFrame* frame,
int* value_index) {
TranslatedValue* slot = frame->ValueAt(*value_index);
SkipSlots(1, frame, value_index);
if (slot->kind() == TranslatedValue::kDuplicatedObject) {
slot = ResolveCapturedObject(slot);
}
CHECK_NE(TranslatedValue::kUninitialized, slot->materialization_state());
return slot->GetStorage();
}
// Make sure the duplicate is referring to a previous object. void TranslatedState::InitializeJSObjectAt(
CHECK(pos.frame_index_ < frame_index || TranslatedFrame* frame, int* value_index, TranslatedValue* slot,
(pos.frame_index_ == frame_index && Handle<Map> map, const DisallowHeapAllocation& no_allocation) {
pos.value_index_ < *value_index - 1)); Handle<HeapObject> object_storage = Handle<HeapObject>::cast(slot->storage_);
DCHECK_EQ(TranslatedValue::kCapturedObject, slot->kind());
Handle<Object> object = // The object should have at least a map and some payload.
frames_[pos.frame_index_].values_[pos.value_index_].GetValue(); CHECK_GE(slot->GetChildrenCount(), 2);
// The object should have a (non-sentinel) value. // Notify the concurrent marker about the layout change.
CHECK(!object.is_null() && isolate()->heap()->NotifyObjectLayoutChange(
!object.is_identical_to(isolate_->factory()->arguments_marker())); *object_storage, slot->GetChildrenCount() * kPointerSize, no_allocation);
slot->value_ = object; // Fill the property array field.
return object; {
Handle<Object> properties = GetValueAndAdvance(frame, value_index);
WRITE_FIELD(*object_storage, JSObject::kPropertiesOrHashOffset,
*properties);
WRITE_BARRIER(isolate()->heap(), *object_storage,
JSObject::kPropertiesOrHashOffset, *properties);
}
// For all the other fields we first look at the fixed array and check the
// marker to see if we store an unboxed double.
DCHECK_EQ(kPointerSize, JSObject::kPropertiesOrHashOffset);
for (int i = 2; i < slot->GetChildrenCount(); i++) {
// Initialize and extract the value from its slot.
Handle<Object> field_value = GetValueAndAdvance(frame, value_index);
// Read out the marker and ensure the field is consistent with
// what the markers in the storage say (note that all heap numbers
// should be fully initialized by now).
int offset = i * kPointerSize;
uint8_t marker = READ_UINT8_FIELD(*object_storage, offset);
if (marker == kStoreUnboxedDouble) {
double double_field_value;
if (field_value->IsSmi()) {
double_field_value = Smi::cast(*field_value)->value();
} else {
CHECK(field_value->IsHeapNumber());
double_field_value = HeapNumber::cast(*field_value)->value();
}
WRITE_DOUBLE_FIELD(*object_storage, offset, double_field_value);
} else if (marker == kStoreMutableHeapNumber) {
CHECK(field_value->IsMutableHeapNumber());
WRITE_FIELD(*object_storage, offset, *field_value);
WRITE_BARRIER(isolate()->heap(), *object_storage, offset, *field_value);
} else {
CHECK_EQ(kStoreTagged, marker);
WRITE_FIELD(*object_storage, offset, *field_value);
WRITE_BARRIER(isolate()->heap(), *object_storage, offset, *field_value);
} }
}
object_storage->synchronized_set_map(*map);
}
case TranslatedValue::kInvalid: void TranslatedState::InitializeObjectWithTaggedFieldsAt(
UNREACHABLE(); TranslatedFrame* frame, int* value_index, TranslatedValue* slot,
break; Handle<Map> map, const DisallowHeapAllocation& no_allocation) {
Handle<HeapObject> object_storage = Handle<HeapObject>::cast(slot->storage_);
// Notify the concurrent marker about the layout change.
isolate()->heap()->NotifyObjectLayoutChange(
*object_storage, slot->GetChildrenCount() * kPointerSize, no_allocation);
// Write the fields to the object.
for (int i = 1; i < slot->GetChildrenCount(); i++) {
Handle<Object> field_value = GetValueAndAdvance(frame, value_index);
int offset = i * kPointerSize;
uint8_t marker = READ_UINT8_FIELD(*object_storage, offset);
if (i > 1 && marker == kStoreMutableHeapNumber) {
CHECK(field_value->IsMutableHeapNumber());
} else {
CHECK(marker == kStoreTagged || i == 1);
CHECK(!field_value->IsMutableHeapNumber());
}
WRITE_FIELD(*object_storage, offset, *field_value);
WRITE_BARRIER(isolate()->heap(), *object_storage, offset, *field_value);
} }
FATAL("We should never get here - unexpected deopt slot kind."); object_storage->synchronized_set_map(*map);
return Handle<Object>::null();
} }
Handle<Object> TranslatedState::MaterializeObjectAt(int object_index) { TranslatedValue* TranslatedState::ResolveCapturedObject(TranslatedValue* slot) {
CHECK_LT(static_cast<size_t>(object_index), object_positions_.size()); while (slot->kind() == TranslatedValue::kDuplicatedObject) {
TranslatedState::ObjectPosition pos = object_positions_[object_index]; slot = GetValueByObjectIndex(slot->object_index());
return MaterializeAt(pos.frame_index_, &(pos.value_index_)); }
CHECK_EQ(TranslatedValue::kCapturedObject, slot->kind());
return slot;
} }
TranslatedFrame* TranslatedState::GetFrameFromJSFrameIndex(int jsframe_index) { TranslatedFrame* TranslatedState::GetFrameFromJSFrameIndex(int jsframe_index) {
...@@ -3814,7 +3734,7 @@ void TranslatedState::StoreMaterializedValuesAndDeopt(JavaScriptFrame* frame) { ...@@ -3814,7 +3734,7 @@ void TranslatedState::StoreMaterializedValuesAndDeopt(JavaScriptFrame* frame) {
bool new_store = false; bool new_store = false;
if (previously_materialized_objects.is_null()) { if (previously_materialized_objects.is_null()) {
previously_materialized_objects = previously_materialized_objects =
isolate_->factory()->NewFixedArray(length); isolate_->factory()->NewFixedArray(length, TENURED);
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
previously_materialized_objects->set(i, *marker); previously_materialized_objects->set(i, *marker);
} }
...@@ -3831,6 +3751,10 @@ void TranslatedState::StoreMaterializedValuesAndDeopt(JavaScriptFrame* frame) { ...@@ -3831,6 +3751,10 @@ void TranslatedState::StoreMaterializedValuesAndDeopt(JavaScriptFrame* frame) {
CHECK(value_info->IsMaterializedObject()); CHECK(value_info->IsMaterializedObject());
// Skip duplicate objects (i.e., those that point to some
// other object id).
if (value_info->object_index() != i) continue;
Handle<Object> value(value_info->GetRawValue(), isolate_); Handle<Object> value(value_info->GetRawValue(), isolate_);
if (!value.is_identical_to(marker)) { if (!value.is_identical_to(marker)) {
...@@ -3874,11 +3798,34 @@ void TranslatedState::UpdateFromPreviouslyMaterializedObjects() { ...@@ -3874,11 +3798,34 @@ void TranslatedState::UpdateFromPreviouslyMaterializedObjects() {
&(frames_[pos.frame_index_].values_[pos.value_index_]); &(frames_[pos.frame_index_].values_[pos.value_index_]);
CHECK(value_info->IsMaterializedObject()); CHECK(value_info->IsMaterializedObject());
value_info->value_ = if (value_info->kind() == TranslatedValue::kCapturedObject) {
Handle<Object>(previously_materialized_objects->get(i), isolate_); value_info->set_initialized_storage(
Handle<Object>(previously_materialized_objects->get(i), isolate_));
}
}
}
}
void TranslatedState::VerifyMaterializedObjects() {
#if VERIFY_HEAP
int length = static_cast<int>(object_positions_.size());
for (int i = 0; i < length; i++) {
TranslatedValue* slot = GetValueByObjectIndex(i);
if (slot->kind() == TranslatedValue::kCapturedObject) {
CHECK_EQ(slot, GetValueByObjectIndex(slot->object_index()));
if (slot->materialization_state() == TranslatedValue::kFinished) {
slot->GetStorage()->ObjectVerify();
} else {
CHECK_EQ(slot->materialization_state(),
TranslatedValue::kUninitialized);
}
} }
} }
#endif
} }
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
// Undefine the heap manipulation macros.
#include "src/objects/object-macros-undef.h"
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#ifndef V8_DEOPTIMIZER_H_ #ifndef V8_DEOPTIMIZER_H_
#define V8_DEOPTIMIZER_H_ #define V8_DEOPTIMIZER_H_
#include <stack>
#include <vector> #include <vector>
#include "src/allocation.h" #include "src/allocation.h"
...@@ -31,6 +32,9 @@ class TranslatedValue { ...@@ -31,6 +32,9 @@ class TranslatedValue {
// Returns heap()->arguments_marker() if allocation would be // Returns heap()->arguments_marker() if allocation would be
// necessary to get the value. // necessary to get the value.
Object* GetRawValue() const; Object* GetRawValue() const;
// Getter for the value, takes care of materializing the subgraph
// reachable from this value.
Handle<Object> GetValue(); Handle<Object> GetValue();
bool IsMaterializedObject() const; bool IsMaterializedObject() const;
...@@ -40,7 +44,7 @@ class TranslatedValue { ...@@ -40,7 +44,7 @@ class TranslatedValue {
friend class TranslatedState; friend class TranslatedState;
friend class TranslatedFrame; friend class TranslatedFrame;
enum Kind { enum Kind : uint8_t {
kInvalid, kInvalid,
kTagged, kTagged,
kInt32, kInt32,
...@@ -56,9 +60,20 @@ class TranslatedValue { ...@@ -56,9 +60,20 @@ class TranslatedValue {
kDuplicatedObject // Duplicated object of a deferred object. kDuplicatedObject // Duplicated object of a deferred object.
}; };
enum MaterializationState : uint8_t {
kUninitialized,
kAllocated, // Storage for the object has been allocated (or
// enqueued for allocation).
kFinished, // The object has been initialized (or enqueued for
// initialization).
};
TranslatedValue(TranslatedState* container, Kind kind) TranslatedValue(TranslatedState* container, Kind kind)
: kind_(kind), container_(container) {} : kind_(kind), container_(container) {}
Kind kind() const { return kind_; } Kind kind() const { return kind_; }
MaterializationState materialization_state() const {
return materialization_state_;
}
void Handlify(); void Handlify();
int GetChildrenCount() const; int GetChildrenCount() const;
...@@ -76,15 +91,25 @@ class TranslatedValue { ...@@ -76,15 +91,25 @@ class TranslatedValue {
Isolate* isolate() const; Isolate* isolate() const;
void MaterializeSimple(); void MaterializeSimple();
void set_storage(Handle<HeapObject> storage) { storage_ = storage; }
void set_initialized_storage(Handle<Object> storage);
void mark_finished() { materialization_state_ = kFinished; }
void mark_allocated() { materialization_state_ = kAllocated; }
Handle<Object> GetStorage() {
DCHECK_NE(kUninitialized, materialization_state());
return storage_;
}
Kind kind_; Kind kind_;
MaterializationState materialization_state_ = kUninitialized;
TranslatedState* container_; // This is only needed for materialization of TranslatedState* container_; // This is only needed for materialization of
// objects and constructing handles (to get // objects and constructing handles (to get
// to the isolate). // to the isolate).
MaybeHandle<Object> value_; // Before handlification, this is always null, Handle<Object> storage_; // Contains the materialized value or the
// after materialization it is never null, // byte-array that will be later morphed into
// in between it is only null if the value needs // the materialized object.
// to be materialized.
struct MaterializedObjectInfo { struct MaterializedObjectInfo {
int id_; int id_;
...@@ -211,6 +236,7 @@ class TranslatedFrame { ...@@ -211,6 +236,7 @@ class TranslatedFrame {
height_(height) {} height_(height) {}
void Add(const TranslatedValue& value) { values_.push_back(value); } void Add(const TranslatedValue& value) { values_.push_back(value); }
TranslatedValue* ValueAt(int index) { return &(values_[index]); }
void Handlify(); void Handlify();
Kind kind_; Kind kind_;
...@@ -270,6 +296,8 @@ class TranslatedState { ...@@ -270,6 +296,8 @@ class TranslatedState {
FixedArray* literal_array, RegisterValues* registers, FixedArray* literal_array, RegisterValues* registers,
FILE* trace_file, int parameter_count); FILE* trace_file, int parameter_count);
void VerifyMaterializedObjects();
private: private:
friend TranslatedValue; friend TranslatedValue;
...@@ -288,11 +316,36 @@ class TranslatedState { ...@@ -288,11 +316,36 @@ class TranslatedState {
FILE* trace_file); FILE* trace_file);
void UpdateFromPreviouslyMaterializedObjects(); void UpdateFromPreviouslyMaterializedObjects();
Handle<Object> MaterializeAt(int frame_index, int* value_index); void MaterializeFixedDoubleArray(TranslatedFrame* frame, int* value_index,
Handle<Object> MaterializeObjectAt(int object_index); TranslatedValue* slot, Handle<Map> map);
class CapturedObjectMaterializer; void MaterializeMutableHeapNumber(TranslatedFrame* frame, int* value_index,
Handle<Object> MaterializeCapturedObjectAt(TranslatedValue* slot, TranslatedValue* slot);
int frame_index, int* value_index);
void EnsureObjectAllocatedAt(TranslatedValue* slot);
void SkipSlots(int slots_to_skip, TranslatedFrame* frame, int* value_index);
Handle<ByteArray> AllocateStorageFor(TranslatedValue* slot);
void EnsureJSObjectAllocated(TranslatedValue* slot, Handle<Map> map);
void EnsurePropertiesAllocatedAndMarked(TranslatedValue* properties_slot,
Handle<Map> map);
void EnsureChildrenAllocated(int count, TranslatedFrame* frame,
int* value_index, std::stack<int>* worklist);
void EnsureCapturedObjectAllocatedAt(int object_index,
std::stack<int>* worklist);
Handle<Object> InitializeObjectAt(TranslatedValue* slot);
void InitializeCapturedObjectAt(int object_index, std::stack<int>* worklist,
const DisallowHeapAllocation& no_allocation);
void InitializeJSObjectAt(TranslatedFrame* frame, int* value_index,
TranslatedValue* slot, Handle<Map> map,
const DisallowHeapAllocation& no_allocation);
void InitializeObjectWithTaggedFieldsAt(
TranslatedFrame* frame, int* value_index, TranslatedValue* slot,
Handle<Map> map, const DisallowHeapAllocation& no_allocation);
TranslatedValue* ResolveCapturedObject(TranslatedValue* slot);
TranslatedValue* GetValueByObjectIndex(int object_index);
Handle<Object> GetValueAndAdvance(TranslatedFrame* frame, int* value_index);
static uint32_t GetUInt32Slot(Address fp, int slot_index); static uint32_t GetUInt32Slot(Address fp, int slot_index);
static Float32 GetFloatSlot(Address fp, int slot_index); static Float32 GetFloatSlot(Address fp, int slot_index);
......
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
function f() {
// Create a non-escaping object.
var o = Object.create(null);
%DeoptimizeNow();
// Keep it alive.
return o ? 1 : 0;
}
f();
f();
%OptimizeFunctionOnNextCall(f);
assertEquals(1, f());
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
function C() {}
%CompleteInobjectSlackTracking(new C());
function f() {
// Create a non-escaping object.
var o = new C();
// Add an out-of-object double property.
o.x = 0.5;
%DeoptimizeNow();
return o.x + 0.25;
}
f();
f();
%OptimizeFunctionOnNextCall(f);
assertEquals(0.75, 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