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) {
|
void FastNewClosureDescriptor::Initialize(CallInterfaceDescriptorData* data) {
|
||||||
Register registers[] = {cp, r2};
|
Register registers[] = {cp, r2};
|
||||||
data->Initialize(arraysize(registers), registers, NULL);
|
data->Initialize(arraysize(registers), registers, NULL);
|
||||||
|
@ -4015,8 +4015,12 @@ void LCodeGen::DoCallWithDescriptor(LCallWithDescriptor* instr) {
|
|||||||
generator.BeforeCall(__ CallSize(code, RelocInfo::CODE_TARGET));
|
generator.BeforeCall(__ CallSize(code, RelocInfo::CODE_TARGET));
|
||||||
PlatformInterfaceDescriptor* call_descriptor =
|
PlatformInterfaceDescriptor* call_descriptor =
|
||||||
instr->descriptor().platform_specific_descriptor();
|
instr->descriptor().platform_specific_descriptor();
|
||||||
__ Call(code, RelocInfo::CODE_TARGET, TypeFeedbackId::None(), al,
|
if (call_descriptor != NULL) {
|
||||||
call_descriptor->storage_mode());
|
__ Call(code, RelocInfo::CODE_TARGET, TypeFeedbackId::None(), al,
|
||||||
|
call_descriptor->storage_mode());
|
||||||
|
} else {
|
||||||
|
__ Call(code, RelocInfo::CODE_TARGET, TypeFeedbackId::None(), al);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
DCHECK(instr->target()->IsRegister());
|
DCHECK(instr->target()->IsRegister());
|
||||||
Register target = ToRegister(instr->target());
|
Register target = ToRegister(instr->target());
|
||||||
|
@ -60,6 +60,12 @@ const Register MathPowTaggedDescriptor::exponent() { return x11; }
|
|||||||
const Register MathPowIntegerDescriptor::exponent() { return x12; }
|
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) {
|
void FastNewClosureDescriptor::Initialize(CallInterfaceDescriptorData* data) {
|
||||||
// cp: context
|
// cp: context
|
||||||
// x2: function info
|
// 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 <>
|
template <>
|
||||||
HValue* CodeStubGraphBuilder<LoadFastElementStub>::BuildCodeStub() {
|
HValue* CodeStubGraphBuilder<LoadFastElementStub>::BuildCodeStub() {
|
||||||
HInstruction* load = BuildUncheckedMonomorphicElementAccess(
|
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) {
|
void CreateAllocationSiteStub::GenerateAheadOfTime(Isolate* isolate) {
|
||||||
CreateAllocationSiteStub stub(isolate);
|
CreateAllocationSiteStub stub(isolate);
|
||||||
stub.GetCode();
|
stub.GetCode();
|
||||||
|
@ -65,6 +65,7 @@ namespace internal {
|
|||||||
V(FastCloneShallowObject) \
|
V(FastCloneShallowObject) \
|
||||||
V(FastNewClosure) \
|
V(FastNewClosure) \
|
||||||
V(FastNewContext) \
|
V(FastNewContext) \
|
||||||
|
V(GrowArrayElements) \
|
||||||
V(InternalArrayNArgumentsConstructor) \
|
V(InternalArrayNArgumentsConstructor) \
|
||||||
V(InternalArrayNoArgumentConstructor) \
|
V(InternalArrayNoArgumentConstructor) \
|
||||||
V(InternalArraySingleArgumentConstructor) \
|
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 {
|
class InstanceofStub: public PlatformCodeStub {
|
||||||
public:
|
public:
|
||||||
enum Flags {
|
enum Flags {
|
||||||
|
@ -783,6 +783,9 @@ void HInstruction::Verify() {
|
|||||||
|
|
||||||
bool HInstruction::CanDeoptimize() {
|
bool HInstruction::CanDeoptimize() {
|
||||||
// TODO(titzer): make this a virtual method?
|
// 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()) {
|
switch (opcode()) {
|
||||||
case HValue::kAbnormalExit:
|
case HValue::kAbnormalExit:
|
||||||
case HValue::kAccessArgumentsAt:
|
case HValue::kAccessArgumentsAt:
|
||||||
@ -796,7 +799,6 @@ bool HInstruction::CanDeoptimize() {
|
|||||||
case HValue::kCallNew:
|
case HValue::kCallNew:
|
||||||
case HValue::kCallNewArray:
|
case HValue::kCallNewArray:
|
||||||
case HValue::kCallStub:
|
case HValue::kCallStub:
|
||||||
case HValue::kCallWithDescriptor:
|
|
||||||
case HValue::kCapturedObject:
|
case HValue::kCapturedObject:
|
||||||
case HValue::kClassOfTestAndBranch:
|
case HValue::kClassOfTestAndBranch:
|
||||||
case HValue::kCompareGeneric:
|
case HValue::kCompareGeneric:
|
||||||
@ -863,6 +865,7 @@ bool HInstruction::CanDeoptimize() {
|
|||||||
case HValue::kBranch:
|
case HValue::kBranch:
|
||||||
case HValue::kCallJSFunction:
|
case HValue::kCallJSFunction:
|
||||||
case HValue::kCallRuntime:
|
case HValue::kCallRuntime:
|
||||||
|
case HValue::kCallWithDescriptor:
|
||||||
case HValue::kChange:
|
case HValue::kChange:
|
||||||
case HValue::kCheckHeapObject:
|
case HValue::kCheckHeapObject:
|
||||||
case HValue::kCheckInstanceType:
|
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* HGraphBuilder::BuildCheckForCapacityGrow(
|
||||||
HValue* object,
|
HValue* object,
|
||||||
HValue* elements,
|
HValue* elements,
|
||||||
@ -1320,17 +1334,26 @@ HValue* HGraphBuilder::BuildCheckForCapacityGrow(
|
|||||||
Token::GTE);
|
Token::GTE);
|
||||||
capacity_checker.Then();
|
capacity_checker.Then();
|
||||||
|
|
||||||
HValue* max_gap = Add<HConstant>(static_cast<int32_t>(JSObject::kMaxGap));
|
// BuildCheckAndGrowElementsCapacity could de-opt without profitable feedback,
|
||||||
HValue* max_capacity = AddUncasted<HAdd>(current_capacity, max_gap);
|
// 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();
|
capacity_checker.Else();
|
||||||
|
|
||||||
environment()->Push(elements);
|
environment()->Push(elements);
|
||||||
|
@ -1335,6 +1335,10 @@ class HGraphBuilder {
|
|||||||
bool is_js_array,
|
bool is_js_array,
|
||||||
PropertyAccessType access_type);
|
PropertyAccessType access_type);
|
||||||
|
|
||||||
|
HValue* BuildCheckAndGrowElementsCapacity(HValue* object, HValue* elements,
|
||||||
|
ElementsKind kind, HValue* length,
|
||||||
|
HValue* capacity, HValue* key);
|
||||||
|
|
||||||
HValue* BuildCopyElementsOnWrite(HValue* object,
|
HValue* BuildCopyElementsOnWrite(HValue* object,
|
||||||
HValue* elements,
|
HValue* elements,
|
||||||
ElementsKind kind,
|
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) {
|
void FastNewClosureDescriptor::Initialize(CallInterfaceDescriptorData* data) {
|
||||||
Register registers[] = {esi, ebx};
|
Register registers[] = {esi, ebx};
|
||||||
data->Initialize(arraysize(registers), registers, NULL);
|
data->Initialize(arraysize(registers), registers, NULL);
|
||||||
|
@ -146,5 +146,12 @@ void ContextOnlyDescriptor::Initialize(CallInterfaceDescriptorData* data) {
|
|||||||
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
|
} // namespace v8::internal
|
||||||
|
@ -53,7 +53,8 @@ class PlatformInterfaceDescriptor;
|
|||||||
V(StoreArrayLiteralElement) \
|
V(StoreArrayLiteralElement) \
|
||||||
V(MathPowTagged) \
|
V(MathPowTagged) \
|
||||||
V(MathPowInteger) \
|
V(MathPowInteger) \
|
||||||
V(ContextOnly)
|
V(ContextOnly) \
|
||||||
|
V(GrowArrayElements)
|
||||||
|
|
||||||
|
|
||||||
class CallInterfaceDescriptorData {
|
class CallInterfaceDescriptorData {
|
||||||
@ -488,6 +489,17 @@ class ContextOnlyDescriptor : public CallInterfaceDescriptor {
|
|||||||
DECLARE_DESCRIPTOR(ContextOnlyDescriptor, 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
|
#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,
|
void JSObject::SetMapAndElements(Handle<JSObject> object,
|
||||||
Handle<Map> new_map,
|
Handle<Map> new_map,
|
||||||
Handle<FixedArrayBase> value) {
|
Handle<FixedArrayBase> value) {
|
||||||
|
@ -11267,10 +11267,8 @@ void Code::Disassemble(const char* name, std::ostream& os) { // NOLINT
|
|||||||
#endif // ENABLE_DISASSEMBLER
|
#endif // ENABLE_DISASSEMBLER
|
||||||
|
|
||||||
|
|
||||||
Handle<FixedArray> JSObject::SetFastElementsCapacityAndLength(
|
Handle<FixedArray> JSObject::SetFastElementsCapacity(
|
||||||
Handle<JSObject> object,
|
Handle<JSObject> object, int capacity,
|
||||||
int capacity,
|
|
||||||
int length,
|
|
||||||
SetFastElementsCapacitySmiMode smi_mode) {
|
SetFastElementsCapacitySmiMode smi_mode) {
|
||||||
// We should never end in here with a pixel or external array.
|
// We should never end in here with a pixel or external array.
|
||||||
DCHECK(!object->HasExternalArrayElements());
|
DCHECK(!object->HasExternalArrayElements());
|
||||||
@ -11322,6 +11320,15 @@ Handle<FixedArray> JSObject::SetFastElementsCapacityAndLength(
|
|||||||
object->GetElementsKind(), new_elements);
|
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()) {
|
if (object->IsJSArray()) {
|
||||||
Handle<JSArray>::cast(object)->set_length(Smi::FromInt(length));
|
Handle<JSArray>::cast(object)->set_length(Smi::FromInt(length));
|
||||||
}
|
}
|
||||||
@ -11329,9 +11336,8 @@ Handle<FixedArray> JSObject::SetFastElementsCapacityAndLength(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void JSObject::SetFastDoubleElementsCapacityAndLength(Handle<JSObject> object,
|
Handle<FixedArrayBase> JSObject::SetFastDoubleElementsCapacity(
|
||||||
int capacity,
|
Handle<JSObject> object, int capacity) {
|
||||||
int length) {
|
|
||||||
// We should never end in here with a pixel or external array.
|
// We should never end in here with a pixel or external array.
|
||||||
DCHECK(!object->HasExternalArrayElements());
|
DCHECK(!object->HasExternalArrayElements());
|
||||||
|
|
||||||
@ -11361,9 +11367,18 @@ void JSObject::SetFastDoubleElementsCapacityAndLength(Handle<JSObject> object,
|
|||||||
object->GetElementsKind(), elems);
|
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()) {
|
if (object->IsJSArray()) {
|
||||||
Handle<JSArray>::cast(object)->set_length(Smi::FromInt(length));
|
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) {
|
bool JSObject::WouldConvertToSlowElements(uint32_t index) {
|
||||||
uint32_t index;
|
if (HasFastElements()) {
|
||||||
if (HasFastElements() && key->ToArrayIndex(&index)) {
|
|
||||||
Handle<FixedArrayBase> backing_store(FixedArrayBase::cast(elements()));
|
Handle<FixedArrayBase> backing_store(FixedArrayBase::cast(elements()));
|
||||||
uint32_t capacity = static_cast<uint32_t>(backing_store->length());
|
uint32_t capacity = static_cast<uint32_t>(backing_store->length());
|
||||||
if (index >= capacity) {
|
if (index >= capacity) {
|
||||||
|
@ -1906,7 +1906,8 @@ class JSObject: public JSReceiver {
|
|||||||
|
|
||||||
// Would we convert a fast elements array to dictionary mode given
|
// Would we convert a fast elements array to dictionary mode given
|
||||||
// an access at key?
|
// 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
|
// Do we want to keep the elements in fast case when increasing the
|
||||||
// capacity?
|
// capacity?
|
||||||
bool ShouldConvertToSlowElements(int new_capacity);
|
bool ShouldConvertToSlowElements(int new_capacity);
|
||||||
@ -1966,6 +1967,12 @@ class JSObject: public JSReceiver {
|
|||||||
kDontAllowSmiElements
|
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
|
// Replace the elements' backing store with fast elements of the given
|
||||||
// capacity. Update the length for JSArrays. Returns the new backing
|
// capacity. Update the length for JSArrays. Returns the new backing
|
||||||
// store.
|
// store.
|
||||||
@ -1974,10 +1981,8 @@ class JSObject: public JSReceiver {
|
|||||||
int capacity,
|
int capacity,
|
||||||
int length,
|
int length,
|
||||||
SetFastElementsCapacitySmiMode smi_mode);
|
SetFastElementsCapacitySmiMode smi_mode);
|
||||||
static void SetFastDoubleElementsCapacityAndLength(
|
static Handle<FixedArrayBase> SetFastDoubleElementsCapacityAndLength(
|
||||||
Handle<JSObject> object,
|
Handle<JSObject> object, int capacity, int length);
|
||||||
int capacity,
|
|
||||||
int length);
|
|
||||||
|
|
||||||
// Lookup interceptors are used for handling properties controlled by host
|
// Lookup interceptors are used for handling properties controlled by host
|
||||||
// objects.
|
// 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) {
|
RUNTIME_FUNCTION(Runtime_HasComplexElements) {
|
||||||
HandleScope scope(isolate);
|
HandleScope scope(isolate);
|
||||||
DCHECK(args.length() == 1);
|
DCHECK(args.length() == 1);
|
||||||
|
@ -272,6 +272,7 @@ namespace internal {
|
|||||||
F(MoveArrayContents, 2, 1) \
|
F(MoveArrayContents, 2, 1) \
|
||||||
F(EstimateNumberOfElements, 1, 1) \
|
F(EstimateNumberOfElements, 1, 1) \
|
||||||
F(NormalizeElements, 1, 1) \
|
F(NormalizeElements, 1, 1) \
|
||||||
|
F(GrowArrayElements, 3, 1) \
|
||||||
F(HasComplexElements, 1, 1) \
|
F(HasComplexElements, 1, 1) \
|
||||||
\
|
\
|
||||||
/* Getters and Setters */ \
|
/* 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) {
|
void FastNewClosureDescriptor::Initialize(CallInterfaceDescriptorData* data) {
|
||||||
Register registers[] = {rsi, rbx};
|
Register registers[] = {rsi, rbx};
|
||||||
data->Initialize(arraysize(registers), registers, NULL);
|
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