Revision: 9702
Author: [email protected]
Date: Wed Oct 19 05:10:18 2011
Log: Introduce HTransitionElementsKind instruction.
TEST=mjsunit/elements-kind
Review URL: http://codereview.chromium.org/8305001
http://code.google.com/p/v8/source/detail?r=9702
Modified:
/branches/bleeding_edge/src/arm/ic-arm.cc
/branches/bleeding_edge/src/arm/lithium-arm.cc
/branches/bleeding_edge/src/arm/lithium-arm.h
/branches/bleeding_edge/src/arm/lithium-codegen-arm.cc
/branches/bleeding_edge/src/arm/stub-cache-arm.cc
/branches/bleeding_edge/src/builtins.cc
/branches/bleeding_edge/src/builtins.h
/branches/bleeding_edge/src/handles.cc
/branches/bleeding_edge/src/handles.h
/branches/bleeding_edge/src/hydrogen-instructions.cc
/branches/bleeding_edge/src/hydrogen-instructions.h
/branches/bleeding_edge/src/hydrogen.cc
/branches/bleeding_edge/src/ia32/ic-ia32.cc
/branches/bleeding_edge/src/ia32/lithium-codegen-ia32.cc
/branches/bleeding_edge/src/ia32/lithium-ia32.cc
/branches/bleeding_edge/src/ia32/lithium-ia32.h
/branches/bleeding_edge/src/ic.cc
/branches/bleeding_edge/src/ic.h
/branches/bleeding_edge/src/objects.cc
/branches/bleeding_edge/src/objects.h
/branches/bleeding_edge/src/runtime.cc
/branches/bleeding_edge/src/runtime.h
/branches/bleeding_edge/src/x64/ic-x64.cc
/branches/bleeding_edge/src/x64/lithium-codegen-x64.cc
/branches/bleeding_edge/src/x64/lithium-x64.cc
/branches/bleeding_edge/src/x64/lithium-x64.h
/branches/bleeding_edge/test/mjsunit/elements-kind.js
=======================================
--- /branches/bleeding_edge/src/arm/ic-arm.cc Mon Oct 17 00:43:40 2011
+++ /branches/bleeding_edge/src/arm/ic-arm.cc Wed Oct 19 05:10:18 2011
@@ -1242,6 +1242,31 @@
ExternalReference(IC_Utility(kKeyedStoreIC_Slow), masm->isolate());
__ TailCallExternalReference(ref, 3, 1);
}
+
+
+void KeyedStoreIC::GenerateTransitionElementsSmiToDouble(MacroAssembler*
masm) {
+ // ---------- S t a t e --------------
+ // -- r2 : receiver
+ // -- lr : return address
+ // -----------------------------------
+ // Must return the modified receiver in r0.
+
+ __ push(r2);
+ __ TailCallRuntime(Runtime::kTransitionElementsSmiToDouble, 1, 1);
+}
+
+
+void KeyedStoreIC::GenerateTransitionElementsDoubleToObject(
+ MacroAssembler* masm) {
+ // ---------- S t a t e --------------
+ // -- r2 : receiver
+ // -- lr : return address
+ // -----------------------------------
+ // Must return the modified receiver in r0.
+
+ __ push(r2);
+ __ TailCallRuntime(Runtime::kTransitionElementsDoubleToObject, 1, 1);
+}
void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm,
=======================================
--- /branches/bleeding_edge/src/arm/lithium-arm.cc Mon Oct 17 00:43:40 2011
+++ /branches/bleeding_edge/src/arm/lithium-arm.cc Wed Oct 19 05:10:18 2011
@@ -389,6 +389,12 @@
stream->Add("] <- ");
value()->PrintTo(stream);
}
+
+
+void LTransitionElementsKind::PrintDataTo(StringStream* stream) {
+ object()->PrintTo(stream);
+ stream->Add(" %p -> %p", *original_map(), *transitioned_map());
+}
LChunk::LChunk(CompilationInfo* info, HGraph* graph)
@@ -1966,6 +1972,26 @@
return MarkAsCall(new LStoreKeyedGeneric(obj, key, val), instr);
}
+
+
+LInstruction* LChunkBuilder::DoTransitionElementsKind(
+ HTransitionElementsKind* instr) {
+ if (instr->original_map()->elements_kind() == FAST_SMI_ONLY_ELEMENTS &&
+ instr->transitioned_map()->elements_kind() == FAST_ELEMENTS) {
+ LOperand* object = UseRegister(instr->object());
+ LOperand* new_map_reg = TempRegister();
+ LTransitionElementsKind* result =
+ new LTransitionElementsKind(object, new_map_reg, NULL);
+ return DefineSameAsFirst(result);
+ } else {
+ LOperand* object = UseFixed(instr->object(), r0);
+ LOperand* fixed_object_reg = FixedTemp(r2);
+ LOperand* new_map_reg = FixedTemp(r3);
+ LTransitionElementsKind* result =
+ new LTransitionElementsKind(object, new_map_reg, fixed_object_reg);
+ return MarkAsCall(DefineFixed(result, r0), instr);
+ }
+}
LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) {
=======================================
--- /branches/bleeding_edge/src/arm/lithium-arm.h Wed Oct 19 04:41:22 2011
+++ /branches/bleeding_edge/src/arm/lithium-arm.h Wed Oct 19 05:10:18 2011
@@ -162,6 +162,7 @@
V(ThisFunction) \
V(Throw) \
V(ToFastProperties) \
+ V(TransitionElementsKind) \
V(Typeof) \
V(TypeofIsAndBranch) \
V(UnaryMathOperation) \
@@ -1669,6 +1670,30 @@
};
+class LTransitionElementsKind: public LTemplateInstruction<1, 1, 2> {
+ public:
+ LTransitionElementsKind(LOperand* object,
+ LOperand* new_map_temp,
+ LOperand* temp_reg) {
+ inputs_[0] = object;
+ temps_[0] = new_map_temp;
+ temps_[1] = temp_reg;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(TransitionElementsKind,
+ "transition-elements-kind")
+ DECLARE_HYDROGEN_ACCESSOR(TransitionElementsKind)
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ LOperand* object() { return inputs_[0]; }
+ LOperand* new_map_reg() { return temps_[0]; }
+ LOperand* temp_reg() { return temps_[1]; }
+ Handle<Map> original_map() { return hydrogen()->original_map(); }
+ Handle<Map> transitioned_map() { return hydrogen()->transitioned_map(); }
+};
+
+
class LStringAdd: public LTemplateInstruction<1, 2, 0> {
public:
LStringAdd(LOperand* left, LOperand* right) {
=======================================
--- /branches/bleeding_edge/src/arm/lithium-codegen-arm.cc Wed Oct 19
04:41:22 2011
+++ /branches/bleeding_edge/src/arm/lithium-codegen-arm.cc Wed Oct 19
05:10:18 2011
@@ -3517,6 +3517,48 @@
: isolate()->builtins()->KeyedStoreIC_Initialize();
CallCode(ic, RelocInfo::CODE_TARGET, instr);
}
+
+
+void LCodeGen::DoTransitionElementsKind(LTransitionElementsKind* instr) {
+ Register object_reg = ToRegister(instr->object());
+ Register new_map_reg = ToRegister(instr->new_map_reg());
+ Register scratch = scratch0();
+
+ Handle<Map> from_map = instr->original_map();
+ Handle<Map> to_map = instr->transitioned_map();
+ ElementsKind from_kind = from_map->elements_kind();
+ ElementsKind to_kind = to_map->elements_kind();
+
+ Label not_applicable;
+ __ ldr(scratch, FieldMemOperand(object_reg, HeapObject::kMapOffset));
+ __ cmp(scratch, Operand(from_map));
+ __ b(ne, ¬_applicable);
+ __ mov(new_map_reg, Operand(to_map));
+ if (from_kind == FAST_SMI_ONLY_ELEMENTS && to_kind == FAST_ELEMENTS) {
+ __ str(new_map_reg, FieldMemOperand(object_reg,
HeapObject::kMapOffset));
+ // Write barrier.
+ __ RecordWriteField(object_reg, HeapObject::kMapOffset, new_map_reg,
+ scratch, kLRHasBeenSaved, kDontSaveFPRegs);
+ } else if (from_kind == FAST_SMI_ONLY_ELEMENTS &&
+ to_kind == FAST_DOUBLE_ELEMENTS) {
+ Register fixed_object_reg = ToRegister(instr->temp_reg());
+ ASSERT(fixed_object_reg.is(r2));
+ ASSERT(new_map_reg.is(r3));
+ __ mov(fixed_object_reg, object_reg);
+ CallCode(isolate()->builtins()->TransitionElementsSmiToDouble(),
+ RelocInfo::CODE_TARGET, instr);
+ } else if (from_kind == FAST_DOUBLE_ELEMENTS && to_kind ==
FAST_ELEMENTS) {
+ Register fixed_object_reg = ToRegister(instr->temp_reg());
+ ASSERT(fixed_object_reg.is(r2));
+ ASSERT(new_map_reg.is(r3));
+ __ mov(fixed_object_reg, object_reg);
+ CallCode(isolate()->builtins()->TransitionElementsDoubleToObject(),
+ RelocInfo::CODE_TARGET, instr);
+ } else {
+ UNREACHABLE();
+ }
+ __ bind(¬_applicable);
+}
void LCodeGen::DoStringAdd(LStringAdd* instr) {
=======================================
--- /branches/bleeding_edge/src/arm/stub-cache-arm.cc Wed Oct 19 02:04:35
2011
+++ /branches/bleeding_edge/src/arm/stub-cache-arm.cc Wed Oct 19 05:10:18
2011
@@ -1619,7 +1619,7 @@
__ bind(&with_write_barrier);
__ ldr(r6, FieldMemOperand(receiver, HeapObject::kMapOffset));
- __ CheckFastSmiOnlyElements(r6, r6, &call_builtin);
+ __ CheckFastObjectElements(r6, r6, &call_builtin);
// Save new length.
__ str(r0, FieldMemOperand(receiver, JSArray::kLengthOffset));
=======================================
--- /branches/bleeding_edge/src/builtins.cc Tue Oct 11 04:35:04 2011
+++ /branches/bleeding_edge/src/builtins.cc Wed Oct 19 05:10:18 2011
@@ -1506,6 +1506,14 @@
static void Generate_KeyedStoreIC_NonStrictArguments(MacroAssembler* masm)
{
KeyedStoreIC::GenerateNonStrictArguments(masm);
}
+
+static void Generate_TransitionElementsSmiToDouble(MacroAssembler* masm) {
+ KeyedStoreIC::GenerateTransitionElementsSmiToDouble(masm);
+}
+
+static void Generate_TransitionElementsDoubleToObject(MacroAssembler*
masm) {
+ KeyedStoreIC::GenerateTransitionElementsDoubleToObject(masm);
+}
#ifdef ENABLE_DEBUGGER_SUPPORT
static void Generate_LoadIC_DebugBreak(MacroAssembler* masm) {
=======================================
--- /branches/bleeding_edge/src/builtins.h Tue Sep 13 04:42:57 2011
+++ /branches/bleeding_edge/src/builtins.h Wed Oct 19 05:10:18 2011
@@ -167,6 +167,10 @@
kStrictMode) \
V(KeyedStoreIC_NonStrictArguments, KEYED_STORE_IC, MEGAMORPHIC, \
Code::kNoExtraICState) \
+ V(TransitionElementsSmiToDouble, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
+ V(TransitionElementsDoubleToObject, BUILTIN, UNINITIALIZED, \
+ Code::kNoExtraICState) \
\
/* Uses KeyedLoadIC_Initialize; must be after in list. */ \
V(FunctionCall, BUILTIN, UNINITIALIZED, \
=======================================
--- /branches/bleeding_edge/src/handles.cc Wed Oct 19 05:04:16 2011
+++ /branches/bleeding_edge/src/handles.cc Wed Oct 19 05:10:18 2011
@@ -484,6 +484,14 @@
object->SetElement(index, *value, strict_mode, false),
Object);
}
+
+
+Handle<Object> TransitionElementsKind(Handle<JSObject> object,
+ ElementsKind to_kind) {
+ CALL_HEAP_FUNCTION(object->GetIsolate(),
+ object->TransitionElementsKind(to_kind),
+ Object);
+}
Handle<JSObject> Copy(Handle<JSObject> obj) {
=======================================
--- /branches/bleeding_edge/src/handles.h Wed Oct 19 05:04:16 2011
+++ /branches/bleeding_edge/src/handles.h Wed Oct 19 05:10:18 2011
@@ -240,6 +240,9 @@
Handle<Object> value,
StrictModeFlag strict_mode);
+Handle<Object> TransitionElementsKind(Handle<JSObject> object,
+ ElementsKind to_kind);
+
Handle<Object> GetProperty(Handle<JSReceiver> obj,
const char* name);
=======================================
--- /branches/bleeding_edge/src/hydrogen-instructions.cc Wed Oct 19
04:36:55 2011
+++ /branches/bleeding_edge/src/hydrogen-instructions.cc Wed Oct 19
05:10:18 2011
@@ -1642,6 +1642,12 @@
stream->Add("] = ");
value()->PrintNameTo(stream);
}
+
+
+void HTransitionElementsKind::PrintDataTo(StringStream* stream) {
+ object()->PrintNameTo(stream);
+ stream->Add(" %p -> %p", *original_map(), *transitioned_map());
+}
void HLoadGlobalCell::PrintDataTo(StringStream* stream) {
=======================================
--- /branches/bleeding_edge/src/hydrogen-instructions.h Wed Oct 19 04:41:22
2011
+++ /branches/bleeding_edge/src/hydrogen-instructions.h Wed Oct 19 05:10:18
2011
@@ -171,6 +171,7 @@
V(Throw) \
V(ToFastProperties) \
V(ToInt32) \
+ V(TransitionElementsKind) \
V(Typeof) \
V(TypeofIsAndBranch) \
V(UnaryMathOperation) \
@@ -3905,6 +3906,44 @@
};
+class HTransitionElementsKind: public HTemplateInstruction<1> {
+ public:
+ HTransitionElementsKind(HValue* object,
+ Handle<Map> original_map,
+ Handle<Map> transitioned_map)
+ : original_map_(original_map),
+ transitioned_map_(transitioned_map) {
+ SetOperandAt(0, object);
+ SetFlag(kUseGVN);
+ SetFlag(kDependsOnMaps);
+ set_representation(Representation::Tagged());
+ }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::Tagged();
+ }
+
+ HValue* object() { return OperandAt(0); }
+ Handle<Map> original_map() { return original_map_; }
+ Handle<Map> transitioned_map() { return transitioned_map_; }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ DECLARE_CONCRETE_INSTRUCTION(TransitionElementsKind)
+
+ protected:
+ virtual bool DataEquals(HValue* other) {
+ HTransitionElementsKind* instr = HTransitionElementsKind::cast(other);
+ return original_map_.is_identical_to(instr->original_map()) &&
+ transitioned_map_.is_identical_to(instr->transitioned_map());
+ }
+
+ private:
+ Handle<Map> original_map_;
+ Handle<Map> transitioned_map_;
+};
+
+
class HStringAdd: public HBinaryOperation {
public:
HStringAdd(HValue* context, HValue* left, HValue* right)
=======================================
--- /branches/bleeding_edge/src/hydrogen.cc Wed Oct 19 04:41:22 2011
+++ /branches/bleeding_edge/src/hydrogen.cc Wed Oct 19 05:10:18 2011
@@ -4097,13 +4097,41 @@
for (int i = 0; i < kNumElementTypes; ++i) {
type_todo[i] = false;
}
+
+ // Elements_kind transition support.
+ MapList transition_target(maps->length());
+ if (is_store) {
+ // Collect possible transition targets.
+ MapList possible_transitioned_maps(maps->length());
+ for (int i = 0; i < maps->length(); ++i) {
+ Handle<Map> map = maps->at(i);
+ ElementsKind elements_kind = map->elements_kind();
+ if (elements_kind == FAST_DOUBLE_ELEMENTS ||
+ elements_kind == FAST_ELEMENTS) {
+ possible_transitioned_maps.Add(*map);
+ }
+ }
+ // Get transition target for each map (NULL == no transition).
+ for (int i = 0; i < maps->length(); ++i) {
+ Handle<Map> map = maps->at(i);
+ Map* transitioned_map =
+ map->FindTransitionedMap(&possible_transitioned_maps);
+ transition_target.Add(transitioned_map);
+ }
+ }
for (int i = 0; i < maps->length(); ++i) {
- ASSERT(maps->at(i)->IsMap());
- type_todo[maps->at(i)->elements_kind()] = true;
- if (maps->at(i)->elements_kind()
- >= FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND) {
- todo_external_array = true;
+ Handle<Map> map = maps->at(i);
+ ASSERT(map->IsMap());
+ ASSERT(!is_store || (transition_target.length() == maps->length()));
+ if (is_store && transition_target.at(i) != NULL) {
+ object = AddInstruction(new(zone()) HTransitionElementsKind(
+ object, map, Handle<Map>(transition_target.at(i))));
+ } else {
+ type_todo[map->elements_kind()] = true;
+ if (map->elements_kind() >= FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND) {
+ todo_external_array = true;
+ }
}
}
=======================================
--- /branches/bleeding_edge/src/ia32/ic-ia32.cc Mon Oct 17 00:43:40 2011
+++ /branches/bleeding_edge/src/ia32/ic-ia32.cc Wed Oct 19 05:10:18 2011
@@ -1578,6 +1578,37 @@
ExternalReference ref(IC_Utility(kKeyedStoreIC_Slow), masm->isolate());
__ TailCallExternalReference(ref, 3, 1);
}
+
+
+void KeyedStoreIC::GenerateTransitionElementsSmiToDouble(MacroAssembler*
masm) {
+ // ----------- S t a t e -------------
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+ // Must return the modified receiver in eax.
+
+ __ pop(ebx);
+ __ push(edx);
+ __ push(ebx); // return address
+
+ __ TailCallRuntime(Runtime::kTransitionElementsSmiToDouble, 1, 1);
+}
+
+
+void KeyedStoreIC::GenerateTransitionElementsDoubleToObject(
+ MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+ // Must return the modified receiver in eax.
+
+ __ pop(ebx);
+ __ push(edx);
+ __ push(ebx); // return address
+
+ __ TailCallRuntime(Runtime::kTransitionElementsDoubleToObject, 1, 1);
+}
#undef __
=======================================
--- /branches/bleeding_edge/src/ia32/lithium-codegen-ia32.cc Wed Oct 19
04:41:22 2011
+++ /branches/bleeding_edge/src/ia32/lithium-codegen-ia32.cc Wed Oct 19
05:10:18 2011
@@ -3336,6 +3336,48 @@
: isolate()->builtins()->KeyedStoreIC_Initialize();
CallCode(ic, RelocInfo::CODE_TARGET, instr);
}
+
+
+void LCodeGen::DoTransitionElementsKind(LTransitionElementsKind* instr) {
+ Register object_reg = ToRegister(instr->object());
+ Register new_map_reg = ToRegister(instr->new_map_reg());
+
+ Handle<Map> from_map = instr->original_map();
+ Handle<Map> to_map = instr->transitioned_map();
+ ElementsKind from_kind = from_map->elements_kind();
+ ElementsKind to_kind = to_map->elements_kind();
+
+ Label not_applicable;
+ __ cmp(FieldOperand(object_reg, HeapObject::kMapOffset), from_map);
+ __ j(not_equal, ¬_applicable);
+ __ mov(new_map_reg, to_map);
+ if (from_kind == FAST_SMI_ONLY_ELEMENTS && to_kind == FAST_ELEMENTS) {
+ Register object_reg = ToRegister(instr->object());
+ __ mov(FieldOperand(object_reg, HeapObject::kMapOffset), new_map_reg);
+ // Write barrier.
+ ASSERT_NE(instr->temp_reg(), NULL);
+ __ RecordWriteField(object_reg, HeapObject::kMapOffset, new_map_reg,
+ ToRegister(instr->temp_reg()), kDontSaveFPRegs);
+ } else if (from_kind == FAST_SMI_ONLY_ELEMENTS &&
+ to_kind == FAST_DOUBLE_ELEMENTS) {
+ Register fixed_object_reg = ToRegister(instr->temp_reg());
+ ASSERT(fixed_object_reg.is(edx));
+ ASSERT(new_map_reg.is(ebx));
+ __ mov(fixed_object_reg, object_reg);
+ CallCode(isolate()->builtins()->TransitionElementsSmiToDouble(),
+ RelocInfo::CODE_TARGET, instr);
+ } else if (from_kind == FAST_DOUBLE_ELEMENTS && to_kind ==
FAST_ELEMENTS) {
+ Register fixed_object_reg = ToRegister(instr->temp_reg());
+ ASSERT(fixed_object_reg.is(edx));
+ ASSERT(new_map_reg.is(ebx));
+ __ mov(fixed_object_reg, object_reg);
+ CallCode(isolate()->builtins()->TransitionElementsDoubleToObject(),
+ RelocInfo::CODE_TARGET, instr);
+ } else {
+ UNREACHABLE();
+ }
+ __ bind(¬_applicable);
+}
void LCodeGen::DoStringCharCodeAt(LStringCharCodeAt* instr) {
=======================================
--- /branches/bleeding_edge/src/ia32/lithium-ia32.cc Mon Oct 17 00:43:40
2011
+++ /branches/bleeding_edge/src/ia32/lithium-ia32.cc Wed Oct 19 05:10:18
2011
@@ -450,6 +450,12 @@
stream->Add("] <- ");
value()->PrintTo(stream);
}
+
+
+void LTransitionElementsKind::PrintDataTo(StringStream* stream) {
+ object()->PrintTo(stream);
+ stream->Add(" %p -> %p", *original_map(), *transitioned_map());
+}
void LChunk::AddInstruction(LInstruction* instr, HBasicBlock* block) {
@@ -2029,6 +2035,27 @@
new LStoreKeyedGeneric(context, object, key, value);
return MarkAsCall(result, instr);
}
+
+
+LInstruction* LChunkBuilder::DoTransitionElementsKind(
+ HTransitionElementsKind* instr) {
+ if (instr->original_map()->elements_kind() == FAST_SMI_ONLY_ELEMENTS &&
+ instr->transitioned_map()->elements_kind() == FAST_ELEMENTS) {
+ LOperand* object = UseRegister(instr->object());
+ LOperand* new_map_reg = TempRegister();
+ LOperand* temp_reg = TempRegister();
+ LTransitionElementsKind* result =
+ new LTransitionElementsKind(object, new_map_reg, temp_reg);
+ return DefineSameAsFirst(result);
+ } else {
+ LOperand* object = UseFixed(instr->object(), eax);
+ LOperand* fixed_object_reg = FixedTemp(edx);
+ LOperand* new_map_reg = FixedTemp(ebx);
+ LTransitionElementsKind* result =
+ new LTransitionElementsKind(object, new_map_reg, fixed_object_reg);
+ return MarkAsCall(DefineFixed(result, eax), instr);
+ }
+}
LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) {
=======================================
--- /branches/bleeding_edge/src/ia32/lithium-ia32.h Wed Oct 19 04:41:22 2011
+++ /branches/bleeding_edge/src/ia32/lithium-ia32.h Wed Oct 19 05:10:18 2011
@@ -156,6 +156,7 @@
V(ThisFunction) \
V(Throw) \
V(ToFastProperties) \
+ V(TransitionElementsKind) \
V(Typeof) \
V(TypeofIsAndBranch) \
V(UnaryMathOperation) \
@@ -1733,6 +1734,30 @@
};
+class LTransitionElementsKind: public LTemplateInstruction<1, 1, 2> {
+ public:
+ LTransitionElementsKind(LOperand* object,
+ LOperand* new_map_temp,
+ LOperand* temp_reg) {
+ inputs_[0] = object;
+ temps_[0] = new_map_temp;
+ temps_[1] = temp_reg;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(TransitionElementsKind,
+ "transition-elements-kind")
+ DECLARE_HYDROGEN_ACCESSOR(TransitionElementsKind)
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ LOperand* object() { return inputs_[0]; }
+ LOperand* new_map_reg() { return temps_[0]; }
+ LOperand* temp_reg() { return temps_[1]; }
+ Handle<Map> original_map() { return hydrogen()->original_map(); }
+ Handle<Map> transitioned_map() { return hydrogen()->transitioned_map(); }
+};
+
+
class LStringAdd: public LTemplateInstruction<1, 3, 0> {
public:
LStringAdd(LOperand* context, LOperand* left, LOperand* right) {
=======================================
--- /branches/bleeding_edge/src/ic.cc Wed Oct 19 05:04:16 2011
+++ /branches/bleeding_edge/src/ic.cc Wed Oct 19 05:10:18 2011
@@ -1719,49 +1719,6 @@
ElementsKind elements_kind) {
return KeyedStoreElementStub(is_js_array, elements_kind).TryGetCode();
}
-
-
-// If |map| is contained in |maps_list|, returns |map|; otherwise returns
NULL.
-Map* GetMapIfPresent(Map* map, MapList* maps_list) {
- for (int i = 0; i < maps_list->length(); ++i) {
- if (maps_list->at(i) == map) return map;
- }
- return NULL;
-}
-
-
-// Returns the most generic transitioned map for |map| that's found in
-// |maps_list|, or NULL if no transitioned map for |map| is found at all.
-Map* GetTransitionedMap(Map* map, MapList* maps_list) {
- ElementsKind elements_kind = map->elements_kind();
- if (elements_kind == FAST_ELEMENTS) {
- return NULL;
- }
- if (elements_kind == FAST_DOUBLE_ELEMENTS) {
- bool dummy = true;
- Map* fast_map = map->LookupElementsTransitionMap(FAST_ELEMENTS,
&dummy);
- if (fast_map == NULL) return NULL;
- return GetMapIfPresent(fast_map, maps_list);
- }
- if (elements_kind == FAST_SMI_ONLY_ELEMENTS) {
- bool dummy = true;
- Map* double_map =
map->LookupElementsTransitionMap(FAST_DOUBLE_ELEMENTS,
- &dummy);
- // In the current implementation, if the DOUBLE map doesn't exist, the
- // FAST map can't exist either.
- if (double_map == NULL) return NULL;
- Map* fast_map = map->LookupElementsTransitionMap(FAST_ELEMENTS,
&dummy);
- if (fast_map == NULL) {
- return GetMapIfPresent(double_map, maps_list);
- }
- // Both double_map and fast_map are non-NULL. Return fast_map if it's
in
- // maps_list, double_map otherwise.
- Map* fast_map_present = GetMapIfPresent(fast_map, maps_list);
- if (fast_map_present != NULL) return fast_map_present;
- return GetMapIfPresent(double_map, maps_list);
- }
- return NULL;
-}
MaybeObject* KeyedStoreIC::ComputePolymorphicStub(
@@ -1773,7 +1730,7 @@
for (int i = 0; i < receiver_maps->length(); ++i) {
Map* receiver_map(receiver_maps->at(i));
MaybeObject* maybe_cached_stub = NULL;
- Map* transitioned_map = GetTransitionedMap(receiver_map,
receiver_maps);
+ Map* transitioned_map =
receiver_map->FindTransitionedMap(receiver_maps);
if (transitioned_map != NULL) {
maybe_cached_stub = FastElementsConversionStub(
receiver_map->elements_kind(), // original elements_kind
=======================================
--- /branches/bleeding_edge/src/ic.h Wed Oct 19 05:04:16 2011
+++ /branches/bleeding_edge/src/ic.h Wed Oct 19 05:10:18 2011
@@ -569,6 +569,8 @@
StrictModeFlag strict_mode);
static void GenerateGeneric(MacroAssembler* masm, StrictModeFlag
strict_mode);
static void GenerateNonStrictArguments(MacroAssembler* masm);
+ static void GenerateTransitionElementsSmiToDouble(MacroAssembler* masm);
+ static void GenerateTransitionElementsDoubleToObject(MacroAssembler*
masm);
virtual MaybeObject* GetElementStubWithoutMapCheck(
bool is_js_array,
=======================================
--- /branches/bleeding_edge/src/objects.cc Wed Oct 19 05:04:16 2011
+++ /branches/bleeding_edge/src/objects.cc Wed Oct 19 05:10:18 2011
@@ -2106,6 +2106,39 @@
result->NotFound();
}
}
+
+
+// If |map| is contained in |maps_list|, returns |map|; otherwise returns
NULL.
+static bool ContainsMap(MapList* maps_list, Map* map) {
+ for (int i = 0; i < maps_list->length(); ++i) {
+ if (maps_list->at(i) == map) return true;
+ }
+ return false;
+}
+
+
+Map* Map::FindTransitionedMap(MapList* candidates) {
+ ElementsKind elms_kind = elements_kind();
+ if (elms_kind == FAST_DOUBLE_ELEMENTS) {
+ bool dummy = true;
+ Map* fast_map = LookupElementsTransitionMap(FAST_ELEMENTS, &dummy);
+ if (fast_map == NULL) return NULL;
+ if (ContainsMap(candidates, fast_map)) return fast_map;
+ return NULL;
+ }
+ if (elms_kind == FAST_SMI_ONLY_ELEMENTS) {
+ bool dummy = true;
+ Map* double_map = LookupElementsTransitionMap(FAST_DOUBLE_ELEMENTS,
&dummy);
+ // In the current implementation, if the DOUBLE map doesn't exist, the
+ // FAST map can't exist either.
+ if (double_map == NULL) return NULL;
+ Map* fast_map = double_map->LookupElementsTransitionMap(FAST_ELEMENTS,
+ &dummy);
+ if (fast_map != NULL && ContainsMap(candidates, fast_map)) return
fast_map;
+ if (ContainsMap(candidates, double_map)) return double_map;
+ }
+ return NULL;
+}
static Map* GetElementsTransitionMapFromDescriptor(Object*
descriptor_contents,
@@ -9462,6 +9495,51 @@
UNREACHABLE();
return isolate->heap()->null_value();
}
+
+
+MUST_USE_RESULT MaybeObject* JSObject::TransitionElementsKind(
+ ElementsKind to_kind) {
+ ElementsKind from_kind = map()->elements_kind();
+ FixedArrayBase* elms = FixedArrayBase::cast(elements());
+ uint32_t capacity = static_cast<uint32_t>(elms->length());
+ uint32_t length = capacity;
+ if (IsJSArray()) {
+ CHECK(JSArray::cast(this)->length()->ToArrayIndex(&length));
+ }
+ if (from_kind == FAST_SMI_ONLY_ELEMENTS) {
+ if (to_kind == FAST_DOUBLE_ELEMENTS) {
+ MaybeObject* maybe_result =
+ SetFastDoubleElementsCapacityAndLength(capacity, length);
+ if (maybe_result->IsFailure()) return maybe_result;
+ return this;
+ } else if (to_kind == FAST_ELEMENTS) {
+ MaybeObject* maybe_new_map = GetElementsTransitionMap(FAST_ELEMENTS);
+ Map* new_map;
+ if (!maybe_new_map->To(&new_map)) return maybe_new_map;
+ set_map(new_map);
+ return this;
+ }
+ } else if (from_kind == FAST_DOUBLE_ELEMENTS && to_kind ==
FAST_ELEMENTS) {
+ MaybeObject* maybe_result = SetFastElementsCapacityAndLength(
+ capacity, length, kDontAllowSmiOnlyElements);
+ if (maybe_result->IsFailure()) return maybe_result;
+ return this;
+ }
+ // This method should never be called for any other case than the ones
+ // handled above.
+ UNREACHABLE();
+ return GetIsolate()->heap()->null_value();
+}
+
+
+// static
+bool Map::IsValidElementsTransition(ElementsKind from_kind,
+ ElementsKind to_kind) {
+ return
+ (from_kind == FAST_SMI_ONLY_ELEMENTS &&
+ (to_kind == FAST_DOUBLE_ELEMENTS || to_kind == FAST_ELEMENTS)) ||
+ (from_kind == FAST_DOUBLE_ELEMENTS && to_kind == FAST_ELEMENTS);
+}
MaybeObject* JSArray::JSArrayUpdateLengthFromIndex(uint32_t index,
=======================================
--- /branches/bleeding_edge/src/objects.h Wed Oct 19 05:04:16 2011
+++ /branches/bleeding_edge/src/objects.h Wed Oct 19 05:10:18 2011
@@ -1798,6 +1798,8 @@
MUST_USE_RESULT MaybeObject* GetElementsTransitionMap(
ElementsKind elements_kind);
+ MUST_USE_RESULT MaybeObject* TransitionElementsKind(ElementsKind
to_kind);
+
// Converts a descriptor of any other type to a real field,
// backed by the properties array. Descriptors of visible
// types, such as CONSTANT_FUNCTION, keep their enumeration order.
@@ -4150,6 +4152,9 @@
inline bool has_dictionary_elements() {
return elements_kind() == DICTIONARY_ELEMENTS;
}
+
+ static bool IsValidElementsTransition(ElementsKind from_kind,
+ ElementsKind to_kind);
// Tells whether the map is attached to SharedFunctionInfo
// (for inobject slack tracking).
@@ -4317,6 +4322,11 @@
MaybeObject* AddElementsTransition(ElementsKind elements_kind,
Map* transitioned_map);
+ // Returns the transitioned map for this map with the most generic
+ // elements_kind that's found in |candidates|, or NULL if no match is
+ // found at all.
+ Map* FindTransitionedMap(MapList* candidates);
+
// Dispatched behavior.
#ifdef OBJECT_PRINT
inline void MapPrint() {
=======================================
--- /branches/bleeding_edge/src/runtime.cc Wed Oct 19 05:04:16 2011
+++ /branches/bleeding_edge/src/runtime.cc Wed Oct 19 05:10:18 2011
@@ -4534,6 +4534,39 @@
attributes,
strict_mode);
}
+
+
+MaybeObject* TransitionElements(Handle<Object> object,
+ ElementsKind to_kind,
+ Isolate* isolate) {
+ HandleScope scope(isolate);
+ if (!object->IsJSObject()) return isolate->ThrowIllegalOperation();
+ ElementsKind from_kind =
+ Handle<JSObject>::cast(object)->map()->elements_kind();
+ if (Map::IsValidElementsTransition(from_kind, to_kind)) {
+ Handle<Object> result =
+ TransitionElementsKind(Handle<JSObject>::cast(object), to_kind);
+ if (result.is_null()) return isolate->ThrowIllegalOperation();
+ return *result;
+ }
+ return isolate->ThrowIllegalOperation();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_TransitionElementsSmiToDouble) {
+ NoHandleAllocation ha;
+ RUNTIME_ASSERT(args.length() == 1);
+ Handle<Object> object = args.at<Object>(0);
+ return TransitionElements(object, FAST_DOUBLE_ELEMENTS, isolate);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_TransitionElementsDoubleToObject) {
+ NoHandleAllocation ha;
+ RUNTIME_ASSERT(args.length() == 1);
+ Handle<Object> object = args.at<Object>(0);
+ return TransitionElements(object, FAST_ELEMENTS, isolate);
+}
// Set the native flag on the function.
=======================================
--- /branches/bleeding_edge/src/runtime.h Wed Oct 19 05:04:16 2011
+++ /branches/bleeding_edge/src/runtime.h Wed Oct 19 05:10:18 2011
@@ -368,6 +368,8 @@
F(HasExternalUnsignedIntElements, 1, 1) \
F(HasExternalFloatElements, 1, 1) \
F(HasExternalDoubleElements, 1, 1) \
+ F(TransitionElementsSmiToDouble, 1, 1) \
+ F(TransitionElementsDoubleToObject, 1, 1) \
F(HaveSameMap, 2, 1) \
/* profiler */ \
F(ProfilerResume, 0, 1) \
=======================================
--- /branches/bleeding_edge/src/x64/ic-x64.cc Mon Oct 17 03:44:47 2011
+++ /branches/bleeding_edge/src/x64/ic-x64.cc Wed Oct 19 05:10:18 2011
@@ -1599,6 +1599,37 @@
: ExternalReference(IC_Utility(kKeyedStoreIC_Miss), masm->isolate());
__ TailCallExternalReference(ref, 3, 1);
}
+
+
+void KeyedStoreIC::GenerateTransitionElementsSmiToDouble(MacroAssembler*
masm) {
+ // ----------- S t a t e -------------
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+ // Must return the modified receiver in eax.
+
+ __ pop(rbx);
+ __ push(rdx);
+ __ push(rbx); // return address
+
+ __ TailCallRuntime(Runtime::kTransitionElementsSmiToDouble, 1, 1);
+}
+
+
+void KeyedStoreIC::GenerateTransitionElementsDoubleToObject(
+ MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+ // Must return the modified receiver in eax.
+
+ __ pop(rbx);
+ __ push(rdx);
+ __ push(rbx); // return address
+
+ __ TailCallRuntime(Runtime::kTransitionElementsDoubleToObject, 1, 1);
+}
#undef __
=======================================
--- /branches/bleeding_edge/src/x64/lithium-codegen-x64.cc Wed Oct 19
04:41:22 2011
+++ /branches/bleeding_edge/src/x64/lithium-codegen-x64.cc Wed Oct 19
05:10:18 2011
@@ -3255,6 +3255,47 @@
: isolate()->builtins()->KeyedStoreIC_Initialize();
CallCode(ic, RelocInfo::CODE_TARGET, instr);
}
+
+
+void LCodeGen::DoTransitionElementsKind(LTransitionElementsKind* instr) {
+ Register object_reg = ToRegister(instr->object());
+ Register new_map_reg = ToRegister(instr->new_map_reg());
+
+ Handle<Map> from_map = instr->original_map();
+ Handle<Map> to_map = instr->transitioned_map();
+ ElementsKind from_kind = from_map->elements_kind();
+ ElementsKind to_kind = to_map->elements_kind();
+
+ Label not_applicable;
+ __ Cmp(FieldOperand(object_reg, HeapObject::kMapOffset), from_map);
+ __ j(not_equal, ¬_applicable);
+ __ movq(new_map_reg, to_map, RelocInfo::EMBEDDED_OBJECT);
+ if (from_kind == FAST_SMI_ONLY_ELEMENTS && to_kind == FAST_ELEMENTS) {
+ __ movq(FieldOperand(object_reg, HeapObject::kMapOffset), new_map_reg);
+ // Write barrier.
+ ASSERT_NE(instr->temp_reg(), NULL);
+ __ RecordWriteField(object_reg, HeapObject::kMapOffset, new_map_reg,
+ ToRegister(instr->temp_reg()), kDontSaveFPRegs);
+ } else if (from_kind == FAST_SMI_ONLY_ELEMENTS &&
+ to_kind == FAST_DOUBLE_ELEMENTS) {
+ Register fixed_object_reg = ToRegister(instr->temp_reg());
+ ASSERT(fixed_object_reg.is(rdx));
+ ASSERT(new_map_reg.is(rbx));
+ __ movq(fixed_object_reg, object_reg);
+ CallCode(isolate()->builtins()->TransitionElementsSmiToDouble(),
+ RelocInfo::CODE_TARGET, instr);
+ } else if (from_kind == FAST_DOUBLE_ELEMENTS && to_kind ==
FAST_ELEMENTS) {
+ Register fixed_object_reg = ToRegister(instr->temp_reg());
+ ASSERT(fixed_object_reg.is(rdx));
+ ASSERT(new_map_reg.is(rbx));
+ __ movq(fixed_object_reg, object_reg);
+ CallCode(isolate()->builtins()->TransitionElementsDoubleToObject(),
+ RelocInfo::CODE_TARGET, instr);
+ } else {
+ UNREACHABLE();
+ }
+ __ bind(¬_applicable);
+}
void LCodeGen::DoStringAdd(LStringAdd* instr) {
=======================================
--- /branches/bleeding_edge/src/x64/lithium-x64.cc Mon Oct 17 00:43:40 2011
+++ /branches/bleeding_edge/src/x64/lithium-x64.cc Wed Oct 19 05:10:18 2011
@@ -445,6 +445,12 @@
stream->Add("] <- ");
value()->PrintTo(stream);
}
+
+
+void LTransitionElementsKind::PrintDataTo(StringStream* stream) {
+ object()->PrintTo(stream);
+ stream->Add(" %p -> %p", *original_map(), *transitioned_map());
+}
void LChunk::AddInstruction(LInstruction* instr, HBasicBlock* block) {
@@ -1952,6 +1958,27 @@
LStoreKeyedGeneric* result = new LStoreKeyedGeneric(object, key, value);
return MarkAsCall(result, instr);
}
+
+
+LInstruction* LChunkBuilder::DoTransitionElementsKind(
+ HTransitionElementsKind* instr) {
+ if (instr->original_map()->elements_kind() == FAST_SMI_ONLY_ELEMENTS &&
+ instr->transitioned_map()->elements_kind() == FAST_ELEMENTS) {
+ LOperand* object = UseRegister(instr->object());
+ LOperand* new_map_reg = TempRegister();
+ LOperand* temp_reg = TempRegister();
+ LTransitionElementsKind* result =
+ new LTransitionElementsKind(object, new_map_reg, temp_reg);
+ return DefineSameAsFirst(result);
+ } else {
+ LOperand* object = UseFixed(instr->object(), rax);
+ LOperand* fixed_object_reg = FixedTemp(rdx);
+ LOperand* new_map_reg = FixedTemp(rbx);
+ LTransitionElementsKind* result =
+ new LTransitionElementsKind(object, new_map_reg, fixed_object_reg);
+ return MarkAsCall(DefineFixed(result, rax), instr);
+ }
+}
LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) {
=======================================
--- /branches/bleeding_edge/src/x64/lithium-x64.h Wed Oct 19 04:41:22 2011
+++ /branches/bleeding_edge/src/x64/lithium-x64.h Wed Oct 19 05:10:18 2011
@@ -162,6 +162,7 @@
V(ThisFunction) \
V(Throw) \
V(ToFastProperties) \
+ V(TransitionElementsKind) \
V(Typeof) \
V(TypeofIsAndBranch) \
V(UnaryMathOperation) \
@@ -1660,6 +1661,30 @@
};
+class LTransitionElementsKind: public LTemplateInstruction<1, 1, 2> {
+ public:
+ LTransitionElementsKind(LOperand* object,
+ LOperand* new_map_temp,
+ LOperand* temp_reg) {
+ inputs_[0] = object;
+ temps_[0] = new_map_temp;
+ temps_[1] = temp_reg;
+ }
+
+ DECLARE_CONCRETE_INSTRUCTION(TransitionElementsKind,
+ "transition-elements-kind")
+ DECLARE_HYDROGEN_ACCESSOR(TransitionElementsKind)
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ LOperand* object() { return inputs_[0]; }
+ LOperand* new_map_reg() { return temps_[0]; }
+ LOperand* temp_reg() { return temps_[1]; }
+ Handle<Map> original_map() { return hydrogen()->original_map(); }
+ Handle<Map> transitioned_map() { return hydrogen()->transitioned_map(); }
+};
+
+
class LStringAdd: public LTemplateInstruction<1, 2, 0> {
public:
LStringAdd(LOperand* left, LOperand* right) {
=======================================
--- /branches/bleeding_edge/test/mjsunit/elements-kind.js Wed Oct 19
04:36:55 2011
+++ /branches/bleeding_edge/test/mjsunit/elements-kind.js Wed Oct 19
05:10:18 2011
@@ -158,45 +158,92 @@
for (var i = 0; i < 3; i++) monomorphic(smi_only);
%OptimizeFunctionOnNextCall(monomorphic);
monomorphic(smi_only);
-function polymorphic(array, expected_kind) {
- array[1] = 42;
- assertKind(expected_kind, array);
- var a = array[1];
- assertEquals(42, a);
-}
-var smis = [1, 2, 3];
-var strings = [0, 0, 0]; strings[0] = "one";
-var doubles = [0, 0, 0]; doubles[0] = 1.5;
-assertKind(support_smi_only_arrays
- ? elements_kind.fast_double
- : elements_kind.fast,
- doubles);
-for (var i = 0; i < 3; i++) {
- polymorphic(smis, elements_kind.fast_smi_only);
+
+if (support_smi_only_arrays) {
+ function construct_smis() {
+ var a = [0, 0, 0];
+ a[0] = 0; // Send the COW array map to the steak house.
+ assertKind(elements_kind.fast_smi_only, a);
+ return a;
+ }
+ function construct_doubles() {
+ var a = construct_smis();
+ a[0] = 1.5;
+ assertKind(elements_kind.fast_double, a);
+ return a;
+ }
+ function construct_objects() {
+ var a = construct_smis();
+ a[0] = "one";
+ assertKind(elements_kind.fast, a);
+ return a;
+ }
+
+ // Test crankshafted transition SMI->DOUBLE.
+ function convert_to_double(array) {
+ array[1] = 2.5;
+ assertKind(elements_kind.fast_double, array);
+ assertEquals(2.5, array[1]);
+ }
+ var smis = construct_smis();
+ for (var i = 0; i < 3; i++) convert_to_double(smis);
+ %OptimizeFunctionOnNextCall(convert_to_double);
+ smis = construct_smis();
+ convert_to_double(smis);
+ // Test crankshafted transitions SMI->FAST and DOUBLE->FAST.
+ function convert_to_fast(array) {
+ array[1] = "two";
+ assertKind(elements_kind.fast, array);
+ assertEquals("two", array[1]);
+ }
+ smis = construct_smis();
+ for (var i = 0; i < 3; i++) convert_to_fast(smis);
+ var doubles = construct_doubles();
+ for (var i = 0; i < 3; i++) convert_to_fast(doubles);
+ smis = construct_smis();
+ doubles = construct_doubles();
+ %OptimizeFunctionOnNextCall(convert_to_fast);
+ convert_to_fast(smis);
+ convert_to_fast(doubles);
+ // Test transition chain SMI->DOUBLE->FAST (crankshafted function will
+ // transition to FAST directly).
+ function convert_mixed(array, value, kind) {
+ array[1] = value;
+ assertKind(kind, array);
+ assertEquals(value, array[1]);
+ }
+ smis = construct_smis();
+ for (var i = 0; i < 3; i++) {
+ convert_mixed(smis, 1.5, elements_kind.fast_double);
+ }
+ doubles = construct_doubles();
+ for (var i = 0; i < 3; i++) {
+ convert_mixed(doubles, "three", elements_kind.fast);
+ }
+ smis = construct_smis();
+ doubles = construct_doubles();
+ %OptimizeFunctionOnNextCall(convert_mixed);
+ convert_mixed(smis, 1, elements_kind.fast);
+ convert_mixed(doubles, 1, elements_kind.fast);
+ assertTrue(%HaveSameMap(smis, doubles));
+}
+
+// Crankshaft support for smi-only elements in dynamic array literals.
+function get(foo) { return foo; } // Used to generate dynamic values.
+
+function crankshaft_test() {
+ var a = [get(1), get(2), get(3)];
+ assertKind(elements_kind.fast_smi_only, a);
+ var b = [get(1), get(2), get("three")];
+ assertKind(elements_kind.fast, b);
+ var c = [get(1), get(2), get(3.5)];
+ assertKind(elements_kind.fast_double, c);
}
for (var i = 0; i < 3; i++) {
- polymorphic(strings, elements_kind.fast);
-}
-/* In the first iteration, feeding polymorphic with a fast double elements
- * array leads to a miss and is then routed to runtime code. No conversion
- * is done in there. The second time the store is handled by the newly
- * created IC, which converts the fast double elements into fast elements
- * since arrays with fast elements have been handled earlier in
polymorphic.
- * Since the x64 and arm port of the generated code conversion does not yet
- * exist, this test is skipped for now.
-for (var i = 0; i < 3; i++) {
- polymorphic(doubles, i == 0 && support_smi_only_arrays
- ? elements_kind.fast_double
- : elements_kind.fast);
-}
-*/
-
-/* Element transitions have not been implemented in crankshaft yet.
-%OptimizeFunctionOnNextCall(polymorphic);
-polymorphic(smis, elements_kind.fast_smi_only);
-polymorphic(strings, elements_kind.fast);
-polymorphic(doubles, elements_kind.fast);
-*/
+ crankshaft_test();
+}
+%OptimizeFunctionOnNextCall(crankshaft_test);
+crankshaft_test();
// Elements_kind transitions for arrays.
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev