Commit f48c9f65 authored by danno@chromium.org's avatar danno@chromium.org

Basic support for tracking smi-only arrays on ia32.

Activated by the flag --smi-only-arrays

Currently not crankshaft support, using flag on non-ia32 platforms will lead to write barrier misses and crashes.

BUG=none
TEST=elements_kind.js

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@9392 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent e04d0b23
......@@ -2615,6 +2615,7 @@ void LCodeGen::DoLoadKeyedSpecializedArrayElement(
case EXTERNAL_DOUBLE_ELEMENTS:
case FAST_DOUBLE_ELEMENTS:
case FAST_ELEMENTS:
case FAST_SMI_ONLY_ELEMENTS:
case DICTIONARY_ELEMENTS:
case NON_STRICT_ARGUMENTS_ELEMENTS:
UNREACHABLE();
......@@ -3457,6 +3458,7 @@ void LCodeGen::DoStoreKeyedSpecializedArrayElement(
case EXTERNAL_DOUBLE_ELEMENTS:
case FAST_DOUBLE_ELEMENTS:
case FAST_ELEMENTS:
case FAST_SMI_ONLY_ELEMENTS:
case DICTIONARY_ELEMENTS:
case NON_STRICT_ARGUMENTS_ELEMENTS:
UNREACHABLE();
......
......@@ -1859,7 +1859,8 @@ void MacroAssembler::CompareRoot(Register obj,
void MacroAssembler::CheckFastElements(Register map,
Register scratch,
Label* fail) {
STATIC_ASSERT(FAST_ELEMENTS == 0);
STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0);
STATIC_ASSERT(FAST_ELEMENTS == 1);
ldrb(scratch, FieldMemOperand(map, Map::kBitField2Offset));
cmp(scratch, Operand(Map::kMaximumBitField2FastElementValue));
b(hi, fail);
......
......@@ -3493,6 +3493,7 @@ static bool IsElementTypeSigned(ElementsKind elements_kind) {
case EXTERNAL_FLOAT_ELEMENTS:
case EXTERNAL_DOUBLE_ELEMENTS:
case FAST_ELEMENTS:
case FAST_SMI_ONLY_ELEMENTS:
case FAST_DOUBLE_ELEMENTS:
case DICTIONARY_ELEMENTS:
case NON_STRICT_ARGUMENTS_ELEMENTS:
......@@ -3579,6 +3580,7 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray(
}
break;
case FAST_ELEMENTS:
case FAST_SMI_ONLY_ELEMENTS:
case FAST_DOUBLE_ELEMENTS:
case DICTIONARY_ELEMENTS:
case NON_STRICT_ARGUMENTS_ELEMENTS:
......@@ -3919,6 +3921,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray(
}
break;
case FAST_ELEMENTS:
case FAST_SMI_ONLY_ELEMENTS:
case FAST_DOUBLE_ELEMENTS:
case DICTIONARY_ELEMENTS:
case NON_STRICT_ARGUMENTS_ELEMENTS:
......@@ -3982,6 +3985,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray(
case EXTERNAL_FLOAT_ELEMENTS:
case EXTERNAL_DOUBLE_ELEMENTS:
case FAST_ELEMENTS:
case FAST_SMI_ONLY_ELEMENTS:
case FAST_DOUBLE_ELEMENTS:
case DICTIONARY_ELEMENTS:
case NON_STRICT_ARGUMENTS_ELEMENTS:
......@@ -4121,6 +4125,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray(
case EXTERNAL_FLOAT_ELEMENTS:
case EXTERNAL_DOUBLE_ELEMENTS:
case FAST_ELEMENTS:
case FAST_SMI_ONLY_ELEMENTS:
case FAST_DOUBLE_ELEMENTS:
case DICTIONARY_ELEMENTS:
case NON_STRICT_ARGUMENTS_ELEMENTS:
......@@ -4273,8 +4278,10 @@ void KeyedLoadStubCompiler::GenerateLoadFastDoubleElement(
}
void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm,
bool is_js_array) {
void KeyedStoreStubCompiler::GenerateStoreFastElement(
MacroAssembler* masm,
bool is_js_array,
ElementsKind elements_kind) {
// ----------- S t a t e -------------
// -- r0 : value
// -- r1 : key
......@@ -4324,6 +4331,7 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm,
Operand(key_reg, LSL, kPointerSizeLog2 - kSmiTagSize));
__ str(value_reg, MemOperand(scratch));
__ mov(receiver_reg, value_reg);
ASSERT(elements_kind == FAST_ELEMENTS);
__ RecordWrite(elements_reg, // Object.
scratch, // Address.
receiver_reg, // Value.
......
......@@ -1076,6 +1076,11 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> inner_global,
elements->set(0, *array);
array = factory->NewFixedArray(0);
elements->set(1, *array);
Handle<Map> non_strict_arguments_elements_map =
factory->GetElementsTransitionMap(result,
NON_STRICT_ARGUMENTS_ELEMENTS);
result->set_map(*non_strict_arguments_elements_map);
ASSERT(result->HasNonStrictArgumentsElements());
result->set_elements(*elements);
global_context()->set_aliased_arguments_boilerplate(*result);
}
......@@ -1557,6 +1562,18 @@ bool Genesis::InstallNatives() {
isolate()->builtins()->builtin(Builtins::kArrayConstructCode));
array_function->shared()->DontAdaptArguments();
// InternalArrays should not use Smi-Only array optimizations. There are too
// many places in the C++ runtime code (e.g. RegEx) that assume that
// elements in InternalArrays can be set to non-Smi values without going
// through a common bottleneck that would make the SMI_ONLY -> FAST_ELEMENT
// transition easy to trap. Moreover, they rarely are smi-only.
MaybeObject* maybe_map =
array_function->initial_map()->CopyDropTransitions();
Map* new_map;
if (!maybe_map->To<Map>(&new_map)) return maybe_map;
new_map->set_elements_kind(FAST_ELEMENTS);
array_function->set_initial_map(new_map);
// Make "length" magic on instances.
Handle<DescriptorArray> array_descriptors =
factory()->CopyAppendForeignDescriptor(
......
......@@ -203,7 +203,7 @@ BUILTIN(ArrayCodeGeneric) {
}
// 'array' now contains the JSArray we should initialize.
ASSERT(array->HasFastElements());
ASSERT(array->HasFastTypeElements());
// Optimize the case where there is one argument and the argument is a
// small smi.
......@@ -216,7 +216,8 @@ BUILTIN(ArrayCodeGeneric) {
{ MaybeObject* maybe_obj = heap->AllocateFixedArrayWithHoles(len);
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
}
array->SetContent(FixedArray::cast(obj));
MaybeObject* maybe_obj = array->SetContent(FixedArray::cast(obj));
if (maybe_obj->IsFailure()) return maybe_obj;
return array;
}
}
......@@ -240,6 +241,13 @@ BUILTIN(ArrayCodeGeneric) {
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
}
// Set length and elements on the array.
if (FLAG_smi_only_arrays) {
MaybeObject* maybe_object =
array->EnsureCanContainElements(FixedArray::cast(obj));
if (maybe_object->IsFailure()) return maybe_object;
}
AssertNoAllocation no_gc;
FixedArray* elms = FixedArray::cast(obj);
WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc);
......@@ -248,7 +256,6 @@ BUILTIN(ArrayCodeGeneric) {
elms->set(index, args[index+1], mode);
}
// Set length and elements on the array.
array->set_elements(FixedArray::cast(obj));
array->set_length(len);
......@@ -486,9 +493,11 @@ BUILTIN(ArrayPush) {
FillWithHoles(heap, new_elms, new_length, capacity);
elms = new_elms;
array->set_elements(elms);
}
MaybeObject* maybe = array->EnsureCanContainElements(&args, 1, to_add);
if (maybe->IsFailure()) return maybe;
// Add the provided values.
AssertNoAllocation no_gc;
WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc);
......@@ -496,6 +505,10 @@ BUILTIN(ArrayPush) {
elms->set(index + len, args[index + 1], mode);
}
if (elms != array->elements()) {
array->set_elements(elms);
}
// Set the length.
array->set_length(Smi::FromInt(new_length));
return Smi::FromInt(new_length);
......@@ -550,7 +563,7 @@ BUILTIN(ArrayShift) {
}
FixedArray* elms = FixedArray::cast(elms_obj);
JSArray* array = JSArray::cast(receiver);
ASSERT(array->HasFastElements());
ASSERT(array->HasFastTypeElements());
int len = Smi::cast(array->length())->value();
if (len == 0) return heap->undefined_value();
......@@ -592,7 +605,7 @@ BUILTIN(ArrayUnshift) {
}
FixedArray* elms = FixedArray::cast(elms_obj);
JSArray* array = JSArray::cast(receiver);
ASSERT(array->HasFastElements());
ASSERT(array->HasFastTypeElements());
int len = Smi::cast(array->length())->value();
int to_add = args.length() - 1;
......@@ -601,6 +614,12 @@ BUILTIN(ArrayUnshift) {
// we should never hit this case.
ASSERT(to_add <= (Smi::kMaxValue - len));
if (FLAG_smi_only_arrays) {
MaybeObject* maybe_object =
array->EnsureCanContainElements(&args, 1, to_add);
if (maybe_object->IsFailure()) return maybe_object;
}
if (new_length > elms->length()) {
// New backing storage is needed.
int capacity = new_length + (new_length >> 1) + 16;
......@@ -609,13 +628,11 @@ BUILTIN(ArrayUnshift) {
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
}
FixedArray* new_elms = FixedArray::cast(obj);
AssertNoAllocation no_gc;
if (len > 0) {
CopyElements(heap, &no_gc, new_elms, to_add, elms, 0, len);
}
FillWithHoles(heap, new_elms, new_length, capacity);
elms = new_elms;
array->set_elements(elms);
} else {
......@@ -730,6 +747,12 @@ BUILTIN(ArraySlice) {
}
FixedArray* result_elms = FixedArray::cast(result);
if (FLAG_smi_only_arrays) {
MaybeObject* maybe_object =
result_array->EnsureCanContainElements(result_elms);
if (maybe_object->IsFailure()) return maybe_object;
}
AssertNoAllocation no_gc;
CopyElements(heap, &no_gc, result_elms, 0, elms, k, result_len);
......@@ -757,7 +780,7 @@ BUILTIN(ArraySplice) {
}
FixedArray* elms = FixedArray::cast(elms_obj);
JSArray* array = JSArray::cast(receiver);
ASSERT(array->HasFastElements());
ASSERT(array->HasFastTypeElements());
int len = Smi::cast(array->length())->value();
......@@ -835,8 +858,14 @@ BUILTIN(ArraySplice) {
int item_count = (n_arguments > 1) ? (n_arguments - 2) : 0;
if (FLAG_smi_only_arrays) {
MaybeObject* maybe = array->EnsureCanContainElements(&args, 3, item_count);
if (maybe->IsFailure()) return maybe;
}
int new_length = len - actual_delete_count + item_count;
bool elms_changed = false;
if (item_count < actual_delete_count) {
// Shrink the array.
const bool trim_array = !heap->lo_space()->Contains(elms) &&
......@@ -851,7 +880,8 @@ BUILTIN(ArraySplice) {
}
elms = LeftTrimFixedArray(heap, elms, delta);
array->set_elements(elms);
elms_changed = true;
} else {
AssertNoAllocation no_gc;
MoveElements(heap, &no_gc,
......@@ -891,7 +921,7 @@ BUILTIN(ArraySplice) {
FillWithHoles(heap, new_elms, new_length, capacity);
elms = new_elms;
array->set_elements(elms);
elms_changed = true;
} else {
AssertNoAllocation no_gc;
MoveElements(heap, &no_gc,
......@@ -907,6 +937,10 @@ BUILTIN(ArraySplice) {
elms->set(k, args[3 + k - actual_start], mode);
}
if (elms_changed) {
array->set_elements(elms);
}
// Set the length.
array->set_length(Smi::FromInt(new_length));
......@@ -965,6 +999,19 @@ BUILTIN(ArrayConcat) {
}
FixedArray* result_elms = FixedArray::cast(result);
if (FLAG_smi_only_arrays) {
for (int i = 0; i < n_arguments; i++) {
JSArray* array = JSArray::cast(args[i]);
int len = Smi::cast(array->length())->value();
if (len > 0) {
FixedArray* elms = FixedArray::cast(array->elements());
MaybeObject* maybe_object =
result_array->EnsureCanContainElements(elms);
if (maybe_object->IsFailure()) return maybe_object;
}
}
}
// Copy data.
AssertNoAllocation no_gc;
int start_pos = 0;
......
......@@ -250,6 +250,7 @@ void InstanceofStub::PrintName(StringStream* stream) {
void KeyedLoadElementStub::Generate(MacroAssembler* masm) {
switch (elements_kind_) {
case FAST_ELEMENTS:
case FAST_SMI_ONLY_ELEMENTS:
KeyedLoadStubCompiler::GenerateLoadFastElement(masm);
break;
case FAST_DOUBLE_ELEMENTS:
......@@ -279,7 +280,11 @@ void KeyedLoadElementStub::Generate(MacroAssembler* masm) {
void KeyedStoreElementStub::Generate(MacroAssembler* masm) {
switch (elements_kind_) {
case FAST_ELEMENTS:
KeyedStoreStubCompiler::GenerateStoreFastElement(masm, is_js_array_);
case FAST_SMI_ONLY_ELEMENTS: {
KeyedStoreStubCompiler::GenerateStoreFastElement(masm,
is_js_array_,
elements_kind_);
}
break;
case FAST_DOUBLE_ELEMENTS:
KeyedStoreStubCompiler::GenerateStoreFastDoubleElement(masm,
......
......@@ -227,7 +227,9 @@ class FastElementsAccessor
public:
static MaybeObject* DeleteCommon(JSObject* obj,
uint32_t key) {
ASSERT(obj->HasFastElements() || obj->HasFastArgumentsElements());
ASSERT(obj->HasFastElements() ||
obj->HasFastSmiOnlyElements() ||
obj->HasFastArgumentsElements());
Heap* heap = obj->GetHeap();
FixedArray* backing_store = FixedArray::cast(obj->elements());
if (backing_store->map() == heap->non_strict_arguments_elements_map()) {
......@@ -596,6 +598,9 @@ ElementsAccessor* ElementsAccessor::ForArray(FixedArrayBase* array) {
void ElementsAccessor::InitializeOncePerProcess() {
static struct ConcreteElementsAccessors {
// Use the fast element handler for smi-only arrays. The implementation is
// currently identical.
FastElementsAccessor fast_smi_elements_handler;
FastElementsAccessor fast_elements_handler;
FastDoubleElementsAccessor fast_double_elements_handler;
DictionaryElementsAccessor dictionary_elements_handler;
......@@ -612,6 +617,7 @@ void ElementsAccessor::InitializeOncePerProcess() {
} element_accessors;
static ElementsAccessor* accessor_array[] = {
&element_accessors.fast_smi_elements_handler,
&element_accessors.fast_elements_handler,
&element_accessors.fast_double_elements_handler,
&element_accessors.dictionary_elements_handler,
......@@ -627,6 +633,9 @@ void ElementsAccessor::InitializeOncePerProcess() {
&element_accessors.pixel_elements_handler
};
STATIC_ASSERT((sizeof(accessor_array) / sizeof(*accessor_array)) ==
kElementsKindCount);
elements_accessors_ = accessor_array;
}
......
......@@ -404,10 +404,12 @@ Handle<JSGlobalPropertyCell> Factory::NewJSGlobalPropertyCell(
}
Handle<Map> Factory::NewMap(InstanceType type, int instance_size) {
Handle<Map> Factory::NewMap(InstanceType type,
int instance_size,
ElementsKind elements_kind) {
CALL_HEAP_FUNCTION(
isolate(),
isolate()->heap()->AllocateMap(type, instance_size),
isolate()->heap()->AllocateMap(type, instance_size, elements_kind),
Map);
}
......@@ -710,7 +712,12 @@ Handle<JSFunction> Factory::NewFunctionWithPrototype(Handle<String> name,
if (force_initial_map ||
type != JS_OBJECT_TYPE ||
instance_size != JSObject::kHeaderSize) {
Handle<Map> initial_map = NewMap(type, instance_size);
ElementsKind default_elements_kind = FLAG_smi_only_arrays
? FAST_SMI_ONLY_ELEMENTS
: FAST_ELEMENTS;
Handle<Map> initial_map = NewMap(type,
instance_size,
default_elements_kind);
function->set_initial_map(*initial_map);
initial_map->set_constructor(*function);
}
......@@ -896,11 +903,26 @@ Handle<JSArray> Factory::NewJSArrayWithElements(Handle<FixedArray> elements,
Handle<JSArray> result =
Handle<JSArray>::cast(NewJSObject(isolate()->array_function(),
pretenure));
result->SetContent(*elements);
SetContent(result, elements);
return result;
}
void Factory::SetContent(Handle<JSArray> array,
Handle<FixedArray> elements) {
CALL_HEAP_FUNCTION_VOID(
isolate(),
array->SetContent(*elements));
}
void Factory::EnsureCanContainNonSmiElements(Handle<JSArray> array) {
CALL_HEAP_FUNCTION_VOID(
isolate(),
array->EnsureCanContainNonSmiElements());
}
Handle<JSProxy> Factory::NewJSProxy(Handle<Object> handler,
Handle<Object> prototype) {
CALL_HEAP_FUNCTION(
......
......@@ -203,7 +203,9 @@ class Factory {
Handle<JSGlobalPropertyCell> NewJSGlobalPropertyCell(
Handle<Object> value);
Handle<Map> NewMap(InstanceType type, int instance_size);
Handle<Map> NewMap(InstanceType type,
int instance_size,
ElementsKind elements_kind = FAST_ELEMENTS);
Handle<JSObject> NewFunctionPrototype(Handle<JSFunction> function);
......@@ -253,6 +255,10 @@ class Factory {
Handle<FixedArray> elements,
PretenureFlag pretenure = NOT_TENURED);
void SetContent(Handle<JSArray> array, Handle<FixedArray> elements);
void EnsureCanContainNonSmiElements(Handle<JSArray> array);
Handle<JSProxy> NewJSProxy(Handle<Object> handler, Handle<Object> prototype);
// Change the type of the argument into a JS object/function and reinitialize.
......
......@@ -104,6 +104,7 @@ DEFINE_bool(harmony_block_scoping, false, "enable harmony block scoping")
// Flags for experimental implementation features.
DEFINE_bool(unbox_double_arrays, true, "automatically unbox arrays of doubles")
DEFINE_bool(smi_only_arrays, false, "tracks arrays with only smi values")
DEFINE_bool(string_slices, false, "use string slices")
// Flags for Crankshaft.
......
......@@ -1700,7 +1700,9 @@ MaybeObject* Heap::AllocatePartialMap(InstanceType instance_type,
}
MaybeObject* Heap::AllocateMap(InstanceType instance_type, int instance_size) {
MaybeObject* Heap::AllocateMap(InstanceType instance_type,
int instance_size,
ElementsKind elements_kind) {
Object* result;
{ MaybeObject* maybe_result = AllocateRawMap();
if (!maybe_result->ToObject(&result)) return maybe_result;
......@@ -1722,7 +1724,7 @@ MaybeObject* Heap::AllocateMap(InstanceType instance_type, int instance_size) {
map->set_unused_property_fields(0);
map->set_bit_field(0);
map->set_bit_field2(1 << Map::kIsExtensible);
map->set_elements_kind(FAST_ELEMENTS);
map->set_elements_kind(elements_kind);
// If the map object is aligned fill the padding area with Smi 0 objects.
if (Map::kPadStart < Map::kSize) {
......@@ -2112,7 +2114,13 @@ bool Heap::CreateApiObjects() {
{ MaybeObject* maybe_obj = AllocateMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
if (!maybe_obj->ToObject(&obj)) return false;
}
set_neander_map(Map::cast(obj));
// Don't use Smi-only elements optimizations for objects with the neander
// map. There are too many cases where element values are set directly with a
// bottleneck to trap the Smi-only -> fast elements transition, and there
// appears to be no benefit for optimize this case.
Map* new_neander_map = Map::cast(obj);
new_neander_map->set_elements_kind(FAST_ELEMENTS);
set_neander_map(new_neander_map);
{ MaybeObject* maybe_obj = AllocateJSObjectFromMap(neander_map());
if (!maybe_obj->ToObject(&obj)) return false;
......@@ -3503,7 +3511,8 @@ MaybeObject* Heap::AllocateJSObjectFromMap(Map* map, PretenureFlag pretenure) {
InitializeJSObjectFromMap(JSObject::cast(obj),
FixedArray::cast(properties),
map);
ASSERT(JSObject::cast(obj)->HasFastElements());
ASSERT(JSObject::cast(obj)->HasFastSmiOnlyElements() ||
JSObject::cast(obj)->HasFastElements());
return obj;
}
......@@ -3685,6 +3694,7 @@ MaybeObject* Heap::CopyJSObject(JSObject* source) {
object_size);
}
ASSERT(JSObject::cast(clone)->GetElementsKind() == source->GetElementsKind());
FixedArrayBase* elements = FixedArrayBase::cast(source->elements());
FixedArray* properties = FixedArray::cast(source->properties());
// Update elements if necessary.
......
......@@ -523,8 +523,10 @@ class Heap {
// Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
// failed.
// Please note this function does not perform a garbage collection.
MUST_USE_RESULT MaybeObject* AllocateMap(InstanceType instance_type,
int instance_size);
MUST_USE_RESULT MaybeObject* AllocateMap(
InstanceType instance_type,
int instance_size,
ElementsKind elements_kind = FAST_ELEMENTS);
// Allocates a partial map for bootstrapping.
MUST_USE_RESULT MaybeObject* AllocatePartialMap(InstanceType instance_type,
......
......@@ -1488,6 +1488,7 @@ void HLoadKeyedSpecializedArrayElement::PrintDataTo(
stream->Add("pixel");
break;
case FAST_ELEMENTS:
case FAST_SMI_ONLY_ELEMENTS:
case FAST_DOUBLE_ELEMENTS:
case DICTIONARY_ELEMENTS:
case NON_STRICT_ARGUMENTS_ELEMENTS:
......@@ -1582,6 +1583,7 @@ void HStoreKeyedSpecializedArrayElement::PrintDataTo(
case EXTERNAL_PIXEL_ELEMENTS:
stream->Add("pixel");
break;
case FAST_SMI_ONLY_ELEMENTS:
case FAST_ELEMENTS:
case FAST_DOUBLE_ELEMENTS:
case DICTIONARY_ELEMENTS:
......
......@@ -3336,7 +3336,12 @@ void HGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) {
HValue* key = AddInstruction(
new(zone()) HConstant(Handle<Object>(Smi::FromInt(i)),
Representation::Integer32()));
AddInstruction(new(zone()) HStoreKeyedFastElement(elements, key, value));
if (FLAG_smi_only_arrays) {
AddInstruction(BuildStoreKeyedGeneric(literal, key, value));
} else {
AddInstruction(new(zone()) HStoreKeyedFastElement(elements, key, value));
}
AddSimulate(expr->GetIdForElement(i));
}
return ast_context()->ReturnValue(Pop());
......@@ -3947,6 +3952,7 @@ HInstruction* HGraphBuilder::BuildExternalArrayElementAccess(
case EXTERNAL_FLOAT_ELEMENTS:
case EXTERNAL_DOUBLE_ELEMENTS:
break;
case FAST_SMI_ONLY_ELEMENTS:
case FAST_ELEMENTS:
case FAST_DOUBLE_ELEMENTS:
case DICTIONARY_ELEMENTS:
......@@ -4058,14 +4064,20 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
HLoadExternalArrayPointer* external_elements = NULL;
HInstruction* checked_key = NULL;
// FAST_ELEMENTS is assumed to be the first case.
STATIC_ASSERT(FAST_ELEMENTS == 0);
// Generated code assumes that FAST_SMI_ONLY_ELEMENTS, FAST_ELEMENTS,
// FAST_DOUBLE_ELEMENTS and DICTIONARY_ELEMENTS are handled before external
// arrays.
STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND);
STATIC_ASSERT(FAST_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND);
STATIC_ASSERT(FAST_DOUBLE_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND);
STATIC_ASSERT(DICTIONARY_ELEMENTS < FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND);
for (ElementsKind elements_kind = FAST_ELEMENTS;
for (ElementsKind elements_kind = FIRST_ELEMENTS_KIND;
elements_kind <= LAST_ELEMENTS_KIND;
elements_kind = ElementsKind(elements_kind + 1)) {
// After having handled FAST_ELEMENTS and DICTIONARY_ELEMENTS, we
// need to add some code that's executed for all external array cases.
// After having handled FAST_ELEMENTS, FAST_SMI_ELEMENTS,
// FAST_DOUBLE_ELEMENTS and DICTIONARY_ELEMENTS, we need to add some code
// that's executed for all external array cases.
STATIC_ASSERT(LAST_EXTERNAL_ARRAY_ELEMENTS_KIND ==
LAST_ELEMENTS_KIND);
if (elements_kind == FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND
......@@ -4087,11 +4099,12 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
set_current_block(if_true);
HInstruction* access;
if (elements_kind == FAST_ELEMENTS ||
if (elements_kind == FAST_SMI_ONLY_ELEMENTS ||
elements_kind == FAST_ELEMENTS ||
elements_kind == FAST_DOUBLE_ELEMENTS) {
bool fast_double_elements =
elements_kind == FAST_DOUBLE_ELEMENTS;
if (is_store && elements_kind == FAST_ELEMENTS) {
if (is_store && !fast_double_elements) {
AddInstruction(new(zone()) HCheckMap(
elements, isolate()->factory()->fixed_array_map(),
elements_kind_branch));
......@@ -4105,29 +4118,40 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
current_block()->Finish(typecheck);
set_current_block(if_jsarray);
HInstruction* length = new(zone()) HJSArrayLength(object, typecheck);
AddInstruction(length);
checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
if (is_store) {
if (fast_double_elements) {
access = AddInstruction(
new(zone()) HStoreKeyedFastDoubleElement(elements,
checked_key,
val));
} else {
access = AddInstruction(
new(zone()) HStoreKeyedFastElement(elements, checked_key, val));
}
HInstruction* length;
if (is_store && elements_kind == FAST_SMI_ONLY_ELEMENTS) {
// For now, fall back to the generic stub for
// FAST_SMI_ONLY_ELEMENTS
access = AddInstruction(BuildStoreKeyedGeneric(object, key, val));
} else {
if (fast_double_elements) {
access = AddInstruction(
new(zone()) HLoadKeyedFastDoubleElement(elements, checked_key));
length = new(zone()) HJSArrayLength(object, typecheck);
AddInstruction(length);
checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length));
if (is_store) {
if (fast_double_elements) {
access = AddInstruction(
new(zone()) HStoreKeyedFastDoubleElement(elements,
checked_key,
val));
} else {
access = AddInstruction(
new(zone()) HStoreKeyedFastElement(elements,
checked_key,
val));
}
} else {
access = AddInstruction(
new(zone()) HLoadKeyedFastElement(elements, checked_key));
if (fast_double_elements) {
access = AddInstruction(
new(zone()) HLoadKeyedFastDoubleElement(elements,
checked_key));
} else {
access = AddInstruction(
new(zone()) HLoadKeyedFastElement(elements, checked_key));
}
Push(access);
}
Push(access);
}
*has_side_effects |= access->HasSideEffects();
if (position != -1) {
access->set_position(position);
......
......@@ -1492,8 +1492,20 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
int offset = FixedArray::kHeaderSize + (i * kPointerSize);
__ mov(FieldOperand(ebx, offset), result_register());
Label no_map_change;
__ JumpIfSmi(result_register(), &no_map_change);
// Update the write barrier for the array store.
__ RecordWriteField(ebx, offset, result_register(), ecx, kDontSaveFPRegs);
__ RecordWriteField(ebx, offset, result_register(), ecx,
kDontSaveFPRegs,
EMIT_REMEMBERED_SET,
OMIT_SMI_CHECK);
if (FLAG_smi_only_arrays) {
__ mov(edi, FieldOperand(ebx, JSObject::kMapOffset));
__ CheckFastSmiOnlyElements(edi, &no_map_change, Label::kNear);
__ push(Operand(esp, 0));
__ CallRuntime(Runtime::kNonSmiElementStored, 1);
}
__ bind(&no_map_change);
PrepareForBailoutForId(expr->GetIdForElement(i), NO_REGISTERS);
}
......
......@@ -810,6 +810,19 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm,
// ecx: key (a smi)
// edx: receiver
// edi: FixedArray receiver->elements
if (FLAG_smi_only_arrays) {
Label not_smi_only;
// Make sure the elements are smi-only.
__ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset));
__ CheckFastSmiOnlyElements(ebx, &not_smi_only, Label::kNear);
// Non-smis need to call into the runtime if the array is smi only.
__ JumpIfNotSmi(eax, &slow);
__ mov(CodeGenerator::FixedArrayElementOperand(edi, ecx), eax);
__ ret(0);
__ bind(&not_smi_only);
}
__ mov(CodeGenerator::FixedArrayElementOperand(edi, ecx), eax);
// Update write barrier for the elements array address.
......
......@@ -2400,6 +2400,7 @@ void LCodeGen::DoLoadKeyedSpecializedArrayElement(
break;
case EXTERNAL_FLOAT_ELEMENTS:
case EXTERNAL_DOUBLE_ELEMENTS:
case FAST_SMI_ONLY_ELEMENTS:
case FAST_ELEMENTS:
case FAST_DOUBLE_ELEMENTS:
case DICTIONARY_ELEMENTS:
......@@ -3171,6 +3172,7 @@ void LCodeGen::DoStoreKeyedSpecializedArrayElement(
break;
case EXTERNAL_FLOAT_ELEMENTS:
case EXTERNAL_DOUBLE_ELEMENTS:
case FAST_SMI_ONLY_ELEMENTS:
case FAST_ELEMENTS:
case FAST_DOUBLE_ELEMENTS:
case DICTIONARY_ELEMENTS:
......
......@@ -374,13 +374,38 @@ void MacroAssembler::CmpInstanceType(Register map, InstanceType type) {
void MacroAssembler::CheckFastElements(Register map,
Label* fail,
Label::Distance distance) {
STATIC_ASSERT(FAST_ELEMENTS == 0);
STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0);
STATIC_ASSERT(FAST_ELEMENTS == 1);
cmpb(FieldOperand(map, Map::kBitField2Offset),
Map::kMaximumBitField2FastElementValue);
j(above, fail, distance);
}
void MacroAssembler::CheckFastObjectElements(Register map,
Label* fail,
Label::Distance distance) {
STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0);
STATIC_ASSERT(FAST_ELEMENTS == 1);
cmpb(FieldOperand(map, Map::kBitField2Offset),
Map::kMaximumBitField2FastSmiOnlyElementValue);
j(below_equal, fail, distance);
cmpb(FieldOperand(map, Map::kBitField2Offset),
Map::kMaximumBitField2FastElementValue);
j(above, fail, distance);
}
void MacroAssembler::CheckFastSmiOnlyElements(Register map,
Label* fail,
Label::Distance distance) {
STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0);
cmpb(FieldOperand(map, Map::kBitField2Offset),
Map::kMaximumBitField2FastSmiOnlyElementValue);
j(above, fail, distance);
}
void MacroAssembler::CheckMap(Register obj,
Handle<Map> map,
Label* fail,
......
......@@ -312,6 +312,18 @@ class MacroAssembler: public Assembler {
Label* fail,
Label::Distance distance = Label::kFar);
// Check if a map for a JSObject indicates that the object can have both smi
// and HeapObject elements. Jump to the specified label if it does not.
void CheckFastObjectElements(Register map,
Label* fail,
Label::Distance distance = Label::kFar);
// Check if a map for a JSObject indicates that the object has fast smi only
// elements. Jump to the specified label if it does not.
void CheckFastSmiOnlyElements(Register map,
Label* fail,
Label::Distance distance = Label::kFar);
// Check if the map of an object is equal to a specified map and branch to
// label if not. Skip the smi check if not required (object is known to be a
// heap object)
......
......@@ -1454,7 +1454,7 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object,
__ j(not_equal, &call_builtin);
if (argc == 1) { // Otherwise fall through to call builtin.
Label exit, attempt_to_grow_elements, with_write_barrier;
Label attempt_to_grow_elements, with_write_barrier;
// Get the array's length into eax and calculate new length.
__ mov(eax, FieldOperand(edx, JSArray::kLengthOffset));
......@@ -1469,6 +1469,10 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object,
__ cmp(eax, Operand(ecx));
__ j(greater, &attempt_to_grow_elements);
// Check if value is a smi.
__ mov(ecx, Operand(esp, argc * kPointerSize));
__ JumpIfNotSmi(ecx, &with_write_barrier);
// Save new length.
__ mov(FieldOperand(edx, JSArray::kLengthOffset), eax);
......@@ -1476,17 +1480,26 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object,
__ lea(edx, FieldOperand(ebx,
eax, times_half_pointer_size,
FixedArray::kHeaderSize - argc * kPointerSize));
__ mov(ecx, Operand(esp, argc * kPointerSize));
__ mov(Operand(edx, 0), ecx);
// Check if value is a smi.
__ JumpIfNotSmi(ecx, &with_write_barrier);
__ bind(&exit);
__ ret((argc + 1) * kPointerSize);
__ bind(&with_write_barrier);
if (FLAG_smi_only_arrays) {
__ mov(edi, FieldOperand(edx, HeapObject::kMapOffset));
__ CheckFastObjectElements(edi, &call_builtin);
}
// Save new length.
__ mov(FieldOperand(edx, JSArray::kLengthOffset), eax);
// Push the element.
__ lea(edx, FieldOperand(ebx,
eax, times_half_pointer_size,
FixedArray::kHeaderSize - argc * kPointerSize));
__ mov(Operand(edx, 0), ecx);
__ RecordWrite(
ebx, edx, ecx, kDontSaveFPRegs, EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
......@@ -1497,6 +1510,17 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object,
__ jmp(&call_builtin);
}
__ mov(edi, Operand(esp, argc * kPointerSize));
if (FLAG_smi_only_arrays) {
// Growing elements that are SMI-only requires special handling in case
// the new element is non-Smi. For now, delegate to the builtin.
Label no_fast_elements_check;
__ JumpIfSmi(edi, &no_fast_elements_check);
__ mov(esi, FieldOperand(edx, HeapObject::kMapOffset));
__ CheckFastObjectElements(esi, &call_builtin, Label::kFar);
__ bind(&no_fast_elements_check);
}
// We could be lucky and the elements array could be at the top of
// new-space. In this case we can just grow it in place by moving the
// allocation pointer up.
......@@ -1522,10 +1546,9 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object,
// We fit and could grow elements.
__ mov(Operand::StaticVariable(new_space_allocation_top), ecx);
__ mov(ecx, Operand(esp, argc * kPointerSize));
// Push the argument...
__ mov(Operand(edx, 0), ecx);
__ mov(Operand(edx, 0), edi);
// ... and fill the rest with holes.
for (int i = 1; i < kAllocationDelta; i++) {
__ mov(Operand(edx, i * kPointerSize),
......@@ -1537,7 +1560,7 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object,
// tell the incremental marker to rescan the object that we just grew. We
// don't need to worry about the holes because they are in old space and
// already marked black.
__ RecordWrite(ebx, edx, ecx, kDontSaveFPRegs, OMIT_REMEMBERED_SET);
__ RecordWrite(ebx, edx, edi, kDontSaveFPRegs, OMIT_REMEMBERED_SET);
// Restore receiver to edx as finish sequence assumes it's here.
__ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
......@@ -3877,8 +3900,10 @@ void KeyedLoadStubCompiler::GenerateLoadFastDoubleElement(
}
void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm,
bool is_js_array) {
void KeyedStoreStubCompiler::GenerateStoreFastElement(
MacroAssembler* masm,
bool is_js_array,
ElementsKind elements_kind) {
// ----------- S t a t e -------------
// -- eax : value
// -- ecx : key
......@@ -3909,12 +3934,28 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm,
__ j(above_equal, &miss_force_generic);
}
// Do the store and update the write barrier.
__ lea(ecx, FieldOperand(edi, ecx, times_2, FixedArray::kHeaderSize));
__ mov(Operand(ecx, 0), eax);
// Make sure to preserve the value in register eax.
__ mov(edx, Operand(eax));
__ RecordWrite(edi, ecx, edx, kDontSaveFPRegs);
if (elements_kind == FAST_SMI_ONLY_ELEMENTS) {
__ JumpIfNotSmi(eax, &miss_force_generic);
// ecx is a smi, don't use times_half_pointer_size istead of
// times_pointer_size
__ mov(FieldOperand(edi,
ecx,
times_half_pointer_size,
FixedArray::kHeaderSize), eax);
} else {
ASSERT(elements_kind == FAST_ELEMENTS);
// Do the store and update the write barrier.
// ecx is a smi, don't use times_half_pointer_size istead of
// times_pointer_size
__ lea(ecx, FieldOperand(edi,
ecx,
times_half_pointer_size,
FixedArray::kHeaderSize));
__ mov(Operand(ecx, 0), eax);
// Make sure to preserve the value in register eax.
__ mov(edx, Operand(eax));
__ RecordWrite(edi, ecx, edx, kDontSaveFPRegs);
}
// Done.
__ ret(0);
......
......@@ -1673,6 +1673,7 @@ MaybeObject* KeyedIC::ComputeMonomorphicStubWithoutMapCheck(
} else {
ASSERT(receiver_map->has_dictionary_elements() ||
receiver_map->has_fast_elements() ||
receiver_map->has_fast_smi_only_elements() ||
receiver_map->has_fast_double_elements() ||
receiver_map->has_external_array_elements());
bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE;
......
......@@ -203,6 +203,7 @@ int ElementsKindToShiftSize(ElementsKind elements_kind) {
case EXTERNAL_DOUBLE_ELEMENTS:
case FAST_DOUBLE_ELEMENTS:
return 3;
case FAST_SMI_ONLY_ELEMENTS:
case FAST_ELEMENTS:
case DICTIONARY_ELEMENTS:
case NON_STRICT_ARGUMENTS_ELEMENTS:
......
......@@ -1336,7 +1336,9 @@ MaybeObject* LiveObjectList::DumpPrivate(DumpWriter* writer,
// Allocate the JSArray of the elements.
Handle<JSObject> elements = factory->NewJSObject(isolate->array_function());
if (elements->IsFailure()) return Object::cast(*elements);
Handle<JSArray>::cast(elements)->SetContent(*elements_arr);
maybe_result = Handle<JSArray>::cast(elements)->SetContent(*elements_arr);
if (maybe_result->IsFailure()) return maybe_result;
// Set body.elements.
Handle<String> elements_sym = factory->LookupAsciiSymbol("elements");
......@@ -1462,7 +1464,9 @@ MaybeObject* LiveObjectList::SummarizePrivate(SummaryWriter* writer,
Handle<JSObject> summary_obj =
factory->NewJSObject(isolate->array_function());
if (summary_obj->IsFailure()) return Object::cast(*summary_obj);
Handle<JSArray>::cast(summary_obj)->SetContent(*summary_arr);
maybe_result = Handle<JSArray>::cast(summary_obj)->SetContent(*summary_arr);
if (maybe_result->IsFailure()) return maybe_result;
// Create the body object.
Handle<JSObject> body = factory->NewJSObject(isolate->object_function());
......@@ -1589,7 +1593,9 @@ MaybeObject* LiveObjectList::Info(int start_idx, int dump_limit) {
// Return the result as a JS array.
Handle<JSObject> lols = factory->NewJSObject(isolate->array_function());
Handle<JSArray>::cast(lols)->SetContent(*list);
maybe_result = Handle<JSArray>::cast(lols)->SetContent(*list);
if (maybe_result->IsFailure()) return maybe_result;
Handle<JSObject> result = factory->NewJSObject(isolate->object_function());
if (result->IsFailure()) return Object::cast(*result);
......
......@@ -3163,7 +3163,8 @@ void MacroAssembler::CopyBytes(Register src,
void MacroAssembler::CheckFastElements(Register map,
Register scratch,
Label* fail) {
STATIC_ASSERT(FAST_ELEMENTS == 0);
STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0);
STATIC_ASSERT(FAST_ELEMENTS == 1);
lbu(scratch, FieldMemOperand(map, Map::kBitField2Offset));
Branch(fail, hi, scratch, Operand(Map::kMaximumBitField2FastElementValue));
}
......
......@@ -3471,6 +3471,7 @@ static bool IsElementTypeSigned(ElementsKind elements_kind) {
case EXTERNAL_FLOAT_ELEMENTS:
case EXTERNAL_DOUBLE_ELEMENTS:
case FAST_SMI_ELEMENTS:
case FAST_ELEMENTS:
case FAST_DOUBLE_ELEMENTS:
case DICTIONARY_ELEMENTS:
......
......@@ -268,7 +268,7 @@ void JSObject::JSObjectVerify() {
(map()->inobject_properties() + properties()->length() -
map()->NextFreePropertyIndex()));
}
ASSERT_EQ(map()->has_fast_elements(),
ASSERT_EQ((map()->has_fast_elements() || map()->has_fast_smi_only_elements()),
(elements()->map() == GetHeap()->fixed_array_map() ||
elements()->map() == GetHeap()->fixed_cow_array_map()));
ASSERT(map()->has_fast_elements() == HasFastElements());
......@@ -330,7 +330,8 @@ void FixedDoubleArray::FixedDoubleArrayVerify() {
double value = get_scalar(i);
ASSERT(!isnan(value) ||
(BitCast<uint64_t>(value) ==
BitCast<uint64_t>(canonical_not_the_hole_nan_as_double())));
BitCast<uint64_t>(canonical_not_the_hole_nan_as_double())) ||
((BitCast<uint64_t>(value) & Double::kSignMask) != 0));
}
}
}
......
......@@ -1300,14 +1300,82 @@ FixedArrayBase* JSObject::elements() {
return static_cast<FixedArrayBase*>(array);
}
void JSObject::ValidateSmiOnlyElements() {
#if DEBUG
if (FLAG_smi_only_arrays &&
map()->elements_kind() == FAST_SMI_ONLY_ELEMENTS) {
Heap* heap = GetHeap();
// Don't use elements, since integrity checks will fail if there
// are filler pointers in the array.
FixedArray* fixed_array =
reinterpret_cast<FixedArray*>(READ_FIELD(this, kElementsOffset));
Map* map = fixed_array->map();
// Arrays that have been shifted in place can't be verified.
if (map != heap->raw_unchecked_one_pointer_filler_map() &&
map != heap->raw_unchecked_two_pointer_filler_map() &&
map != heap->free_space_map()) {
for (int i = 0; i < fixed_array->length(); i++) {
Object* current = fixed_array->get(i);
ASSERT(current->IsSmi() || current == heap->the_hole_value());
}
}
}
#endif
}
MaybeObject* JSObject::EnsureCanContainNonSmiElements() {
#if DEBUG
ValidateSmiOnlyElements();
#endif
if (FLAG_smi_only_arrays &&
(map()->elements_kind() == FAST_SMI_ONLY_ELEMENTS)) {
Object* obj;
MaybeObject* maybe_obj = GetElementsTransitionMap(FAST_ELEMENTS);
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
set_map(Map::cast(obj));
}
return this;
}
MaybeObject* JSObject::EnsureCanContainElements(Object** objects,
uint32_t count) {
if (FLAG_smi_only_arrays &&
map()->elements_kind() == FAST_SMI_ONLY_ELEMENTS) {
for (uint32_t i = 0; i < count; ++i) {
Object* current = *objects++;
if (!current->IsSmi() && current != GetHeap()->the_hole_value()) {
return EnsureCanContainNonSmiElements();
}
}
}
return this;
}
MaybeObject* JSObject::EnsureCanContainElements(FixedArray* elements) {
if (FLAG_smi_only_arrays) {
Object** objects = reinterpret_cast<Object**>(
FIELD_ADDR(elements, elements->OffsetOfElementAt(0)));
return EnsureCanContainElements(objects, elements->length());
} else {
return this;
}
}
void JSObject::set_elements(FixedArrayBase* value, WriteBarrierMode mode) {
ASSERT(map()->has_fast_elements() ==
ASSERT((map()->has_fast_elements() ||
map()->has_fast_smi_only_elements()) ==
(value->map() == GetHeap()->fixed_array_map() ||
value->map() == GetHeap()->fixed_cow_array_map()));
ASSERT(map()->has_fast_double_elements() ==
value->IsFixedDoubleArray());
ASSERT(value->HasValidElements());
#ifdef DEBUG
ValidateSmiOnlyElements();
#endif
WRITE_FIELD(this, kElementsOffset, value);
CONDITIONAL_WRITE_BARRIER(GetHeap(), this, kElementsOffset, value, mode);
}
......@@ -1320,7 +1388,7 @@ void JSObject::initialize_properties() {
void JSObject::initialize_elements() {
ASSERT(map()->has_fast_elements());
ASSERT(map()->has_fast_elements() || map()->has_fast_smi_only_elements());
ASSERT(!GetHeap()->InNewSpace(GetHeap()->empty_fixed_array()));
WRITE_FIELD(this, kElementsOffset, GetHeap()->empty_fixed_array());
}
......@@ -1328,9 +1396,11 @@ void JSObject::initialize_elements() {
MaybeObject* JSObject::ResetElements() {
Object* obj;
{ MaybeObject* maybe_obj = GetElementsTransitionMap(FAST_ELEMENTS);
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
}
ElementsKind elements_kind = FLAG_smi_only_arrays
? FAST_SMI_ONLY_ELEMENTS
: FAST_ELEMENTS;
MaybeObject* maybe_obj = GetElementsTransitionMap(elements_kind);
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
set_map(Map::cast(obj));
initialize_elements();
return this;
......@@ -1686,7 +1756,7 @@ void FixedDoubleArray::Initialize(FixedDoubleArray* from) {
void FixedDoubleArray::Initialize(FixedArray* from) {
int old_length = from->length();
ASSERT(old_length < length());
ASSERT(old_length <= length());
for (int i = 0; i < old_length; i++) {
Object* hole_or_object = from->get(i);
if (hole_or_object->IsTheHole()) {
......@@ -3957,15 +4027,20 @@ void JSRegExp::SetDataAtUnchecked(int index, Object* value, Heap* heap) {
ElementsKind JSObject::GetElementsKind() {
ElementsKind kind = map()->elements_kind();
ASSERT((kind == FAST_ELEMENTS &&
(elements()->map() == GetHeap()->fixed_array_map() ||
elements()->map() == GetHeap()->fixed_cow_array_map())) ||
#if DEBUG
FixedArrayBase* fixed_array =
reinterpret_cast<FixedArrayBase*>(READ_FIELD(this, kElementsOffset));
Map* map = fixed_array->map();
ASSERT(((kind == FAST_ELEMENTS || kind == FAST_SMI_ONLY_ELEMENTS) &&
(map == GetHeap()->fixed_array_map() ||
map == GetHeap()->fixed_cow_array_map())) ||
(kind == FAST_DOUBLE_ELEMENTS &&
elements()->IsFixedDoubleArray()) ||
fixed_array->IsFixedDoubleArray()) ||
(kind == DICTIONARY_ELEMENTS &&
elements()->IsFixedArray() &&
elements()->IsDictionary()) ||
fixed_array->IsFixedArray() &&
fixed_array->IsDictionary()) ||
(kind > DICTIONARY_ELEMENTS));
#endif
return kind;
}
......@@ -3980,6 +4055,18 @@ bool JSObject::HasFastElements() {
}
bool JSObject::HasFastSmiOnlyElements() {
return GetElementsKind() == FAST_SMI_ONLY_ELEMENTS;
}
bool JSObject::HasFastTypeElements() {
ElementsKind elements_kind = GetElementsKind();
return elements_kind == FAST_SMI_ONLY_ELEMENTS ||
elements_kind == FAST_ELEMENTS;
}
bool JSObject::HasFastDoubleElements() {
return GetElementsKind() == FAST_DOUBLE_ELEMENTS;
}
......@@ -3990,6 +4077,11 @@ bool JSObject::HasDictionaryElements() {
}
bool JSObject::HasNonStrictArgumentsElements() {
return GetElementsKind() == NON_STRICT_ARGUMENTS_ELEMENTS;
}
bool JSObject::HasExternalArrayElements() {
HeapObject* array = elements();
ASSERT(array != NULL);
......@@ -4041,7 +4133,7 @@ bool JSObject::AllowsSetElementsLength() {
MaybeObject* JSObject::EnsureWritableFastElements() {
ASSERT(HasFastElements());
ASSERT(HasFastTypeElements());
FixedArray* elems = FixedArray::cast(elements());
Isolate* isolate = GetIsolate();
if (elems->map() != isolate->heap()->fixed_cow_array_map()) return elems;
......@@ -4409,7 +4501,7 @@ void Map::ClearCodeCache(Heap* heap) {
void JSArray::EnsureSize(int required_size) {
ASSERT(HasFastElements());
ASSERT(HasFastTypeElements());
FixedArray* elts = FixedArray::cast(elements());
const int kArraySizeThatFitsComfortablyInNewSpace = 128;
if (elts->length() < required_size) {
......@@ -4432,9 +4524,12 @@ void JSArray::set_length(Smi* length) {
}
void JSArray::SetContent(FixedArray* storage) {
MaybeObject* JSArray::SetContent(FixedArray* storage) {
MaybeObject* maybe_object = EnsureCanContainElements(storage);
if (maybe_object->IsFailure()) return maybe_object;
set_length(Smi::FromInt(storage->length()));
set_elements(storage);
return this;
}
......
......@@ -82,6 +82,9 @@ void HeapObject::HeapObjectPrint(FILE* out) {
case HEAP_NUMBER_TYPE:
HeapNumber::cast(this)->HeapNumberPrint(out);
break;
case FIXED_DOUBLE_ARRAY_TYPE:
FixedDoubleArray::cast(this)->FixedDoubleArrayPrint(out);
break;
case FIXED_ARRAY_TYPE:
FixedArray::cast(this)->FixedArrayPrint(out);
break;
......@@ -242,6 +245,54 @@ void ExternalDoubleArray::ExternalDoubleArrayPrint(FILE* out) {
}
static void PrintElementsKind(FILE* out, ElementsKind kind) {
switch (kind) {
case FAST_SMI_ONLY_ELEMENTS:
PrintF(out, "FAST_SMI_ONLY_ELEMENTS");
break;
case FAST_ELEMENTS:
PrintF(out, "FAST_ELEMENTS");
break;
case FAST_DOUBLE_ELEMENTS:
PrintF(out, "FAST_DOUBLE_ELEMENTS");
break;
case DICTIONARY_ELEMENTS:
PrintF(out, "DICTIONARY_ELEMENTS");
break;
case NON_STRICT_ARGUMENTS_ELEMENTS:
PrintF(out, "NON_STRICT_ARGUMENTS_ELEMENTS");
break;
case EXTERNAL_BYTE_ELEMENTS:
PrintF(out, "EXTERNAL_BYTE_ELEMENTS");
break;
case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
PrintF(out, "EXTERNAL_UNSIGNED_BYTE_ELEMENTS");
break;
case EXTERNAL_SHORT_ELEMENTS:
PrintF(out, "EXTERNAL_SHORT_ELEMENTS");
break;
case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
PrintF(out, "EXTERNAL_UNSIGNED_SHORT_ELEMENTS");
break;
case EXTERNAL_INT_ELEMENTS:
PrintF(out, "EXTERNAL_INT_ELEMENTS");
break;
case EXTERNAL_UNSIGNED_INT_ELEMENTS:
PrintF(out, "EXTERNAL_UNSIGNED_INT_ELEMENTS");
break;
case EXTERNAL_FLOAT_ELEMENTS:
PrintF(out, "EXTERNAL_FLOAT_ELEMENTS");
break;
case EXTERNAL_DOUBLE_ELEMENTS:
PrintF(out, "EXTERNAL_DOUBLE_ELEMENTS");
break;
case EXTERNAL_PIXEL_ELEMENTS:
PrintF(out, "EXTERNAL_DOUBLE_ELEMENTS");
break;
}
}
void JSObject::PrintProperties(FILE* out) {
if (HasFastProperties()) {
DescriptorArray* descs = map()->instance_descriptors();
......@@ -265,16 +316,19 @@ void JSObject::PrintProperties(FILE* out) {
PrintF(out, " (callback)\n");
break;
case ELEMENTS_TRANSITION:
PrintF(out, " (elements transition)\n");
PrintF(out, "(elements transition to ");
PrintElementsKind(out,
Map::cast(descs->GetValue(i))->elements_kind());
PrintF(out, ")\n");
break;
case MAP_TRANSITION:
PrintF(out, " (map transition)\n");
PrintF(out, "(map transition)\n");
break;
case CONSTANT_TRANSITION:
PrintF(out, " (constant transition)\n");
PrintF(out, "(constant transition)\n");
break;
case NULL_DESCRIPTOR:
PrintF(out, " (null descriptor)\n");
PrintF(out, "(null descriptor)\n");
break;
default:
UNREACHABLE();
......@@ -288,7 +342,10 @@ void JSObject::PrintProperties(FILE* out) {
void JSObject::PrintElements(FILE* out) {
switch (GetElementsKind()) {
// Don't call GetElementsKind, its validation code can cause the printer to
// fail when debugging.
switch (map()->elements_kind()) {
case FAST_SMI_ONLY_ELEMENTS:
case FAST_ELEMENTS: {
// Print in array notation for non-sparse arrays.
FixedArray* p = FixedArray::cast(elements());
......@@ -396,8 +453,13 @@ void JSObject::PrintElements(FILE* out) {
void JSObject::JSObjectPrint(FILE* out) {
PrintF(out, "%p: [JSObject]\n", reinterpret_cast<void*>(this));
PrintF(out, " - map = %p\n", reinterpret_cast<void*>(map()));
PrintF(out, " - prototype = %p\n", reinterpret_cast<void*>(GetPrototype()));
PrintF(out, " - map = %p [", reinterpret_cast<void*>(map()));
// Don't call GetElementsKind, its validation code can cause the printer to
// fail when debugging.
PrintElementsKind(out, this->map()->elements_kind());
PrintF(out,
"]\n - prototype = %p\n",
reinterpret_cast<void*>(GetPrototype()));
PrintF(out, " {\n");
PrintProperties(out);
PrintElements(out);
......@@ -528,6 +590,16 @@ void FixedArray::FixedArrayPrint(FILE* out) {
}
void FixedDoubleArray::FixedDoubleArrayPrint(FILE* out) {
HeapObject::PrintHeader(out, "FixedDoubleArray");
PrintF(out, " - length: %d", length());
for (int i = 0; i < length(); i++) {
PrintF(out, "\n [%d]: %g", i, get_scalar(i));
}
PrintF(out, "\n");
}
void JSValue::JSValuePrint(FILE* out) {
HeapObject::PrintHeader(out, "ValueObject");
value()->Print(out);
......
This diff is collapsed.
......@@ -137,8 +137,13 @@ namespace v8 {
namespace internal {
enum ElementsKind {
// The "fast" kind for tagged values. Must be first to make it possible
// to efficiently check maps if they have fast elements.
// The "fast" kind for elements that only contain SMI values. Must be first
// to make it possible to efficiently check maps for this kind.
FAST_SMI_ONLY_ELEMENTS,
// The "fast" kind for tagged values. Must be second to make it possible to
// efficiently check maps for this and the FAST_SMI_ONLY_ELEMENTS kind
// together at once.
FAST_ELEMENTS,
// The "fast" kind for unwrapped, non-tagged double values.
......@@ -161,7 +166,7 @@ enum ElementsKind {
// Derived constants from ElementsKind
FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND = EXTERNAL_BYTE_ELEMENTS,
LAST_EXTERNAL_ARRAY_ELEMENTS_KIND = EXTERNAL_PIXEL_ELEMENTS,
FIRST_ELEMENTS_KIND = FAST_ELEMENTS,
FIRST_ELEMENTS_KIND = FAST_SMI_ONLY_ELEMENTS,
LAST_ELEMENTS_KIND = EXTERNAL_PIXEL_ELEMENTS
};
......@@ -1432,8 +1437,14 @@ class JSObject: public JSReceiver {
MUST_USE_RESULT inline MaybeObject* ResetElements();
inline ElementsKind GetElementsKind();
inline ElementsAccessor* GetElementsAccessor();
inline bool HasFastSmiOnlyElements();
inline bool HasFastElements();
// Returns if an object has either FAST_ELEMENT or FAST_SMI_ONLY_ELEMENT
// elements. TODO(danno): Rename HasFastTypeElements to HasFastElements() and
// HasFastElements to HasFastObjectElements.
inline bool HasFastTypeElements();
inline bool HasFastDoubleElements();
inline bool HasNonStrictArgumentsElements();
inline bool HasDictionaryElements();
inline bool HasExternalPixelElements();
inline bool HasExternalArrayElements();
......@@ -1608,6 +1619,19 @@ class JSObject: public JSReceiver {
// Tests for the fast common case for property enumeration.
bool IsSimpleEnum();
inline void ValidateSmiOnlyElements();
// Makes sure that this object can contain non-smi Object as elements.
inline MaybeObject* EnsureCanContainNonSmiElements();
// Makes sure that this object can contain the specified elements.
inline MaybeObject* EnsureCanContainElements(Object** elements,
uint32_t count);
inline MaybeObject* EnsureCanContainElements(FixedArray* elements);
MaybeObject* EnsureCanContainElements(Arguments* arguments,
uint32_t first_arg,
uint32_t arg_count);
// Do we want to keep the elements in fast case when increasing the
// capacity?
bool ShouldConvertToSlowElements(int new_capacity);
......@@ -1656,6 +1680,7 @@ class JSObject: public JSReceiver {
Object* value,
StrictModeFlag strict_mode,
bool check_prototype);
MUST_USE_RESULT MaybeObject* SetDictionaryElement(uint32_t index,
Object* value,
StrictModeFlag strict_mode,
......@@ -1678,11 +1703,18 @@ class JSObject: public JSReceiver {
// The undefined object if index is out of bounds.
MaybeObject* GetElementWithInterceptor(Object* receiver, uint32_t index);
enum SetFastElementsCapacityMode {
kAllowSmiOnlyElements,
kDontAllowSmiOnlyElements
};
// Replace the elements' backing store with fast elements of the given
// capacity. Update the length for JSArrays. Returns the new backing
// store.
MUST_USE_RESULT MaybeObject* SetFastElementsCapacityAndLength(int capacity,
int length);
MUST_USE_RESULT MaybeObject* SetFastElementsCapacityAndLength(
int capacity,
int length,
SetFastElementsCapacityMode set_capacity_mode);
MUST_USE_RESULT MaybeObject* SetFastDoubleElementsCapacityAndLength(
int capacity,
int length);
......@@ -3980,8 +4012,12 @@ class Map: public HeapObject {
(bit_field2() & kElementsKindMask) >> kElementsKindShift);
}
// Tells whether the instance has fast elements that are only Smis.
inline bool has_fast_smi_only_elements() {
return elements_kind() == FAST_SMI_ONLY_ELEMENTS;
}
// Tells whether the instance has fast elements.
// Equivalent to instance->GetElementsKind() == FAST_ELEMENTS.
inline bool has_fast_elements() {
return elements_kind() == FAST_ELEMENTS;
}
......@@ -3990,6 +4026,10 @@ class Map: public HeapObject {
return elements_kind() == FAST_DOUBLE_ELEMENTS;
}
inline bool has_non_strict_arguments_elements() {
return elements_kind() == NON_STRICT_ARGUMENTS_ELEMENTS;
}
inline bool has_external_array_elements() {
ElementsKind kind(elements_kind());
return kind >= FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND &&
......@@ -4237,7 +4277,7 @@ class Map: public HeapObject {
static const int kStringWrapperSafeForDefaultValueOf = 2;
static const int kAttachedToSharedFunctionInfo = 3;
// No bits can be used after kElementsKindFirstBit, they are all reserved for
// storing ElementKind. for anything other than storing the ElementKind.
// storing ElementKind.
static const int kElementsKindShift = 4;
static const int kElementsKindBitCount = 4;
......@@ -4246,6 +4286,9 @@ class Map: public HeapObject {
((1 << (kElementsKindShift + kElementsKindBitCount)) - 1);
static const int8_t kMaximumBitField2FastElementValue = static_cast<int8_t>(
(FAST_ELEMENTS + 1) << Map::kElementsKindShift) - 1;
static const int8_t kMaximumBitField2FastSmiOnlyElementValue =
static_cast<int8_t>((FAST_SMI_ONLY_ELEMENTS + 1) <<
Map::kElementsKindShift) - 1;
// Bit positions for bit field 3
static const int kIsShared = 0;
......@@ -6861,7 +6904,7 @@ class JSArray: public JSObject {
MUST_USE_RESULT MaybeObject* Initialize(int capacity);
// Set the content of the array to the content of storage.
inline void SetContent(FixedArray* storage);
inline MaybeObject* SetContent(FixedArray* storage);
// Casting.
static inline JSArray* cast(Object* obj);
......
This diff is collapsed.
......@@ -330,6 +330,8 @@ namespace internal {
F(InitializeConstContextSlot, 3, 1) \
F(OptimizeObjectForAddingMultipleProperties, 2, 1) \
\
/* Arrays */ \
F(NonSmiElementStored, 1, 1) \
/* Debugging */ \
F(DebugPrint, 1, 1) \
F(DebugTrace, 0, 1) \
......@@ -354,6 +356,7 @@ namespace internal {
F(IS_VAR, 1, 1) \
\
/* expose boolean functions from objects-inl.h */ \
F(HasFastSmiOnlyElements, 1, 1) \
F(HasFastElements, 1, 1) \
F(HasFastDoubleElements, 1, 1) \
F(HasDictionaryElements, 1, 1) \
......
......@@ -704,7 +704,8 @@ class KeyedStoreStubCompiler: public StubCompiler {
CodeList* handler_ics);
static void GenerateStoreFastElement(MacroAssembler* masm,
bool is_js_array);
bool is_js_array,
ElementsKind element_kind);
static void GenerateStoreFastDoubleElement(MacroAssembler* masm,
bool is_js_array);
......
......@@ -2396,6 +2396,7 @@ void LCodeGen::DoLoadKeyedSpecializedArrayElement(
case EXTERNAL_FLOAT_ELEMENTS:
case EXTERNAL_DOUBLE_ELEMENTS:
case FAST_ELEMENTS:
case FAST_SMI_ONLY_ELEMENTS:
case FAST_DOUBLE_ELEMENTS:
case DICTIONARY_ELEMENTS:
case NON_STRICT_ARGUMENTS_ELEMENTS:
......@@ -3121,6 +3122,7 @@ void LCodeGen::DoStoreKeyedSpecializedArrayElement(
case EXTERNAL_FLOAT_ELEMENTS:
case EXTERNAL_DOUBLE_ELEMENTS:
case FAST_ELEMENTS:
case FAST_SMI_ONLY_ELEMENTS:
case FAST_DOUBLE_ELEMENTS:
case DICTIONARY_ELEMENTS:
case NON_STRICT_ARGUMENTS_ELEMENTS:
......
......@@ -2648,7 +2648,8 @@ void MacroAssembler::CmpInstanceType(Register map, InstanceType type) {
void MacroAssembler::CheckFastElements(Register map,
Label* fail,
Label::Distance distance) {
STATIC_ASSERT(FAST_ELEMENTS == 0);
STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0);
STATIC_ASSERT(FAST_ELEMENTS == 1);
cmpb(FieldOperand(map, Map::kBitField2Offset),
Immediate(Map::kMaximumBitField2FastElementValue));
j(above, fail, distance);
......
......@@ -3464,6 +3464,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray(
__ movsd(Operand(rbx, rdi, times_8, 0), xmm0);
break;
case FAST_ELEMENTS:
case FAST_SMI_ONLY_ELEMENTS:
case FAST_DOUBLE_ELEMENTS:
case DICTIONARY_ELEMENTS:
case NON_STRICT_ARGUMENTS_ELEMENTS:
......@@ -3531,6 +3532,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray(
case EXTERNAL_FLOAT_ELEMENTS:
case EXTERNAL_DOUBLE_ELEMENTS:
case FAST_ELEMENTS:
case FAST_SMI_ONLY_ELEMENTS:
case FAST_DOUBLE_ELEMENTS:
case DICTIONARY_ELEMENTS:
case NON_STRICT_ARGUMENTS_ELEMENTS:
......@@ -3662,8 +3664,10 @@ void KeyedLoadStubCompiler::GenerateLoadFastDoubleElement(
}
void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm,
bool is_js_array) {
void KeyedStoreStubCompiler::GenerateStoreFastElement(
MacroAssembler* masm,
bool is_js_array,
ElementsKind elements_kind) {
// ----------- S t a t e -------------
// -- rax : value
// -- rcx : key
......@@ -3700,6 +3704,7 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm,
__ movq(Operand(rcx, 0), rax);
// Make sure to preserve the value in register rax.
__ movq(rdx, rax);
ASSERT(elements_kind == FAST_ELEMENTS);
__ RecordWrite(rdi, rcx, rdx, kDontSaveFPRegs);
// Done.
......
......@@ -672,7 +672,8 @@ TEST(JSArray) {
// Set array length to 0.
ok = array->SetElementsLength(Smi::FromInt(0))->ToObjectChecked();
CHECK_EQ(Smi::FromInt(0), array->length());
CHECK(array->HasFastElements()); // Must be in fast mode.
// Must be in fast mode.
CHECK(array->HasFastTypeElements());
// array[length] = name.
ok = array->SetElement(0, *name, kNonStrictMode, true)->ToObjectChecked();
......
......@@ -28,7 +28,10 @@
// Flags: --allow-natives-syntax
// Test element kind of objects
support_smi_only_arrays = %HasFastSmiOnlyElements([]);
var element_kind = {
fast_smi_only_elements : 0,
fast_elements : 1,
fast_double_elements : 2,
dictionary_elements : 3,
......@@ -45,8 +48,16 @@ var element_kind = {
// We expect an object to only be of one element kind.
function assertKind(expected, obj){
assertEquals(expected == element_kind.fast_elements,
%HasFastElements(obj));
if (support_smi_only_arrays) {
assertEquals(expected == element_kind.fast_smi_only_elements,
%HasFastSmiOnlyElements(obj));
assertEquals(expected == element_kind.fast_elements,
%HasFastElements(obj));
} else {
assertEquals(expected == element_kind.fast_elements ||
expected == element_kind.fast_smi_only_elements,
%HasFastElements(obj));
}
assertEquals(expected == element_kind.fast_double_elements,
%HasFastDoubleElements(obj));
assertEquals(expected == element_kind.dictionary_elements,
......@@ -80,9 +91,22 @@ me.dance = 0xD15C0;
me.drink = 0xC0C0A;
assertKind(element_kind.fast_elements, me);
var too = [1,2,3];
assertKind(element_kind.fast_smi_only_elements, too);
too.dance = 0xD15C0;
too.drink = 0xC0C0A;
assertKind(element_kind.fast_smi_only_elements, too);
// Make sure the element kind transitions from smionly when a non-smi is stored.
var you = new Array();
assertKind(element_kind.fast_smi_only_elements, you);
for(i = 0; i < 1337; i++) {
you[i] = i;
var val = i;
if (i == 1336) {
assertKind(element_kind.fast_smi_only_elements, you);
val = new Object();
}
you[i] = val;
}
assertKind(element_kind.fast_elements, you);
......
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