Use a stub in crankshaft for grow store arrays.
We were deopting without learning anything. BUG=v8:3417 LOG=N R=danno@chromium.org Review URL: https://codereview.chromium.org/368263003 Cr-Commit-Position: refs/heads/master@{#25392}
This commit is contained in:
parent
ab4d2b11c8
commit
d40204f84c
@ -54,6 +54,12 @@ const Register MathPowIntegerDescriptor::exponent() {
|
||||
}
|
||||
|
||||
|
||||
// IC register specifications
|
||||
const Register GrowArrayElementsDescriptor::ObjectRegister() { return r0; }
|
||||
const Register GrowArrayElementsDescriptor::KeyRegister() { return r1; }
|
||||
const Register GrowArrayElementsDescriptor::CapacityRegister() { return r2; }
|
||||
|
||||
|
||||
void FastNewClosureDescriptor::Initialize(CallInterfaceDescriptorData* data) {
|
||||
Register registers[] = {cp, r2};
|
||||
data->Initialize(arraysize(registers), registers, NULL);
|
||||
|
@ -4015,8 +4015,12 @@ void LCodeGen::DoCallWithDescriptor(LCallWithDescriptor* instr) {
|
||||
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());
|
||||
|
@ -60,6 +60,12 @@ const Register MathPowTaggedDescriptor::exponent() { return x11; }
|
||||
const Register MathPowIntegerDescriptor::exponent() { return x12; }
|
||||
|
||||
|
||||
// IC register specifications
|
||||
const Register GrowArrayElementsDescriptor::ObjectRegister() { return x0; }
|
||||
const Register GrowArrayElementsDescriptor::KeyRegister() { return x1; }
|
||||
const Register GrowArrayElementsDescriptor::CapacityRegister() { return x2; }
|
||||
|
||||
|
||||
void FastNewClosureDescriptor::Initialize(CallInterfaceDescriptorData* data) {
|
||||
// cp: context
|
||||
// x2: function info
|
||||
|
@ -547,6 +547,31 @@ Handle<Code> StoreScriptContextFieldStub::GenerateCode() {
|
||||
}
|
||||
|
||||
|
||||
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() {
|
||||
HInstruction* load = BuildUncheckedMonomorphicElementAccess(
|
||||
|
@ -733,6 +733,13 @@ void StringAddStub::InitializeDescriptor(CodeStubDescriptor* descriptor) {
|
||||
}
|
||||
|
||||
|
||||
void GrowArrayElementsStub::InitializeDescriptor(
|
||||
CodeStubDescriptor* descriptor) {
|
||||
descriptor->Initialize(
|
||||
Runtime::FunctionForId(Runtime::kGrowArrayElements)->entry);
|
||||
}
|
||||
|
||||
|
||||
void CreateAllocationSiteStub::GenerateAheadOfTime(Isolate* isolate) {
|
||||
CreateAllocationSiteStub stub(isolate);
|
||||
stub.GetCode();
|
||||
|
@ -65,6 +65,7 @@ namespace internal {
|
||||
V(FastCloneShallowObject) \
|
||||
V(FastNewClosure) \
|
||||
V(FastNewContext) \
|
||||
V(GrowArrayElements) \
|
||||
V(InternalArrayNArgumentsConstructor) \
|
||||
V(InternalArrayNoArgumentConstructor) \
|
||||
V(InternalArraySingleArgumentConstructor) \
|
||||
@ -662,6 +663,29 @@ class CreateAllocationSiteStub : public HydrogenCodeStub {
|
||||
};
|
||||
|
||||
|
||||
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 {
|
||||
|
@ -783,6 +783,9 @@ void HInstruction::Verify() {
|
||||
|
||||
bool HInstruction::CanDeoptimize() {
|
||||
// TODO(titzer): make this a virtual method?
|
||||
// TODO(all): Some of these may be incorrect, since any method that can
|
||||
// collect can provoke lazy deoptimization. Methods like CallNew can
|
||||
// certainly do that.
|
||||
switch (opcode()) {
|
||||
case HValue::kAbnormalExit:
|
||||
case HValue::kAccessArgumentsAt:
|
||||
@ -796,7 +799,6 @@ bool HInstruction::CanDeoptimize() {
|
||||
case HValue::kCallNew:
|
||||
case HValue::kCallNewArray:
|
||||
case HValue::kCallStub:
|
||||
case HValue::kCallWithDescriptor:
|
||||
case HValue::kCapturedObject:
|
||||
case HValue::kClassOfTestAndBranch:
|
||||
case HValue::kCompareGeneric:
|
||||
@ -863,6 +865,7 @@ bool HInstruction::CanDeoptimize() {
|
||||
case HValue::kBranch:
|
||||
case HValue::kCallJSFunction:
|
||||
case HValue::kCallRuntime:
|
||||
case HValue::kCallWithDescriptor:
|
||||
case HValue::kChange:
|
||||
case HValue::kCheckHeapObject:
|
||||
case HValue::kCheckInstanceType:
|
||||
|
@ -1297,6 +1297,20 @@ HValue* HGraphBuilder::BuildWrapReceiver(HValue* object, HValue* function) {
|
||||
}
|
||||
|
||||
|
||||
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,
|
||||
@ -1320,17 +1334,26 @@ HValue* HGraphBuilder::BuildCheckForCapacityGrow(
|
||||
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);
|
||||
// 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()) {
|
||||
environment()->Push(BuildCheckAndGrowElementsCapacity(
|
||||
object, elements, kind, length, current_capacity, key));
|
||||
} 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);
|
||||
}
|
||||
|
||||
Add<HBoundsCheck>(key, max_capacity);
|
||||
|
||||
HValue* new_capacity = BuildNewElementsCapacity(key);
|
||||
HValue* new_elements = BuildGrowElementsCapacity(object, elements,
|
||||
kind, kind, length,
|
||||
new_capacity);
|
||||
|
||||
environment()->Push(new_elements);
|
||||
capacity_checker.Else();
|
||||
|
||||
environment()->Push(elements);
|
||||
|
@ -1335,6 +1335,10 @@ class HGraphBuilder {
|
||||
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,
|
||||
|
@ -56,6 +56,11 @@ const Register MathPowIntegerDescriptor::exponent() {
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
|
@ -146,5 +146,12 @@ void ContextOnlyDescriptor::Initialize(CallInterfaceDescriptorData* data) {
|
||||
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
|
||||
|
@ -53,7 +53,8 @@ class PlatformInterfaceDescriptor;
|
||||
V(StoreArrayLiteralElement) \
|
||||
V(MathPowTagged) \
|
||||
V(MathPowInteger) \
|
||||
V(ContextOnly)
|
||||
V(ContextOnly) \
|
||||
V(GrowArrayElements)
|
||||
|
||||
|
||||
class CallInterfaceDescriptorData {
|
||||
@ -488,6 +489,17 @@ class ContextOnlyDescriptor : public CallInterfaceDescriptor {
|
||||
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
|
||||
|
||||
|
||||
|
@ -1827,6 +1827,12 @@ void JSObject::EnsureCanContainElements(Handle<JSObject> object,
|
||||
}
|
||||
|
||||
|
||||
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) {
|
||||
|
@ -11267,10 +11267,8 @@ void Code::Disassemble(const char* name, std::ostream& os) { // NOLINT
|
||||
#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());
|
||||
@ -11322,6 +11320,15 @@ Handle<FixedArray> JSObject::SetFastElementsCapacityAndLength(
|
||||
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));
|
||||
}
|
||||
@ -11329,9 +11336,8 @@ Handle<FixedArray> JSObject::SetFastElementsCapacityAndLength(
|
||||
}
|
||||
|
||||
|
||||
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());
|
||||
|
||||
@ -11361,9 +11367,18 @@ void JSObject::SetFastDoubleElementsCapacityAndLength(Handle<JSObject> object,
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@ -13288,9 +13303,8 @@ void JSObject::GetElementsCapacityAndUsage(int* capacity, int* used) {
|
||||
}
|
||||
|
||||
|
||||
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) {
|
||||
|
@ -1906,7 +1906,8 @@ class JSObject: public JSReceiver {
|
||||
|
||||
// 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);
|
||||
@ -1966,6 +1967,12 @@ class JSObject: public JSReceiver {
|
||||
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.
|
||||
@ -1974,10 +1981,8 @@ class JSObject: public JSReceiver {
|
||||
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.
|
||||
|
@ -1055,6 +1055,44 @@ RUNTIME_FUNCTION(Runtime_NormalizeElements) {
|
||||
}
|
||||
|
||||
|
||||
// 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);
|
||||
|
@ -272,6 +272,7 @@ namespace internal {
|
||||
F(MoveArrayContents, 2, 1) \
|
||||
F(EstimateNumberOfElements, 1, 1) \
|
||||
F(NormalizeElements, 1, 1) \
|
||||
F(GrowArrayElements, 3, 1) \
|
||||
F(HasComplexElements, 1, 1) \
|
||||
\
|
||||
/* Getters and Setters */ \
|
||||
|
@ -56,6 +56,12 @@ const Register MathPowIntegerDescriptor::exponent() {
|
||||
}
|
||||
|
||||
|
||||
// IC register specifications
|
||||
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);
|
||||
|
109
test/mjsunit/ensure-growing-store-learns.js
Normal file
109
test/mjsunit/ensure-growing-store-learns.js
Normal file
@ -0,0 +1,109 @@
|
||||
// Copyright 2010 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Flags: --allow-natives-syntax --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);
|
||||
})();
|
Loading…
Reference in New Issue
Block a user