}
+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,
StrictModeFlag strict_mode) {
// ---------- S t a t e --------------
}
+void LTransitionElementsKind::PrintDataTo(StringStream* stream) {
+ object()->PrintTo(stream);
+ stream->Add(" %p -> %p", *original_map(), *transitioned_map());
+}
+
+
LChunk::LChunk(CompilationInfo* info, HGraph* graph)
: spill_slot_count_(0),
info_(info),
}
+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) {
bool needs_write_barrier = instr->NeedsWriteBarrier();
V(ThisFunction) \
V(Throw) \
V(ToFastProperties) \
+ V(TransitionElementsKind) \
V(Typeof) \
V(TypeofIsAndBranch) \
V(UnaryMathOperation) \
};
+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) {
}
+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) {
__ push(ToRegister(instr->left()));
__ push(ToRegister(instr->right()));
__ 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));
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) {
Debug::GenerateLoadICDebugBreak(masm);
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, \
}
+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) {
Isolate* isolate = obj->GetIsolate();
CALL_HEAP_FUNCTION(isolate,
Handle<Object> value,
StrictModeFlag strict_mode);
+Handle<Object> TransitionElementsKind(Handle<JSObject> object,
+ ElementsKind to_kind);
+
Handle<Object> GetProperty(Handle<JSReceiver> obj,
const char* name);
}
+void HTransitionElementsKind::PrintDataTo(StringStream* stream) {
+ object()->PrintNameTo(stream);
+ stream->Add(" %p -> %p", *original_map(), *transitioned_map());
+}
+
+
void HLoadGlobalCell::PrintDataTo(StringStream* stream) {
stream->Add("[%p]", *cell());
if (!details_.IsDontDelete()) stream->Add(" (deleteable)");
V(Throw) \
V(ToFastProperties) \
V(ToInt32) \
+ V(TransitionElementsKind) \
V(Typeof) \
V(TypeofIsAndBranch) \
V(UnaryMathOperation) \
};
+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)
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;
+ }
}
}
}
+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 __
}
+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) {
class DeferredStringCharCodeAt: public LDeferredCode {
public:
}
+void LTransitionElementsKind::PrintDataTo(StringStream* stream) {
+ object()->PrintTo(stream);
+ stream->Add(" %p -> %p", *original_map(), *transitioned_map());
+}
+
+
void LChunk::AddInstruction(LInstruction* instr, HBasicBlock* block) {
LInstructionGap* gap = new LInstructionGap(block);
int index = -1;
}
+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) {
bool needs_write_barrier = instr->NeedsWriteBarrier();
V(ThisFunction) \
V(Throw) \
V(ToFastProperties) \
+ V(TransitionElementsKind) \
V(Typeof) \
V(TypeofIsAndBranch) \
V(UnaryMathOperation) \
};
+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) {
}
-// 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(
MapList* receiver_maps,
StrictModeFlag strict_mode) {
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
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,
}
+// 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,
ElementsKind elements_kind) {
if (descriptor_contents->IsMap()) {
}
+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,
Object* value) {
uint32_t old_len = 0;
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.
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).
inline void set_attached_to_shared_function_info(bool value);
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() {
}
+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.
// This is used to decide if we should transform null and undefined
// into the global object when doing call and apply.
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) \
}
+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 __
}
+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) {
EmitPushTaggedOperand(instr->left());
EmitPushTaggedOperand(instr->right());
}
+void LTransitionElementsKind::PrintDataTo(StringStream* stream) {
+ object()->PrintTo(stream);
+ stream->Add(" %p -> %p", *original_map(), *transitioned_map());
+}
+
+
void LChunk::AddInstruction(LInstruction* instr, HBasicBlock* block) {
LInstructionGap* gap = new LInstructionGap(block);
int index = -1;
}
+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) {
bool needs_write_barrier = instr->NeedsWriteBarrier();
V(ThisFunction) \
V(Throw) \
V(ToFastProperties) \
+ V(TransitionElementsKind) \
V(Typeof) \
V(TypeofIsAndBranch) \
V(UnaryMathOperation) \
};
+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) {
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));
}
-for (var i = 0; i < 3; i++) {
- polymorphic(strings, elements_kind.fast);
+
+// 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);
}
-/* 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);
+ crankshaft_test();
}
-*/
-
-/* 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);
-*/
+%OptimizeFunctionOnNextCall(crankshaft_test);
+crankshaft_test();
// Elements_kind transitions for arrays.