Commit f6d1a4fe authored by yangguo@chromium.org's avatar yangguo@chromium.org

Allow externalizing strings in old pointer space.

This is what I think is a better solution to the "external strings in
old pointer space" problem. Basically, it is an issue because GC scans
all fields of objects in old pointer space and if the cached address
of the backing store is unaligned, it looks like a heap object, boom.

The solution here is to use short external strings when we externalize
a string in old pointer space, and when the address is unaligned.
Short external strings don't cache the address, so GC has no issues.

BUG=268686
LOG=Y
R=dcarney@chromium.org

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@19093 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent dd539775
...@@ -5426,23 +5426,6 @@ static i::Handle<i::String> NewExternalAsciiStringHandle( ...@@ -5426,23 +5426,6 @@ static i::Handle<i::String> NewExternalAsciiStringHandle(
} }
static bool RedirectToExternalString(i::Isolate* isolate,
i::Handle<i::String> parent,
i::Handle<i::String> external) {
if (parent->IsConsString()) {
i::Handle<i::ConsString> cons = i::Handle<i::ConsString>::cast(parent);
cons->set_first(*external);
cons->set_second(isolate->heap()->empty_string());
} else {
ASSERT(parent->IsSlicedString());
i::Handle<i::SlicedString> slice = i::Handle<i::SlicedString>::cast(parent);
slice->set_parent(*external);
slice->set_offset(0);
}
return true;
}
Local<String> v8::String::NewExternal( Local<String> v8::String::NewExternal(
Isolate* isolate, Isolate* isolate,
v8::String::ExternalStringResource* resource) { v8::String::ExternalStringResource* resource) {
...@@ -5472,22 +5455,10 @@ bool v8::String::MakeExternal(v8::String::ExternalStringResource* resource) { ...@@ -5472,22 +5455,10 @@ bool v8::String::MakeExternal(v8::String::ExternalStringResource* resource) {
} }
CHECK(resource && resource->data()); CHECK(resource && resource->data());
bool result; bool result = obj->MakeExternal(resource);
i::Handle<i::String> external;
if (isolate->heap()->old_pointer_space()->Contains(*obj)) {
// We do not allow external strings in the old pointer space. Instead of
// converting the string in-place, we keep the cons/sliced string and
// point it to a newly-allocated external string.
external = NewExternalStringHandle(isolate, resource);
result = RedirectToExternalString(isolate, obj, external);
} else {
result = obj->MakeExternal(resource);
external = obj;
}
if (result) { if (result) {
ASSERT(external->IsExternalString()); ASSERT(obj->IsExternalString());
isolate->heap()->external_string_table()->AddString(*external); isolate->heap()->external_string_table()->AddString(*obj);
} }
return result; return result;
} }
...@@ -5524,22 +5495,10 @@ bool v8::String::MakeExternal( ...@@ -5524,22 +5495,10 @@ bool v8::String::MakeExternal(
} }
CHECK(resource && resource->data()); CHECK(resource && resource->data());
bool result; bool result = obj->MakeExternal(resource);
i::Handle<i::String> external;
if (isolate->heap()->old_pointer_space()->Contains(*obj)) {
// We do not allow external strings in the old pointer space. Instead of
// converting the string in-place, we keep the cons/sliced string and
// point it to a newly-allocated external string.
external = NewExternalAsciiStringHandle(isolate, resource);
result = RedirectToExternalString(isolate, obj, external);
} else {
result = obj->MakeExternal(resource);
external = obj;
}
if (result) { if (result) {
ASSERT(external->IsExternalString()); ASSERT(obj->IsExternalString());
isolate->heap()->external_string_table()->AddString(*external); isolate->heap()->external_string_table()->AddString(*obj);
} }
return result; return result;
} }
......
...@@ -418,7 +418,7 @@ AllocationSpace Heap::TargetSpaceId(InstanceType type) { ...@@ -418,7 +418,7 @@ AllocationSpace Heap::TargetSpaceId(InstanceType type) {
} }
bool Heap::AllowedToBeMigrated(HeapObject* object, AllocationSpace dst) { bool Heap::AllowedToBeMigrated(HeapObject* obj, AllocationSpace dst) {
// Object migration is governed by the following rules: // Object migration is governed by the following rules:
// //
// 1) Objects in new-space can be migrated to one of the old spaces // 1) Objects in new-space can be migrated to one of the old spaces
...@@ -428,18 +428,22 @@ bool Heap::AllowedToBeMigrated(HeapObject* object, AllocationSpace dst) { ...@@ -428,18 +428,22 @@ bool Heap::AllowedToBeMigrated(HeapObject* object, AllocationSpace dst) {
// fixed arrays in new-space, old-data-space and old-pointer-space. // fixed arrays in new-space, old-data-space and old-pointer-space.
// 4) Fillers (one word) can never migrate, they are skipped by // 4) Fillers (one word) can never migrate, they are skipped by
// incremental marking explicitly to prevent invalid pattern. // incremental marking explicitly to prevent invalid pattern.
// 5) Short external strings can end up in old pointer space when a cons
// string in old pointer space is made external (String::MakeExternal).
// //
// Since this function is used for debugging only, we do not place // Since this function is used for debugging only, we do not place
// asserts here, but check everything explicitly. // asserts here, but check everything explicitly.
if (object->map() == one_pointer_filler_map()) return false; if (obj->map() == one_pointer_filler_map()) return false;
InstanceType type = object->map()->instance_type(); InstanceType type = obj->map()->instance_type();
MemoryChunk* chunk = MemoryChunk::FromAddress(object->address()); MemoryChunk* chunk = MemoryChunk::FromAddress(obj->address());
AllocationSpace src = chunk->owner()->identity(); AllocationSpace src = chunk->owner()->identity();
switch (src) { switch (src) {
case NEW_SPACE: case NEW_SPACE:
return dst == src || dst == TargetSpaceId(type); return dst == src || dst == TargetSpaceId(type);
case OLD_POINTER_SPACE: case OLD_POINTER_SPACE:
return dst == src && (dst == TargetSpaceId(type) || object->IsFiller()); return dst == src &&
(dst == TargetSpaceId(type) || obj->IsFiller() ||
(obj->IsExternalString() && ExternalString::cast(obj)->is_short()));
case OLD_DATA_SPACE: case OLD_DATA_SPACE:
return dst == src && dst == TargetSpaceId(type); return dst == src && dst == TargetSpaceId(type);
case CODE_SPACE: case CODE_SPACE:
......
...@@ -3193,6 +3193,7 @@ void ExternalAsciiString::update_data_cache() { ...@@ -3193,6 +3193,7 @@ void ExternalAsciiString::update_data_cache() {
void ExternalAsciiString::set_resource( void ExternalAsciiString::set_resource(
const ExternalAsciiString::Resource* resource) { const ExternalAsciiString::Resource* resource) {
ASSERT(IsAligned(reinterpret_cast<intptr_t>(resource), kPointerSize));
*reinterpret_cast<const Resource**>( *reinterpret_cast<const Resource**>(
FIELD_ADDR(this, kResourceOffset)) = resource; FIELD_ADDR(this, kResourceOffset)) = resource;
if (resource != NULL) update_data_cache(); if (resource != NULL) update_data_cache();
......
...@@ -1273,27 +1273,37 @@ bool String::MakeExternal(v8::String::ExternalStringResource* resource) { ...@@ -1273,27 +1273,37 @@ bool String::MakeExternal(v8::String::ExternalStringResource* resource) {
bool is_ascii = this->IsOneByteRepresentation(); bool is_ascii = this->IsOneByteRepresentation();
bool is_internalized = this->IsInternalizedString(); bool is_internalized = this->IsInternalizedString();
// Morph the object to an external string by adjusting the map and // Morph the string to an external string by replacing the map and
// reinitializing the fields. // reinitializing the fields. This won't work if
if (size >= ExternalString::kSize) { // - the space the existing string occupies is too small for a regular
// external string.
// - the existing string is in old pointer space and the backing store of
// the external string is not aligned. The GC cannot deal with fields
// containing an unaligned address that points to outside of V8's heap.
// In either case we resort to a short external string instead, omitting
// the field caching the address of the backing store. When we encounter
// short external strings in generated code, we need to bailout to runtime.
if (size < ExternalString::kSize ||
(!IsAligned(reinterpret_cast<intptr_t>(resource->data()), kPointerSize) &&
heap->old_pointer_space()->Contains(this))) {
this->set_map_no_write_barrier( this->set_map_no_write_barrier(
is_internalized is_internalized
? (is_ascii ? (is_ascii
? heap->external_internalized_string_with_one_byte_data_map() ? heap->
: heap->external_internalized_string_map()) short_external_internalized_string_with_one_byte_data_map()
: heap->short_external_internalized_string_map())
: (is_ascii : (is_ascii
? heap->external_string_with_one_byte_data_map() ? heap->short_external_string_with_one_byte_data_map()
: heap->external_string_map())); : heap->short_external_string_map()));
} else { } else {
this->set_map_no_write_barrier( this->set_map_no_write_barrier(
is_internalized is_internalized
? (is_ascii ? (is_ascii
? heap-> ? heap->external_internalized_string_with_one_byte_data_map()
short_external_internalized_string_with_one_byte_data_map() : heap->external_internalized_string_map())
: heap->short_external_internalized_string_map()) : (is_ascii
: (is_ascii ? heap->external_string_with_one_byte_data_map()
? heap->short_external_string_with_one_byte_data_map() : heap->external_string_map()));
: heap->short_external_string_map()));
} }
ExternalTwoByteString* self = ExternalTwoByteString::cast(this); ExternalTwoByteString* self = ExternalTwoByteString::cast(this);
self->set_resource(resource); self->set_resource(resource);
...@@ -1334,16 +1344,26 @@ bool String::MakeExternal(v8::String::ExternalAsciiStringResource* resource) { ...@@ -1334,16 +1344,26 @@ bool String::MakeExternal(v8::String::ExternalAsciiStringResource* resource) {
} }
bool is_internalized = this->IsInternalizedString(); bool is_internalized = this->IsInternalizedString();
// Morph the object to an external string by adjusting the map and // Morph the string to an external string by replacing the map and
// reinitializing the fields. Use short version if space is limited. // reinitializing the fields. This won't work if
if (size >= ExternalString::kSize) { // - the space the existing string occupies is too small for a regular
this->set_map_no_write_barrier( // external string.
is_internalized ? heap->external_ascii_internalized_string_map() // - the existing string is in old pointer space and the backing store of
: heap->external_ascii_string_map()); // the external string is not aligned. The GC cannot deal with fields
} else { // containing an unaligned address that points to outside of V8's heap.
// In either case we resort to a short external string instead, omitting
// the field caching the address of the backing store. When we encounter
// short external strings in generated code, we need to bailout to runtime.
if (size < ExternalString::kSize ||
(!IsAligned(reinterpret_cast<intptr_t>(resource->data()), kPointerSize) &&
heap->old_pointer_space()->Contains(this))) {
this->set_map_no_write_barrier( this->set_map_no_write_barrier(
is_internalized ? heap->short_external_ascii_internalized_string_map() is_internalized ? heap->short_external_ascii_internalized_string_map()
: heap->short_external_ascii_string_map()); : heap->short_external_ascii_string_map());
} else {
this->set_map_no_write_barrier(
is_internalized ? heap->external_ascii_internalized_string_map()
: heap->external_ascii_string_map());
} }
ExternalAsciiString* self = ExternalAsciiString::cast(this); ExternalAsciiString* self = ExternalAsciiString::cast(this);
self->set_resource(resource); self->set_resource(resource);
......
...@@ -17862,6 +17862,50 @@ class VisitorImpl : public v8::ExternalResourceVisitor { ...@@ -17862,6 +17862,50 @@ class VisitorImpl : public v8::ExternalResourceVisitor {
}; };
TEST(ExternalizeOldSpaceTwoByteCons) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
v8::Local<v8::String> cons =
CompileRun("'Romeo Montague ' + 'Juliet Capulet'")->ToString();
CHECK(v8::Utils::OpenHandle(*cons)->IsConsString());
CcTest::heap()->CollectAllAvailableGarbage();
CHECK(CcTest::heap()->old_pointer_space()->Contains(
*v8::Utils::OpenHandle(*cons)));
TestResource* resource = new TestResource(
AsciiToTwoByteString("Romeo Montague Juliet Capulet"));
cons->MakeExternal(resource);
CHECK(cons->IsExternal());
CHECK_EQ(resource, cons->GetExternalStringResource());
String::Encoding encoding;
CHECK_EQ(resource, cons->GetExternalStringResourceBase(&encoding));
CHECK_EQ(String::TWO_BYTE_ENCODING, encoding);
}
TEST(ExternalizeOldSpaceOneByteCons) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
v8::Local<v8::String> cons =
CompileRun("'Romeo Montague ' + 'Juliet Capulet'")->ToString();
CHECK(v8::Utils::OpenHandle(*cons)->IsConsString());
CcTest::heap()->CollectAllAvailableGarbage();
CHECK(CcTest::heap()->old_pointer_space()->Contains(
*v8::Utils::OpenHandle(*cons)));
TestAsciiResource* resource =
new TestAsciiResource(i::StrDup("Romeo Montague Juliet Capulet"));
cons->MakeExternal(resource);
CHECK(cons->IsExternalAscii());
CHECK_EQ(resource, cons->GetExternalAsciiStringResource());
String::Encoding encoding;
CHECK_EQ(resource, cons->GetExternalStringResourceBase(&encoding));
CHECK_EQ(String::ONE_BYTE_ENCODING, encoding);
}
TEST(VisitExternalStrings) { TEST(VisitExternalStrings) {
LocalContext env; LocalContext env;
v8::HandleScope scope(env->GetIsolate()); v8::HandleScope scope(env->GetIsolate());
......
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