Store mode for keyed stores should be passed in from type feedback

regardless of the map used in polymorphic stores.

BUG=
R=jkummerow@chromium.org, verwaest@chromium.org

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@16323 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 04da3331
...@@ -495,7 +495,7 @@ Handle<Code> CreateAllocationSiteStub::GenerateCode() { ...@@ -495,7 +495,7 @@ Handle<Code> CreateAllocationSiteStub::GenerateCode() {
template <> template <>
HValue* CodeStubGraphBuilder<KeyedLoadFastElementStub>::BuildCodeStub() { HValue* CodeStubGraphBuilder<KeyedLoadFastElementStub>::BuildCodeStub() {
HInstruction* load = BuildUncheckedMonomorphicElementAccess( HInstruction* load = BuildUncheckedMonomorphicElementAccess(
GetParameter(0), GetParameter(1), NULL, NULL, GetParameter(0), GetParameter(1), NULL,
casted_stub()->is_js_array(), casted_stub()->elements_kind(), casted_stub()->is_js_array(), casted_stub()->elements_kind(),
false, NEVER_RETURN_HOLE, STANDARD_STORE); false, NEVER_RETURN_HOLE, STANDARD_STORE);
return load; return load;
...@@ -540,7 +540,7 @@ Handle<Code> KeyedLoadFieldStub::GenerateCode() { ...@@ -540,7 +540,7 @@ Handle<Code> KeyedLoadFieldStub::GenerateCode() {
template <> template <>
HValue* CodeStubGraphBuilder<KeyedStoreFastElementStub>::BuildCodeStub() { HValue* CodeStubGraphBuilder<KeyedStoreFastElementStub>::BuildCodeStub() {
BuildUncheckedMonomorphicElementAccess( BuildUncheckedMonomorphicElementAccess(
GetParameter(0), GetParameter(1), GetParameter(2), NULL, GetParameter(0), GetParameter(1), GetParameter(2),
casted_stub()->is_js_array(), casted_stub()->elements_kind(), casted_stub()->is_js_array(), casted_stub()->elements_kind(),
true, NEVER_RETURN_HOLE, casted_stub()->store_mode()); true, NEVER_RETURN_HOLE, casted_stub()->store_mode());
...@@ -888,7 +888,7 @@ HValue* CodeStubGraphBuilder<ElementsTransitionAndStoreStub>::BuildCodeStub() { ...@@ -888,7 +888,7 @@ HValue* CodeStubGraphBuilder<ElementsTransitionAndStoreStub>::BuildCodeStub() {
casted_stub()->to_kind(), casted_stub()->to_kind(),
casted_stub()->is_jsarray()); casted_stub()->is_jsarray());
BuildUncheckedMonomorphicElementAccess(object, key, value, NULL, BuildUncheckedMonomorphicElementAccess(object, key, value,
casted_stub()->is_jsarray(), casted_stub()->is_jsarray(),
casted_stub()->to_kind(), casted_stub()->to_kind(),
true, ALLOW_RETURN_HOLE, true, ALLOW_RETURN_HOLE,
......
...@@ -1219,10 +1219,9 @@ void HGraphBuilder::BuildTransitionElementsKind(HValue* object, ...@@ -1219,10 +1219,9 @@ void HGraphBuilder::BuildTransitionElementsKind(HValue* object,
HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess( HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess(
HValue* object, HValue* checked_object,
HValue* key, HValue* key,
HValue* val, HValue* val,
HCheckMaps* checked_object,
bool is_js_array, bool is_js_array,
ElementsKind elements_kind, ElementsKind elements_kind,
bool is_store, bool is_store,
...@@ -1237,14 +1236,12 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess( ...@@ -1237,14 +1236,12 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess(
// generated store code. // generated store code.
if ((elements_kind == FAST_HOLEY_ELEMENTS) || if ((elements_kind == FAST_HOLEY_ELEMENTS) ||
(elements_kind == FAST_ELEMENTS && is_store)) { (elements_kind == FAST_ELEMENTS && is_store)) {
if (checked_object != NULL) {
checked_object->ClearGVNFlag(kDependsOnElementsKind); checked_object->ClearGVNFlag(kDependsOnElementsKind);
} }
}
if (checked_object != NULL) object = checked_object;
bool fast_smi_only_elements = IsFastSmiElementsKind(elements_kind); bool fast_smi_only_elements = IsFastSmiElementsKind(elements_kind);
bool fast_elements = IsFastObjectElementsKind(elements_kind); bool fast_elements = IsFastObjectElementsKind(elements_kind);
HValue* elements = AddLoadElements(object); HValue* elements = AddLoadElements(checked_object);
if (is_store && (fast_elements || fast_smi_only_elements) && if (is_store && (fast_elements || fast_smi_only_elements) &&
store_mode != STORE_NO_TRANSITION_HANDLE_COW) { store_mode != STORE_NO_TRANSITION_HANDLE_COW) {
HCheckMaps* check_cow_map = Add<HCheckMaps>( HCheckMaps* check_cow_map = Add<HCheckMaps>(
...@@ -1254,7 +1251,7 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess( ...@@ -1254,7 +1251,7 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess(
HInstruction* length = NULL; HInstruction* length = NULL;
if (is_js_array) { if (is_js_array) {
length = Add<HLoadNamedField>( length = Add<HLoadNamedField>(
object, HObjectAccess::ForArrayLength(elements_kind)); checked_object, HObjectAccess::ForArrayLength(elements_kind));
} else { } else {
length = AddLoadFixedArrayLength(elements); length = AddLoadFixedArrayLength(elements);
} }
...@@ -1301,8 +1298,9 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess( ...@@ -1301,8 +1298,9 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess(
if (IsGrowStoreMode(store_mode)) { if (IsGrowStoreMode(store_mode)) {
NoObservableSideEffectsScope no_effects(this); NoObservableSideEffectsScope no_effects(this);
elements = BuildCheckForCapacityGrow(object, elements, elements_kind, elements = BuildCheckForCapacityGrow(checked_object, elements,
length, key, is_js_array); elements_kind, length, key,
is_js_array);
checked_key = key; checked_key = key;
} else { } else {
checked_key = Add<HBoundsCheck>(key, length); checked_key = Add<HBoundsCheck>(key, length);
...@@ -1310,9 +1308,8 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess( ...@@ -1310,9 +1308,8 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess(
if (is_store && (fast_elements || fast_smi_only_elements)) { if (is_store && (fast_elements || fast_smi_only_elements)) {
if (store_mode == STORE_NO_TRANSITION_HANDLE_COW) { if (store_mode == STORE_NO_TRANSITION_HANDLE_COW) {
NoObservableSideEffectsScope no_effects(this); NoObservableSideEffectsScope no_effects(this);
elements = BuildCopyElementsOnWrite(checked_object, elements,
elements = BuildCopyElementsOnWrite(object, elements, elements_kind, elements_kind, length);
length);
} else { } else {
HCheckMaps* check_cow_map = Add<HCheckMaps>( HCheckMaps* check_cow_map = Add<HCheckMaps>(
elements, isolate()->factory()->fixed_array_map(), elements, isolate()->factory()->fixed_array_map(),
...@@ -5501,19 +5498,7 @@ HInstruction* HOptimizedGraphBuilder::BuildLoadKeyedGeneric(HValue* object, ...@@ -5501,19 +5498,7 @@ HInstruction* HOptimizedGraphBuilder::BuildLoadKeyedGeneric(HValue* object,
} }
HInstruction* HOptimizedGraphBuilder::BuildMonomorphicElementAccess( LoadKeyedHoleMode HOptimizedGraphBuilder::BuildKeyedHoleMode(Handle<Map> map) {
HValue* object,
HValue* key,
HValue* val,
HValue* dependency,
Handle<Map> map,
bool is_store,
KeyedAccessStoreMode store_mode) {
HCheckMaps* mapcheck = Add<HCheckMaps>(object, map, top_info(), dependency);
if (dependency) {
mapcheck->ClearGVNFlag(kDependsOnElementsKind);
}
// Loads from a "stock" fast holey double arrays can elide the hole check. // Loads from a "stock" fast holey double arrays can elide the hole check.
LoadKeyedHoleMode load_mode = NEVER_RETURN_HOLE; LoadKeyedHoleMode load_mode = NEVER_RETURN_HOLE;
if (*map == isolate()->get_initial_js_array_map(FAST_HOLEY_DOUBLE_ELEMENTS) && if (*map == isolate()->get_initial_js_array_map(FAST_HOLEY_DOUBLE_ELEMENTS) &&
...@@ -5525,10 +5510,30 @@ HInstruction* HOptimizedGraphBuilder::BuildMonomorphicElementAccess( ...@@ -5525,10 +5510,30 @@ HInstruction* HOptimizedGraphBuilder::BuildMonomorphicElementAccess(
graph()->MarkDependsOnEmptyArrayProtoElements(); graph()->MarkDependsOnEmptyArrayProtoElements();
} }
return load_mode;
}
HInstruction* HOptimizedGraphBuilder::BuildMonomorphicElementAccess(
HValue* object,
HValue* key,
HValue* val,
HValue* dependency,
Handle<Map> map,
bool is_store,
KeyedAccessStoreMode store_mode) {
HCheckMaps* checked_object = Add<HCheckMaps>(object, map, top_info(),
dependency);
if (dependency) {
checked_object->ClearGVNFlag(kDependsOnElementsKind);
}
LoadKeyedHoleMode load_mode = BuildKeyedHoleMode(map);
return BuildUncheckedMonomorphicElementAccess( return BuildUncheckedMonomorphicElementAccess(
object, key, val, checked_object, key, val,
mapcheck, map->instance_type() == JS_ARRAY_TYPE, map->instance_type() == JS_ARRAY_TYPE,
map->elements_kind(), is_store, load_mode, store_mode); map->elements_kind(), is_store,
load_mode, store_mode);
} }
...@@ -5582,14 +5587,14 @@ HInstruction* HOptimizedGraphBuilder::TryBuildConsolidatedElementLoad( ...@@ -5582,14 +5587,14 @@ HInstruction* HOptimizedGraphBuilder::TryBuildConsolidatedElementLoad(
} }
if (!has_double_maps && !has_smi_or_object_maps) return NULL; if (!has_double_maps && !has_smi_or_object_maps) return NULL;
HCheckMaps* check_maps = Add<HCheckMaps>(object, maps); HCheckMaps* checked_object = Add<HCheckMaps>(object, maps);
// FAST_ELEMENTS is considered more general than FAST_HOLEY_SMI_ELEMENTS. // FAST_ELEMENTS is considered more general than FAST_HOLEY_SMI_ELEMENTS.
// If we've seen both, the consolidated load must use FAST_HOLEY_ELEMENTS. // If we've seen both, the consolidated load must use FAST_HOLEY_ELEMENTS.
ElementsKind consolidated_elements_kind = has_seen_holey_elements ElementsKind consolidated_elements_kind = has_seen_holey_elements
? GetHoleyElementsKind(most_general_consolidated_map->elements_kind()) ? GetHoleyElementsKind(most_general_consolidated_map->elements_kind())
: most_general_consolidated_map->elements_kind(); : most_general_consolidated_map->elements_kind();
HInstruction* instr = BuildUncheckedMonomorphicElementAccess( HInstruction* instr = BuildUncheckedMonomorphicElementAccess(
object, key, val, check_maps, checked_object, key, val,
most_general_consolidated_map->instance_type() == JS_ARRAY_TYPE, most_general_consolidated_map->instance_type() == JS_ARRAY_TYPE,
consolidated_elements_kind, consolidated_elements_kind,
false, NEVER_RETURN_HOLE, STANDARD_STORE); false, NEVER_RETURN_HOLE, STANDARD_STORE);
...@@ -5678,12 +5683,8 @@ HValue* HOptimizedGraphBuilder::HandlePolymorphicElementAccess( ...@@ -5678,12 +5683,8 @@ HValue* HOptimizedGraphBuilder::HandlePolymorphicElementAccess(
return is_store ? NULL : instr; return is_store ? NULL : instr;
} }
HInstruction* checked_object =
AddInstruction(HCheckInstanceType::NewIsSpecObject(object, zone()));
HBasicBlock* join = graph()->CreateBasicBlock(); HBasicBlock* join = graph()->CreateBasicBlock();
HInstruction* elements = AddLoadElements(checked_object);
for (int i = 0; i < untransitionable_maps.length(); ++i) { for (int i = 0; i < untransitionable_maps.length(); ++i) {
Handle<Map> map = untransitionable_maps[i]; Handle<Map> map = untransitionable_maps[i];
ElementsKind elements_kind = map->elements_kind(); ElementsKind elements_kind = map->elements_kind();
...@@ -5694,40 +5695,22 @@ HValue* HOptimizedGraphBuilder::HandlePolymorphicElementAccess( ...@@ -5694,40 +5695,22 @@ HValue* HOptimizedGraphBuilder::HandlePolymorphicElementAccess(
current_block()->Finish(mapcompare); current_block()->Finish(mapcompare);
set_current_block(this_map); set_current_block(this_map);
HInstruction* checked_key = NULL;
HInstruction* access = NULL; HInstruction* access = NULL;
if (IsFastElementsKind(elements_kind)) { if (IsDictionaryElementsKind(elements_kind)) {
if (is_store && !IsFastDoubleElementsKind(elements_kind)) { access = is_store
Add<HCheckMaps>( ? AddInstruction(BuildStoreKeyedGeneric(object, key, val))
elements, isolate()->factory()->fixed_array_map(), : AddInstruction(BuildLoadKeyedGeneric(object, key));
top_info(), mapcompare); } else {
} ASSERT(IsFastElementsKind(elements_kind) ||
if (map->instance_type() == JS_ARRAY_TYPE) { IsExternalArrayElementsKind(elements_kind));
HInstruction* length = Add<HLoadNamedField>( LoadKeyedHoleMode load_mode = BuildKeyedHoleMode(map);
mapcompare, HObjectAccess::ForArrayLength(elements_kind)); // Happily, mapcompare is a checked object.
checked_key = Add<HBoundsCheck>(key, length); access = BuildUncheckedMonomorphicElementAccess(
} else { mapcompare, key, val,
HInstruction* length = AddLoadFixedArrayLength(elements); map->instance_type() == JS_ARRAY_TYPE,
checked_key = Add<HBoundsCheck>(key, length); elements_kind, is_store,
} load_mode,
access = AddFastElementAccess( store_mode);
elements, checked_key, val, mapcompare,
elements_kind, is_store, NEVER_RETURN_HOLE, STANDARD_STORE);
} else if (IsDictionaryElementsKind(elements_kind)) {
if (is_store) {
access = AddInstruction(BuildStoreKeyedGeneric(object, key, val));
} else {
access = AddInstruction(BuildLoadKeyedGeneric(object, key));
}
} else {
ASSERT(IsExternalArrayElementsKind(elements_kind));
HInstruction* length = AddLoadFixedArrayLength(elements);
checked_key = Add<HBoundsCheck>(key, length);
HLoadExternalArrayPointer* external_elements =
Add<HLoadExternalArrayPointer>(elements);
access = AddExternalArrayElementAccess(
external_elements, checked_key, val,
mapcompare, elements_kind, is_store);
} }
*has_side_effects |= access->HasObservableSideEffects(); *has_side_effects |= access->HasObservableSideEffects();
// The caller will use has_side_effects and add a correct Simulate. // The caller will use has_side_effects and add a correct Simulate.
......
...@@ -1225,10 +1225,9 @@ class HGraphBuilder { ...@@ -1225,10 +1225,9 @@ class HGraphBuilder {
bool is_jsarray); bool is_jsarray);
HInstruction* BuildUncheckedMonomorphicElementAccess( HInstruction* BuildUncheckedMonomorphicElementAccess(
HValue* object, HValue* checked_object,
HValue* key, HValue* key,
HValue* val, HValue* val,
HCheckMaps* mapcheck,
bool is_js_array, bool is_js_array,
ElementsKind elements_kind, ElementsKind elements_kind,
bool is_store, bool is_store,
...@@ -1982,6 +1981,8 @@ class HOptimizedGraphBuilder V8_FINAL ...@@ -1982,6 +1981,8 @@ class HOptimizedGraphBuilder V8_FINAL
HValue* val, HValue* val,
SmallMapList* maps); SmallMapList* maps);
LoadKeyedHoleMode BuildKeyedHoleMode(Handle<Map> map);
HInstruction* BuildMonomorphicElementAccess(HValue* object, HInstruction* BuildMonomorphicElementAccess(HValue* object,
HValue* key, HValue* key,
HValue* val, HValue* val,
......
...@@ -187,6 +187,7 @@ array_store_1(a, 0, 0.5); ...@@ -187,6 +187,7 @@ array_store_1(a, 0, 0.5);
assertEquals(0.5, a[0]); assertEquals(0.5, a[0]);
assertEquals(0.5, array_store_1([], 0, 0.5)); assertEquals(0.5, array_store_1([], 0, 0.5));
// Verify that a grow store will deoptimize if the max gap (difference between // Verify that a grow store will deoptimize if the max gap (difference between
// the end of an array capacity and a new index) is passed. The wrapper is to // the end of an array capacity and a new index) is passed. The wrapper is to
// make sure array_store_10 isn't inlined. // make sure array_store_10 isn't inlined.
...@@ -207,3 +208,49 @@ assertEquals(0.5, array_store_1([], 0, 0.5)); ...@@ -207,3 +208,49 @@ assertEquals(0.5, array_store_1([], 0, 0.5));
%ClearFunctionTypeFeedback(grow_store); %ClearFunctionTypeFeedback(grow_store);
})(); })();
// Verify that a polymorphic store and grow IC when crankshafted is still
// a grow IC (earlier it would revert to a standard store in the polymorphic
// case).
(function() {
function f(o, k, v) {
o[k] = v;
}
a = [3.5];
f(a, 1, "hi"); // DOUBLE packed array -> tagged packed grow
a = {};
a.p = "property";
a[0] = 1;
f(a, 0, 5.4);
%OptimizeFunctionOnNextCall(f);
// Should be a polymorphic grow stub. If not a grow stub it will deopt.
f(new Array("hi"), 1, 3);
assertOptimized(f);
%ClearFunctionTypeFeedback(f);
})();
// Now verify that a polymorphic store (non-growing) IC when crankshafted WILL
// deopt if you pass an element out of bounds.
(function() {
function f(o, k, v) {
o[k] = v;
}
a = [3.5];
f(a, 0, "hi"); // DOUBLE packed array -> tagged packed grow
a = {};
a.p = "property";
a[0] = 1;
f(a, 0, 5.4);
%OptimizeFunctionOnNextCall(f);
f(new Array("hi"), 0, 3);
assertOptimized(f);
// An attempt to grow should cause deopt
f(new Array("hi"), 1, 3);
assertUnoptimized(f);
%ClearFunctionTypeFeedback(f);
})();
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