}
+const Register GrowArrayElementsDescriptor::ObjectRegister() { return r0; }
+const Register GrowArrayElementsDescriptor::KeyRegister() { return r3; }
+const Register GrowArrayElementsDescriptor::CapacityRegister() { return r2; }
+
+
void FastNewClosureDescriptor::Initialize(CallInterfaceDescriptorData* data) {
Register registers[] = {cp, r2};
data->Initialize(arraysize(registers), registers, NULL);
generator.BeforeCall(__ CallSize(code, RelocInfo::CODE_TARGET));
PlatformInterfaceDescriptor* call_descriptor =
instr->descriptor().platform_specific_descriptor();
- __ Call(code, RelocInfo::CODE_TARGET, TypeFeedbackId::None(), al,
- call_descriptor->storage_mode());
+ if (call_descriptor != NULL) {
+ __ Call(code, RelocInfo::CODE_TARGET, TypeFeedbackId::None(), al,
+ call_descriptor->storage_mode());
+ } else {
+ __ Call(code, RelocInfo::CODE_TARGET, TypeFeedbackId::None(), al);
+ }
} else {
DCHECK(instr->target()->IsRegister());
Register target = ToRegister(instr->target());
const Register MathPowIntegerDescriptor::exponent() { return x12; }
+const Register GrowArrayElementsDescriptor::ObjectRegister() { return x0; }
+const Register GrowArrayElementsDescriptor::KeyRegister() { return x3; }
+const Register GrowArrayElementsDescriptor::CapacityRegister() { return x2; }
+
+
void FastNewClosureDescriptor::Initialize(CallInterfaceDescriptorData* data) {
// cp: context
// x2: function info
}
+template <>
+HValue* CodeStubGraphBuilder<GrowArrayElementsStub>::BuildCodeStub() {
+ HValue* object = GetParameter(GrowArrayElementsDescriptor::kObjectIndex);
+ HValue* key = GetParameter(GrowArrayElementsDescriptor::kKeyIndex);
+ HValue* current_capacity =
+ GetParameter(GrowArrayElementsDescriptor::kCapacityIndex);
+ ElementsKind kind = casted_stub()->elements_kind();
+
+ HValue* elements = AddLoadElements(object);
+ HValue* length =
+ casted_stub()->is_js_array()
+ ? Add<HLoadNamedField>(object, static_cast<HValue*>(NULL),
+ HObjectAccess::ForArrayLength(kind))
+ : current_capacity;
+
+ return BuildCheckAndGrowElementsCapacity(object, elements, kind, length,
+ current_capacity, key);
+}
+
+
+Handle<Code> GrowArrayElementsStub::GenerateCode() {
+ return DoGenerateCode(this);
+}
+
+
template <>
HValue* CodeStubGraphBuilder<LoadFastElementStub>::BuildCodeStub() {
LoadKeyedHoleMode hole_mode = casted_stub()->convert_hole_to_undefined()
}
+void GrowArrayElementsStub::InitializeDescriptor(
+ CodeStubDescriptor* descriptor) {
+ descriptor->Initialize(
+ Runtime::FunctionForId(Runtime::kGrowArrayElements)->entry);
+}
+
+
void CreateAllocationSiteStub::GenerateAheadOfTime(Isolate* isolate) {
CreateAllocationSiteStub stub(isolate);
stub.GetCode();
V(FastCloneShallowObject) \
V(FastNewClosure) \
V(FastNewContext) \
+ V(GrowArrayElements) \
V(InternalArrayNArgumentsConstructor) \
V(InternalArrayNoArgumentConstructor) \
V(InternalArraySingleArgumentConstructor) \
};
+class GrowArrayElementsStub : public HydrogenCodeStub {
+ public:
+ GrowArrayElementsStub(Isolate* isolate, bool is_js_array, ElementsKind kind)
+ : HydrogenCodeStub(isolate) {
+ set_sub_minor_key(ElementsKindBits::encode(kind) |
+ IsJsArrayBits::encode(is_js_array));
+ }
+
+ ElementsKind elements_kind() const {
+ return ElementsKindBits::decode(sub_minor_key());
+ }
+
+ bool is_js_array() const { return IsJsArrayBits::decode(sub_minor_key()); }
+
+ private:
+ class ElementsKindBits : public BitField<ElementsKind, 0, 8> {};
+ class IsJsArrayBits : public BitField<bool, ElementsKindBits::kNext, 1> {};
+
+ DEFINE_CALL_INTERFACE_DESCRIPTOR(GrowArrayElements);
+ DEFINE_HYDROGEN_CODE_STUB(GrowArrayElements, HydrogenCodeStub);
+};
+
class InstanceofStub: public PlatformCodeStub {
public:
enum Flags {
}
+HValue* HGraphBuilder::BuildCheckAndGrowElementsCapacity(
+ HValue* object, HValue* elements, ElementsKind kind, HValue* length,
+ HValue* capacity, HValue* key) {
+ HValue* max_gap = Add<HConstant>(static_cast<int32_t>(JSObject::kMaxGap));
+ HValue* max_capacity = AddUncasted<HAdd>(capacity, max_gap);
+ Add<HBoundsCheck>(key, max_capacity);
+
+ HValue* new_capacity = BuildNewElementsCapacity(key);
+ HValue* new_elements = BuildGrowElementsCapacity(object, elements, kind, kind,
+ length, new_capacity);
+ return new_elements;
+}
+
+
HValue* HGraphBuilder::BuildCheckForCapacityGrow(
HValue* object,
HValue* elements,
Token::GTE);
capacity_checker.Then();
- HValue* max_gap = Add<HConstant>(static_cast<int32_t>(JSObject::kMaxGap));
- HValue* max_capacity = AddUncasted<HAdd>(current_capacity, max_gap);
-
- Add<HBoundsCheck>(key, max_capacity);
-
- HValue* new_capacity = BuildNewElementsCapacity(key);
- HValue* new_elements = BuildGrowElementsCapacity(object, elements,
- kind, kind, length,
- new_capacity);
+ // BuildCheckAndGrowElementsCapacity could de-opt without profitable feedback
+ // therefore we defer calling it to a stub in optimized functions. It is
+ // okay to call directly in a code stub though, because a bailout to the
+ // runtime is tolerable in the corner cases.
+ if (top_info()->IsStub()) {
+ HValue* new_elements = BuildCheckAndGrowElementsCapacity(
+ object, elements, kind, length, current_capacity, key);
+ environment()->Push(new_elements);
+ } else {
+ GrowArrayElementsStub stub(isolate(), is_js_array, kind);
+ GrowArrayElementsDescriptor descriptor(isolate());
+ HConstant* target = Add<HConstant>(stub.GetCode());
+ HValue* op_vals[] = {context(), object, key, current_capacity};
+ HValue* new_elements = Add<HCallWithDescriptor>(
+ target, 0, descriptor, Vector<HValue*>(op_vals, 4));
+ // If the object changed to a dictionary, GrowArrayElements will return a
+ // smi to signal that deopt is required.
+ Add<HCheckHeapObject>(new_elements);
+ environment()->Push(new_elements);
+ }
- environment()->Push(new_elements);
capacity_checker.Else();
environment()->Push(elements);
bool is_js_array,
PropertyAccessType access_type);
+ HValue* BuildCheckAndGrowElementsCapacity(HValue* object, HValue* elements,
+ ElementsKind kind, HValue* length,
+ HValue* capacity, HValue* key);
+
HValue* BuildCopyElementsOnWrite(HValue* object,
HValue* elements,
ElementsKind kind,
}
+const Register GrowArrayElementsDescriptor::ObjectRegister() { return eax; }
+const Register GrowArrayElementsDescriptor::KeyRegister() { return ebx; }
+const Register GrowArrayElementsDescriptor::CapacityRegister() { return ecx; }
+
+
void FastNewClosureDescriptor::Initialize(CallInterfaceDescriptorData* data) {
Register registers[] = {esi, ebx};
data->Initialize(arraysize(registers), registers, NULL);
data->Initialize(arraysize(registers), registers, NULL);
}
+
+void GrowArrayElementsDescriptor::Initialize(
+ CallInterfaceDescriptorData* data) {
+ Register registers[] = {ContextRegister(), ObjectRegister(), KeyRegister(),
+ CapacityRegister()};
+ data->Initialize(arraysize(registers), registers, NULL);
+}
}
} // namespace v8::internal
V(StoreArrayLiteralElement) \
V(MathPowTagged) \
V(MathPowInteger) \
- V(ContextOnly)
+ V(ContextOnly) \
+ V(GrowArrayElements)
class CallInterfaceDescriptorData {
DECLARE_DESCRIPTOR(ContextOnlyDescriptor, CallInterfaceDescriptor)
};
+
+class GrowArrayElementsDescriptor : public CallInterfaceDescriptor {
+ public:
+ DECLARE_DESCRIPTOR(GrowArrayElementsDescriptor, CallInterfaceDescriptor)
+
+ enum RegisterInfo { kObjectIndex, kKeyIndex, kCapacityIndex };
+ static const Register ObjectRegister();
+ static const Register KeyRegister();
+ static const Register CapacityRegister();
+};
+
#undef DECLARE_DESCRIPTOR
}
+const Register GrowArrayElementsDescriptor::ObjectRegister() { return a0; }
+const Register GrowArrayElementsDescriptor::KeyRegister() { return a3; }
+const Register GrowArrayElementsDescriptor::CapacityRegister() { return a2; }
+
+
void FastNewClosureDescriptor::Initialize(CallInterfaceDescriptorData* data) {
Register registers[] = {cp, a2};
data->Initialize(arraysize(registers), registers, NULL);
}
+const Register GrowArrayElementsDescriptor::ObjectRegister() { return a0; }
+const Register GrowArrayElementsDescriptor::KeyRegister() { return a3; }
+const Register GrowArrayElementsDescriptor::CapacityRegister() { return a2; }
+
+
void FastNewClosureDescriptor::Initialize(CallInterfaceDescriptorData* data) {
Register registers[] = {cp, a2};
data->Initialize(arraysize(registers), registers, NULL);
}
+bool JSObject::WouldConvertToSlowElements(Handle<Object> key) {
+ uint32_t index;
+ return key->ToArrayIndex(&index) && WouldConvertToSlowElements(index);
+}
+
+
void JSObject::SetMapAndElements(Handle<JSObject> object,
Handle<Map> new_map,
Handle<FixedArrayBase> value) {
#endif // ENABLE_DISASSEMBLER
-Handle<FixedArray> JSObject::SetFastElementsCapacityAndLength(
- Handle<JSObject> object,
- int capacity,
- int length,
+Handle<FixedArray> JSObject::SetFastElementsCapacity(
+ Handle<JSObject> object, int capacity,
SetFastElementsCapacitySmiMode smi_mode) {
// We should never end in here with a pixel or external array.
DCHECK(!object->HasExternalArrayElements());
object->GetElementsKind(), new_elements);
}
+ return new_elements;
+}
+
+
+Handle<FixedArray> JSObject::SetFastElementsCapacityAndLength(
+ Handle<JSObject> object, int capacity, int length,
+ SetFastElementsCapacitySmiMode smi_mode) {
+ Handle<FixedArray> new_elements =
+ SetFastElementsCapacity(object, capacity, smi_mode);
if (object->IsJSArray()) {
Handle<JSArray>::cast(object)->set_length(Smi::FromInt(length));
}
}
-void JSObject::SetFastDoubleElementsCapacityAndLength(Handle<JSObject> object,
- int capacity,
- int length) {
+Handle<FixedArrayBase> JSObject::SetFastDoubleElementsCapacity(
+ Handle<JSObject> object, int capacity) {
// We should never end in here with a pixel or external array.
DCHECK(!object->HasExternalArrayElements());
object->GetElementsKind(), elems);
}
+ return elems;
+}
+
+
+Handle<FixedArrayBase> JSObject::SetFastDoubleElementsCapacityAndLength(
+ Handle<JSObject> object, int capacity, int length) {
+ Handle<FixedArrayBase> new_elements =
+ SetFastDoubleElementsCapacity(object, capacity);
if (object->IsJSArray()) {
Handle<JSArray>::cast(object)->set_length(Smi::FromInt(length));
}
+ return new_elements;
}
}
-bool JSObject::WouldConvertToSlowElements(Handle<Object> key) {
- uint32_t index;
- if (HasFastElements() && key->ToArrayIndex(&index)) {
+bool JSObject::WouldConvertToSlowElements(uint32_t index) {
+ if (HasFastElements()) {
Handle<FixedArrayBase> backing_store(FixedArrayBase::cast(elements()));
uint32_t capacity = static_cast<uint32_t>(backing_store->length());
if (index >= capacity) {
// Would we convert a fast elements array to dictionary mode given
// an access at key?
- bool WouldConvertToSlowElements(Handle<Object> key);
+ bool WouldConvertToSlowElements(uint32_t index);
+ inline bool WouldConvertToSlowElements(Handle<Object> key);
// Do we want to keep the elements in fast case when increasing the
// capacity?
bool ShouldConvertToSlowElements(int new_capacity);
kDontAllowSmiElements
};
+ static Handle<FixedArray> SetFastElementsCapacity(
+ Handle<JSObject> object, int capacity,
+ SetFastElementsCapacitySmiMode smi_mode);
+ static Handle<FixedArrayBase> SetFastDoubleElementsCapacity(
+ Handle<JSObject> object, int capacity);
+
// Replace the elements' backing store with fast elements of the given
// capacity. Update the length for JSArrays. Returns the new backing
// store.
int capacity,
int length,
SetFastElementsCapacitySmiMode smi_mode);
- static void SetFastDoubleElementsCapacityAndLength(
- Handle<JSObject> object,
- int capacity,
- int length);
+ static Handle<FixedArrayBase> SetFastDoubleElementsCapacityAndLength(
+ Handle<JSObject> object, int capacity, int length);
// Lookup interceptors are used for handling properties controlled by host
// objects.
}
+// GrowArrayElements returns a sentinel Smi if the object was normalized.
+RUNTIME_FUNCTION(Runtime_GrowArrayElements) {
+ HandleScope scope(isolate);
+ DCHECK(args.length() == 3);
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0);
+ CONVERT_SMI_ARG_CHECKED(key, 1);
+
+ if (key < 0) {
+ return object->elements();
+ }
+
+ uint32_t capacity = static_cast<uint32_t>(object->elements()->length());
+ uint32_t index = static_cast<uint32_t>(key);
+
+ if (index >= capacity) {
+ if (object->WouldConvertToSlowElements(index)) {
+ JSObject::NormalizeElements(object);
+ return Smi::FromInt(0);
+ }
+
+ uint32_t new_capacity = JSObject::NewElementsCapacity(index + 1);
+ ElementsKind kind = object->GetElementsKind();
+ if (IsFastDoubleElementsKind(kind)) {
+ JSObject::SetFastDoubleElementsCapacity(object, new_capacity);
+ } else {
+ JSObject::SetFastElementsCapacitySmiMode set_capacity_mode =
+ object->HasFastSmiElements() ? JSObject::kAllowSmiElements
+ : JSObject::kDontAllowSmiElements;
+ JSObject::SetFastElementsCapacity(object, new_capacity,
+ set_capacity_mode);
+ }
+ }
+
+ // On success, return the fixed array elements.
+ return object->elements();
+}
+
+
RUNTIME_FUNCTION(Runtime_HasComplexElements) {
HandleScope scope(isolate);
DCHECK(args.length() == 1);
F(ArrayConstructorWithSubclassing, -1, 1) \
F(InternalArrayConstructor, -1, 1) \
F(NormalizeElements, 1, 1) \
+ F(GrowArrayElements, 3, 1) \
F(HasComplexElements, 1, 1) \
F(ForInCacheArrayLength, 2, 1) /* TODO(turbofan): Only temporary */ \
F(IsArray, 1, 1) \
}
+const Register GrowArrayElementsDescriptor::ObjectRegister() { return rax; }
+const Register GrowArrayElementsDescriptor::KeyRegister() { return rbx; }
+const Register GrowArrayElementsDescriptor::CapacityRegister() { return rcx; }
+
+
void FastNewClosureDescriptor::Initialize(CallInterfaceDescriptorData* data) {
Register registers[] = {rsi, rbx};
data->Initialize(arraysize(registers), registers, NULL);
--- /dev/null
+// Copyright 2015 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 --noverify-heap --noenable-slow-asserts
+
+ // --noverify-heap and --noenable-slow-asserts are set because the test is too
+ // slow with it on.
+
+ // Ensure that keyed stores work, and optimized functions learn if the
+ // store required change to dictionary mode. Verify that stores that grow
+ // the array into large object space don't cause a deopt.
+(function() {
+ var a = [];
+
+ function foo(a, i) {
+ a[i] = 5.3;
+ }
+
+ foo(a, 1);
+ foo(a, 2);
+ foo(a, 3);
+ %OptimizeFunctionOnNextCall(foo);
+ a[3] = 0;
+ foo(a, 3);
+ assertEquals(a[3], 5.3);
+ foo(a, 50000);
+ assertUnoptimized(foo);
+ assertTrue(%HasDictionaryElements(a));
+
+ var b = [];
+ foo(b, 1);
+ foo(b, 2);
+ // Put b in dictionary mode.
+ b[10000] = 5;
+ assertTrue(%HasDictionaryElements(b));
+ foo(b, 3);
+ %OptimizeFunctionOnNextCall(foo);
+ foo(b, 50000);
+ assertOptimized(foo);
+ assertTrue(%HasDictionaryElements(b));
+
+ // Clearing feedback for the StoreIC in foo is important for runs with
+ // flag --stress-opt.
+ %ClearFunctionTypeFeedback(foo);
+})();
+
+
+(function() {
+ var a = new Array(10);
+
+ function foo2(a, i) {
+ a[i] = 50;
+ }
+
+ // The KeyedStoreIC will learn GROW_MODE.
+ foo2(a, 10);
+ foo2(a, 12);
+ foo2(a, 31);
+ %OptimizeFunctionOnNextCall(foo2);
+ foo2(a, 40);
+
+ // This test is way too slow without crankshaft.
+ if (4 != %GetOptimizationStatus(foo2)) {
+ assertOptimized(foo2);
+ assertTrue(%HasFastSmiElements(a));
+
+ // Grow a large array into large object space through the keyed store
+ // without deoptimizing. Grow by 10s. If we set elements too sparsely, the
+ // array will convert to dictionary mode.
+ a = new Array(99999);
+ assertTrue(%HasFastSmiElements(a));
+ for (var i = 0; i < 263000; i += 10) {
+ foo2(a, i);
+ }
+
+ // Verify that we are over 1 page in size, and foo2 remains optimized.
+ // This means we've smoothly transitioned to allocating in large object
+ // space.
+ assertTrue(%HasFastSmiElements(a));
+ assertTrue(a.length * 4 > (1024 * 1024));
+ assertOptimized(foo2);
+ }
+
+ %ClearFunctionTypeFeedback(foo2);
+})();