+2010-06-16: Version 2.2.18
+
+ Added API functions to retrieve information on indexed properties
+ managed by the embedding layer. Fixes bug 737.
+
+ Make ES5 Object.defineProperty support array elements. Fixes bug 619.
+
+ Add heap profiling to the API.
+
+ Remove old named property query from the API.
+
+ Incremental performance improvements.
+
+
2010-06-14: Version 2.2.17
Improved debugger support for stepping out of functions.
};
+class HeapGraphNode;
+
+
+/**
+ * HeapSnapshotEdge represents a directed connection between heap
+ * graph nodes: from retaners to retained nodes.
+ */
+class V8EXPORT HeapGraphEdge {
+ public:
+ enum Type {
+ CONTEXT_VARIABLE = 0, // A variable from a function context.
+ ELEMENT = 1, // An element of an array.
+ PROPERTY = 2 // A named object property.
+ };
+
+ /** Returns edge type (see HeapGraphEdge::Type). */
+ Type GetType() const;
+
+ /**
+ * Returns edge name. This can be a variable name, an element index, or
+ * a property name.
+ */
+ Handle<Value> GetName() const;
+
+ /** Returns origin node. */
+ const HeapGraphNode* GetFromNode() const;
+
+ /** Returns destination node. */
+ const HeapGraphNode* GetToNode() const;
+};
+
+
+class V8EXPORT HeapGraphPath {
+ public:
+ /** Returns the number of edges in the path. */
+ int GetEdgesCount() const;
+
+ /** Returns an edge from the path. */
+ const HeapGraphEdge* GetEdge(int index) const;
+
+ /** Returns origin node. */
+ const HeapGraphNode* GetFromNode() const;
+
+ /** Returns destination node. */
+ const HeapGraphNode* GetToNode() const;
+};
+
+
+/**
+ * HeapGraphNode represents a node in a heap graph.
+ */
+class V8EXPORT HeapGraphNode {
+ public:
+ enum Type {
+ INTERNAL = 0, // Internal node, a virtual one, for housekeeping.
+ ARRAY = 1, // An array of elements.
+ STRING = 2, // A string.
+ OBJECT = 3, // A JS object (except for arrays and strings).
+ CODE = 4, // Compiled code.
+ CLOSURE = 5 // Function closure.
+ };
+
+ /** Returns node type (see HeapGraphNode::Type). */
+ Type GetType() const;
+
+ /**
+ * Returns node name. Depending on node's type this can be the name
+ * of the constructor (for objects), the name of the function (for
+ * closures), string value, or an empty string (for compiled code).
+ */
+ Handle<String> GetName() const;
+
+ /** Returns node's own size, in bytes. */
+ int GetSelfSize() const;
+
+ /** Returns node's network (self + reachable nodes) size, in bytes. */
+ int GetTotalSize() const;
+
+ /**
+ * Returns node's private size, in bytes. That is, the size of memory
+ * that will be reclaimed having this node collected.
+ */
+ int GetPrivateSize() const;
+
+ /** Returns child nodes count of the node. */
+ int GetChildrenCount() const;
+
+ /** Retrieves a child by index. */
+ const HeapGraphEdge* GetChild(int index) const;
+
+ /** Returns retainer nodes count of the node. */
+ int GetRetainersCount() const;
+
+ /** Returns a retainer by index. */
+ const HeapGraphEdge* GetRetainer(int index) const;
+
+ /** Returns the number of simple retaining paths from the root to the node. */
+ int GetRetainingPathsCount() const;
+
+ /** Returns a retaining path by index. */
+ const HeapGraphPath* GetRetainingPath(int index) const;
+};
+
+
+/**
+ * HeapSnapshots record the state of the JS heap at some moment.
+ */
+class V8EXPORT HeapSnapshot {
+ public:
+ /** Returns heap snapshot UID (assigned by the profiler.) */
+ unsigned GetUid() const;
+
+ /** Returns heap snapshot title. */
+ Handle<String> GetTitle() const;
+
+ /** Returns the root node of the heap graph. */
+ const HeapGraphNode* GetHead() const;
+};
+
+
+/**
+ * Interface for controlling heap profiling.
+ */
+class V8EXPORT HeapProfiler {
+ public:
+ /** Returns the number of snapshots taken. */
+ static int GetSnapshotsCount();
+
+ /** Returns a snapshot by index. */
+ static const HeapSnapshot* GetSnapshot(int index);
+
+ /** Returns a profile by uid. */
+ static const HeapSnapshot* FindSnapshot(unsigned uid);
+
+ /** Takes a heap snapshot and returns it. Title may be an empty string. */
+ static const HeapSnapshot* TakeSnapshot(Handle<String> title);
+};
+
+
} // namespace v8
* the backing store is preserved while V8 has a reference.
*/
void SetIndexedPropertiesToPixelData(uint8_t* data, int length);
+ bool HasIndexedPropertiesInPixelData();
+ uint8_t* GetIndexedPropertiesPixelData();
+ int GetIndexedPropertiesPixelDataLength();
/**
* Set the backing store of the indexed properties to be managed by the
void SetIndexedPropertiesToExternalArrayData(void* data,
ExternalArrayType array_type,
int number_of_elements);
+ bool HasIndexedPropertiesInExternalArrayData();
+ void* GetIndexedPropertiesExternalArrayData();
+ ExternalArrayType GetIndexedPropertiesExternalArrayDataType();
+ int GetIndexedPropertiesExternalArrayDataLength();
static Local<Object> New();
static inline Object* Cast(Value* obj);
/**
* Returns a non-empty handle if the interceptor intercepts the request.
- * The result is either boolean (true if property exists and false
- * otherwise) or an integer encoding property attributes.
+ * The result is an integer encoding property attributes (like v8::None,
+ * v8::DontEnum, etc.)
*/
-#ifdef USE_NEW_QUERY_CALLBACKS
typedef Handle<Integer> (*NamedPropertyQuery)(Local<String> property,
const AccessorInfo& info);
-#else
-typedef Handle<Boolean> (*NamedPropertyQuery)(Local<String> property,
- const AccessorInfo& info);
-#endif
-
-typedef Handle<Value> (*NamedPropertyQueryImpl)(Local<String> property,
- const AccessorInfo& info);
-
/**
NamedPropertyQuery query,
NamedPropertyDeleter remover,
NamedPropertyEnumerator enumerator,
- Handle<Value> data) {
- NamedPropertyQueryImpl casted =
- reinterpret_cast<NamedPropertyQueryImpl>(query);
- SetNamedInstancePropertyHandlerImpl(getter,
- setter,
- casted,
- remover,
- enumerator,
- data);
- }
+ Handle<Value> data);
void SetIndexedInstancePropertyHandler(IndexedPropertyGetter getter,
IndexedPropertySetter setter,
IndexedPropertyQuery query,
friend class Context;
friend class ObjectTemplate;
- private:
- void SetNamedInstancePropertyHandlerImpl(NamedPropertyGetter getter,
- NamedPropertySetter setter,
- NamedPropertyQueryImpl query,
- NamedPropertyDeleter remover,
- NamedPropertyEnumerator enumerator,
- Handle<Value> data);
};
*
* \param getter The callback to invoke when getting a property.
* \param setter The callback to invoke when setting a property.
- * \param query The callback to invoke to check if an object has a property.
+ * \param query The callback to invoke to check if a property is present,
+ * and if present, get its attributes.
* \param deleter The callback to invoke when deleting a property.
* \param enumerator The callback to invoke to enumerate all the named
* properties of an object.
NamedPropertyQuery query = 0,
NamedPropertyDeleter deleter = 0,
NamedPropertyEnumerator enumerator = 0,
- Handle<Value> data = Handle<Value>()) {
- NamedPropertyQueryImpl casted =
- reinterpret_cast<NamedPropertyQueryImpl>(query);
- SetNamedPropertyHandlerImpl(getter,
- setter,
- casted,
- deleter,
- enumerator,
- data);
- }
-
- private:
- void SetNamedPropertyHandlerImpl(NamedPropertyGetter getter,
- NamedPropertySetter setter,
- NamedPropertyQueryImpl query,
- NamedPropertyDeleter deleter,
- NamedPropertyEnumerator enumerator,
- Handle<Value> data);
-
- public:
+ Handle<Value> data = Handle<Value>());
/**
* Sets an indexed property handler on the object template.
#include "debug.h"
#include "execution.h"
#include "global-handles.h"
+#include "heap-profiler.h"
#include "messages.h"
#include "platform.h"
#include "profile-generator-inl.h"
}
-void FunctionTemplate::SetNamedInstancePropertyHandlerImpl(
+void FunctionTemplate::SetNamedInstancePropertyHandler(
NamedPropertyGetter getter,
NamedPropertySetter setter,
- NamedPropertyQueryImpl query,
+ NamedPropertyQuery query,
NamedPropertyDeleter remover,
NamedPropertyEnumerator enumerator,
Handle<Value> data) {
}
-void ObjectTemplate::SetNamedPropertyHandlerImpl(NamedPropertyGetter getter,
- NamedPropertySetter setter,
- NamedPropertyQueryImpl query,
- NamedPropertyDeleter remover,
- NamedPropertyEnumerator
- enumerator,
- Handle<Value> data) {
+void ObjectTemplate::SetNamedPropertyHandler(NamedPropertyGetter getter,
+ NamedPropertySetter setter,
+ NamedPropertyQuery query,
+ NamedPropertyDeleter remover,
+ NamedPropertyEnumerator enumerator,
+ Handle<Value> data) {
if (IsDeadCheck("v8::ObjectTemplate::SetNamedPropertyHandler()")) return;
ENTER_V8;
HandleScope scope;
i::FunctionTemplateInfo* constructor =
i::FunctionTemplateInfo::cast(Utils::OpenHandle(this)->constructor());
i::Handle<i::FunctionTemplateInfo> cons(constructor);
- Utils::ToLocal(cons)->SetNamedInstancePropertyHandlerImpl(getter,
- setter,
- query,
- remover,
- enumerator,
- data);
+ Utils::ToLocal(cons)->SetNamedInstancePropertyHandler(getter,
+ setter,
+ query,
+ remover,
+ enumerator,
+ data);
}
}
+bool v8::Object::HasIndexedPropertiesInPixelData() {
+ ON_BAILOUT("v8::HasIndexedPropertiesInPixelData()", return false);
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ return self->HasPixelElements();
+}
+
+
+uint8_t* v8::Object::GetIndexedPropertiesPixelData() {
+ ON_BAILOUT("v8::GetIndexedPropertiesPixelData()", return NULL);
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ if (self->HasPixelElements()) {
+ return i::PixelArray::cast(self->elements())->external_pointer();
+ } else {
+ return NULL;
+ }
+}
+
+
+int v8::Object::GetIndexedPropertiesPixelDataLength() {
+ ON_BAILOUT("v8::GetIndexedPropertiesPixelDataLength()", return -1);
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ if (self->HasPixelElements()) {
+ return i::PixelArray::cast(self->elements())->length();
+ } else {
+ return -1;
+ }
+}
+
+
void v8::Object::SetIndexedPropertiesToExternalArrayData(
void* data,
ExternalArrayType array_type,
}
+bool v8::Object::HasIndexedPropertiesInExternalArrayData() {
+ ON_BAILOUT("v8::HasIndexedPropertiesInExternalArrayData()", return false);
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ return self->HasExternalArrayElements();
+}
+
+
+void* v8::Object::GetIndexedPropertiesExternalArrayData() {
+ ON_BAILOUT("v8::GetIndexedPropertiesExternalArrayData()", return NULL);
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ if (self->HasExternalArrayElements()) {
+ return i::ExternalArray::cast(self->elements())->external_pointer();
+ } else {
+ return NULL;
+ }
+}
+
+
+ExternalArrayType v8::Object::GetIndexedPropertiesExternalArrayDataType() {
+ ON_BAILOUT("v8::GetIndexedPropertiesExternalArrayDataType()",
+ return static_cast<ExternalArrayType>(-1));
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ switch (self->elements()->map()->instance_type()) {
+ case i::EXTERNAL_BYTE_ARRAY_TYPE:
+ return kExternalByteArray;
+ case i::EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE:
+ return kExternalUnsignedByteArray;
+ case i::EXTERNAL_SHORT_ARRAY_TYPE:
+ return kExternalShortArray;
+ case i::EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE:
+ return kExternalUnsignedShortArray;
+ case i::EXTERNAL_INT_ARRAY_TYPE:
+ return kExternalIntArray;
+ case i::EXTERNAL_UNSIGNED_INT_ARRAY_TYPE:
+ return kExternalUnsignedIntArray;
+ case i::EXTERNAL_FLOAT_ARRAY_TYPE:
+ return kExternalFloatArray;
+ default:
+ return static_cast<ExternalArrayType>(-1);
+ }
+}
+
+
+int v8::Object::GetIndexedPropertiesExternalArrayDataLength() {
+ ON_BAILOUT("v8::GetIndexedPropertiesExternalArrayDataLength()", return 0);
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ if (self->HasExternalArrayElements()) {
+ return i::ExternalArray::cast(self->elements())->length();
+ } else {
+ return -1;
+ }
+}
+
+
Local<v8::Object> Function::NewInstance() const {
return NewInstance(0, NULL);
}
*Utils::OpenHandle(*title)));
}
+
+HeapGraphEdge::Type HeapGraphEdge::GetType() const {
+ IsDeadCheck("v8::HeapGraphEdge::GetType");
+ return static_cast<HeapGraphEdge::Type>(
+ reinterpret_cast<const i::HeapGraphEdge*>(this)->type());
+}
+
+
+Handle<Value> HeapGraphEdge::GetName() const {
+ IsDeadCheck("v8::HeapGraphEdge::GetName");
+ const i::HeapGraphEdge* edge =
+ reinterpret_cast<const i::HeapGraphEdge*>(this);
+ switch (edge->type()) {
+ case i::HeapGraphEdge::CONTEXT_VARIABLE:
+ case i::HeapGraphEdge::PROPERTY:
+ return Handle<String>(ToApi<String>(i::Factory::LookupAsciiSymbol(
+ edge->name())));
+ case i::HeapGraphEdge::ELEMENT:
+ return Handle<Number>(ToApi<Number>(i::Factory::NewNumberFromInt(
+ edge->index())));
+ default: UNREACHABLE();
+ }
+ return ImplementationUtilities::Undefined();
+}
+
+
+const HeapGraphNode* HeapGraphEdge::GetFromNode() const {
+ IsDeadCheck("v8::HeapGraphEdge::GetFromNode");
+ const i::HeapEntry* from =
+ reinterpret_cast<const i::HeapGraphEdge*>(this)->from();
+ return reinterpret_cast<const HeapGraphNode*>(from);
+}
+
+
+const HeapGraphNode* HeapGraphEdge::GetToNode() const {
+ IsDeadCheck("v8::HeapGraphEdge::GetToNode");
+ const i::HeapEntry* to =
+ reinterpret_cast<const i::HeapGraphEdge*>(this)->to();
+ return reinterpret_cast<const HeapGraphNode*>(to);
+}
+
+
+int HeapGraphPath::GetEdgesCount() const {
+ return reinterpret_cast<const i::HeapGraphPath*>(this)->path()->length();
+}
+
+
+const HeapGraphEdge* HeapGraphPath::GetEdge(int index) const {
+ return reinterpret_cast<const HeapGraphEdge*>(
+ reinterpret_cast<const i::HeapGraphPath*>(this)->path()->at(index));
+}
+
+
+const HeapGraphNode* HeapGraphPath::GetFromNode() const {
+ return GetEdgesCount() > 0 ? GetEdge(0)->GetFromNode() : NULL;
+}
+
+
+const HeapGraphNode* HeapGraphPath::GetToNode() const {
+ const int count = GetEdgesCount();
+ return count > 0 ? GetEdge(count - 1)->GetToNode() : NULL;
+}
+
+
+HeapGraphNode::Type HeapGraphNode::GetType() const {
+ IsDeadCheck("v8::HeapGraphNode::GetType");
+ return static_cast<HeapGraphNode::Type>(
+ reinterpret_cast<const i::HeapEntry*>(this)->type());
+}
+
+
+Handle<String> HeapGraphNode::GetName() const {
+ IsDeadCheck("v8::HeapGraphNode::GetName");
+ return Handle<String>(ToApi<String>(i::Factory::LookupAsciiSymbol(
+ reinterpret_cast<const i::HeapEntry*>(this)->name())));
+}
+
+
+int HeapGraphNode::GetSelfSize() const {
+ IsDeadCheck("v8::HeapGraphNode::GetSelfSize");
+ return reinterpret_cast<const i::HeapEntry*>(this)->self_size();
+}
+
+
+int HeapGraphNode::GetTotalSize() const {
+ IsDeadCheck("v8::HeapSnapshot::GetHead");
+ return const_cast<i::HeapEntry*>(
+ reinterpret_cast<const i::HeapEntry*>(this))->TotalSize();
+}
+
+
+int HeapGraphNode::GetPrivateSize() const {
+ IsDeadCheck("v8::HeapSnapshot::GetPrivateSize");
+ return const_cast<i::HeapEntry*>(
+ reinterpret_cast<const i::HeapEntry*>(this))->NonSharedTotalSize();
+}
+
+
+int HeapGraphNode::GetChildrenCount() const {
+ IsDeadCheck("v8::HeapSnapshot::GetChildrenCount");
+ return reinterpret_cast<const i::HeapEntry*>(this)->children()->length();
+}
+
+
+const HeapGraphEdge* HeapGraphNode::GetChild(int index) const {
+ IsDeadCheck("v8::HeapSnapshot::GetChild");
+ return reinterpret_cast<const HeapGraphEdge*>(
+ reinterpret_cast<const i::HeapEntry*>(this)->children()->at(index));
+}
+
+
+int HeapGraphNode::GetRetainersCount() const {
+ IsDeadCheck("v8::HeapSnapshot::GetRetainersCount");
+ return reinterpret_cast<const i::HeapEntry*>(this)->retainers()->length();
+}
+
+
+const HeapGraphEdge* HeapGraphNode::GetRetainer(int index) const {
+ IsDeadCheck("v8::HeapSnapshot::GetRetainer");
+ return reinterpret_cast<const HeapGraphEdge*>(
+ reinterpret_cast<const i::HeapEntry*>(this)->retainers()->at(index));
+}
+
+
+int HeapGraphNode::GetRetainingPathsCount() const {
+ IsDeadCheck("v8::HeapSnapshot::GetRetainingPathsCount");
+ return const_cast<i::HeapEntry*>(
+ reinterpret_cast<const i::HeapEntry*>(
+ this))->GetRetainingPaths()->length();
+}
+
+
+const HeapGraphPath* HeapGraphNode::GetRetainingPath(int index) const {
+ IsDeadCheck("v8::HeapSnapshot::GetRetainingPath");
+ return reinterpret_cast<const HeapGraphPath*>(
+ const_cast<i::HeapEntry*>(
+ reinterpret_cast<const i::HeapEntry*>(
+ this))->GetRetainingPaths()->at(index));
+}
+
+
+unsigned HeapSnapshot::GetUid() const {
+ IsDeadCheck("v8::HeapSnapshot::GetUid");
+ return reinterpret_cast<const i::HeapSnapshot*>(this)->uid();
+}
+
+
+Handle<String> HeapSnapshot::GetTitle() const {
+ IsDeadCheck("v8::HeapSnapshot::GetTitle");
+ const i::HeapSnapshot* snapshot =
+ reinterpret_cast<const i::HeapSnapshot*>(this);
+ return Handle<String>(ToApi<String>(i::Factory::LookupAsciiSymbol(
+ snapshot->title())));
+}
+
+
+const HeapGraphNode* HeapSnapshot::GetHead() const {
+ IsDeadCheck("v8::HeapSnapshot::GetHead");
+ const i::HeapSnapshot* snapshot =
+ reinterpret_cast<const i::HeapSnapshot*>(this);
+ return reinterpret_cast<const HeapGraphNode*>(snapshot->const_root());
+}
+
+
+int HeapProfiler::GetSnapshotsCount() {
+ IsDeadCheck("v8::HeapProfiler::GetSnapshotsCount");
+ return i::HeapProfiler::GetSnapshotsCount();
+}
+
+
+const HeapSnapshot* HeapProfiler::GetSnapshot(int index) {
+ IsDeadCheck("v8::HeapProfiler::GetSnapshot");
+ return reinterpret_cast<const HeapSnapshot*>(
+ i::HeapProfiler::GetSnapshot(index));
+}
+
+
+const HeapSnapshot* HeapProfiler::FindSnapshot(unsigned uid) {
+ IsDeadCheck("v8::HeapProfiler::FindSnapshot");
+ return reinterpret_cast<const HeapSnapshot*>(
+ i::HeapProfiler::FindSnapshot(uid));
+}
+
+
+const HeapSnapshot* HeapProfiler::TakeSnapshot(Handle<String> title) {
+ IsDeadCheck("v8::HeapProfiler::TakeSnapshot");
+ return reinterpret_cast<const HeapSnapshot*>(
+ i::HeapProfiler::TakeSnapshot(*Utils::OpenHandle(*title)));
+}
+
#endif // ENABLE_LOGGING_AND_PROFILING
15 * B24 | 15 * B20 | 15 * B16 | 15 * B12 | 15 * B8 | 15 * B4;
const Instr kBlxRegPattern =
B24 | B21 | 15 * B16 | 15 * B12 | 15 * B8 | 3 * B4;
+const Instr kMovMvnMask = 0x6d * B21 | 0xf * B16;
+const Instr kMovMvnPattern = 0xd * B21;
+const Instr kMovMvnFlip = B22;
+const Instr kCmpCmnMask = 0xdd * B20 | 0xf * B12;
+const Instr kCmpCmnPattern = 0x15 * B20;
+const Instr kCmpCmnFlip = B21;
+const Instr kALUMask = 0x6f * B21;
+const Instr kAddPattern = 0x4 * B21;
+const Instr kSubPattern = 0x2 * B21;
+const Instr kBicPattern = 0xe * B21;
+const Instr kAndPattern = 0x0 * B21;
+const Instr kAddSubFlip = 0x6 * B21;
+const Instr kAndBicFlip = 0xe * B21;
+
// A mask for the Rd register for push, pop, ldr, str instructions.
const Instr kRdMask = 0x0000f000;
static const int kRdShift = 12;
// Low-level code emission routines depending on the addressing mode.
+// If this returns true then you have to use the rotate_imm and immed_8
+// that it returns, because it may have already changed the instruction
+// to match them!
static bool fits_shifter(uint32_t imm32,
uint32_t* rotate_imm,
uint32_t* immed_8,
return true;
}
}
- // If the opcode is mov or mvn and if ~imm32 fits, change the opcode.
- if (instr != NULL && (*instr & 0xd*B21) == 0xd*B21) {
- if (fits_shifter(~imm32, rotate_imm, immed_8, NULL)) {
- *instr ^= 0x2*B21;
- return true;
+ // If the opcode is one with a complementary version and the complementary
+ // immediate fits, change the opcode.
+ if (instr != NULL) {
+ if ((*instr & kMovMvnMask) == kMovMvnPattern) {
+ if (fits_shifter(~imm32, rotate_imm, immed_8, NULL)) {
+ *instr ^= kMovMvnFlip;
+ return true;
+ }
+ } else if ((*instr & kCmpCmnMask) == kCmpCmnPattern) {
+ if (fits_shifter(-imm32, rotate_imm, immed_8, NULL)) {
+ *instr ^= kCmpCmnFlip;
+ return true;
+ }
+ } else {
+ Instr alu_insn = (*instr & kALUMask);
+ if (alu_insn == kAddPattern ||
+ alu_insn == kSubPattern) {
+ if (fits_shifter(-imm32, rotate_imm, immed_8, NULL)) {
+ *instr ^= kAddSubFlip;
+ return true;
+ }
+ } else if (alu_insn == kAndPattern ||
+ alu_insn == kBicPattern) {
+ if (fits_shifter(~imm32, rotate_imm, immed_8, NULL)) {
+ *instr ^= kAndBicFlip;
+ return true;
+ }
+ }
}
}
return false;
}
+bool Operand::is_single_instruction() const {
+ if (rm_.is_valid()) return true;
+ if (MustUseIp(rmode_)) return false;
+ uint32_t dummy1, dummy2;
+ return fits_shifter(imm32_, &dummy1, &dummy2, NULL);
+}
+
+
void Assembler::addrmod1(Instr instr,
Register rn,
Register rd,
// Return true if this is a register operand.
INLINE(bool is_reg() const);
+ // Return true of this operand fits in one instruction so that no
+ // 2-instruction solution with a load into the ip register is necessary.
+ bool is_single_instruction() const;
+
+ inline int32_t immediate() const {
+ ASSERT(!rm_.is_valid());
+ return imm32_;
+ }
+
Register rm() const { return rm_; }
private:
extern const Instr kBlxRegMask;
extern const Instr kBlxRegPattern;
+extern const Instr kMovMvnMask;
+extern const Instr kMovMvnPattern;
+extern const Instr kMovMvnFlip;
+
+extern const Instr kCmpCmnMask;
+extern const Instr kCmpCmnPattern;
+extern const Instr kCmpCmnFlip;
+
+extern const Instr kALUMask;
+extern const Instr kAddPattern;
+extern const Instr kSubPattern;
+extern const Instr kAndPattern;
+extern const Instr kBicPattern;
+extern const Instr kAddSubFlip;
+extern const Instr kAndBicFlip;
class Assembler : public Malloced {
public:
__ str(scratch1, FieldMemOperand(result, JSArray::kElementsOffset));
// Clear the heap tag on the elements array.
- __ and_(scratch1, scratch1, Operand(~kHeapObjectTagMask));
+ ASSERT(kSmiTag == 0);
+ __ sub(scratch1, scratch1, Operand(kHeapObjectTag));
// Initialize the FixedArray and fill it with holes. FixedArray length is
// stored as a smi.
FieldMemOperand(result, JSArray::kElementsOffset));
// Clear the heap tag on the elements array.
- __ and_(elements_array_storage,
- elements_array_storage,
- Operand(~kHeapObjectTagMask));
+ ASSERT(kSmiTag == 0);
+ __ sub(elements_array_storage,
+ elements_array_storage,
+ Operand(kHeapObjectTag));
// Initialize the fixed array and fill it with holes. FixedArray length is
// stored as a smi.
// result: JSObject
// The field instance sizes contains both pre-allocated property fields and
// in-object properties.
__ ldr(r0, FieldMemOperand(r2, Map::kInstanceSizesOffset));
- __ and_(r6,
- r0,
- Operand(0x000000FF << Map::kPreAllocatedPropertyFieldsByte * 8));
- __ add(r3, r3, Operand(r6, LSR, Map::kPreAllocatedPropertyFieldsByte * 8));
- __ and_(r6, r0, Operand(0x000000FF << Map::kInObjectPropertiesByte * 8));
- __ sub(r3, r3, Operand(r6, LSR, Map::kInObjectPropertiesByte * 8), SetCC);
+ __ Ubfx(r6, r0, Map::kPreAllocatedPropertyFieldsByte * 8, 8);
+ __ add(r3, r3, Operand(r6));
+ __ Ubfx(r6, r0, Map::kInObjectPropertiesByte * 8, 8);
+ __ sub(r3, r3, Operand(r6), SetCC);
// Done if no extra properties are to be allocated.
__ b(eq, &allocated);
}
}
- // Generate the return sequence if necessary.
- if (has_valid_frame() || function_return_.is_linked()) {
- if (!function_return_.is_linked()) {
- CodeForReturnPosition(info->function());
- }
- // exit
- // r0: result
- // sp: stack pointer
- // fp: frame pointer
- // cp: callee's context
+ // Handle the return from the function.
+ if (has_valid_frame()) {
+ // If there is a valid frame, control flow can fall off the end of
+ // the body. In that case there is an implicit return statement.
+ ASSERT(!function_return_is_shadowed_);
+ frame_->PrepareForReturn();
__ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
-
+ if (function_return_.is_bound()) {
+ function_return_.Jump();
+ } else {
+ function_return_.Bind();
+ GenerateReturnSequence();
+ }
+ } else if (function_return_.is_linked()) {
+ // If the return target has dangling jumps to it, then we have not
+ // yet generated the return sequence. This can happen when (a)
+ // control does not flow off the end of the body so we did not
+ // compile an artificial return statement just above, and (b) there
+ // are return statements in the body but (c) they are all shadowed.
function_return_.Bind();
- if (FLAG_trace) {
- // Push the return value on the stack as the parameter.
- // Runtime::TraceExit returns the parameter as it is.
- frame_->EmitPush(r0);
- frame_->CallRuntime(Runtime::kTraceExit, 1);
- }
-
-#ifdef DEBUG
- // Add a label for checking the size of the code used for returning.
- Label check_exit_codesize;
- masm_->bind(&check_exit_codesize);
-#endif
- // Make sure that the constant pool is not emitted inside of the return
- // sequence.
- { Assembler::BlockConstPoolScope block_const_pool(masm_);
- // Tear down the frame which will restore the caller's frame pointer and
- // the link register.
- frame_->Exit();
-
- // Here we use masm_-> instead of the __ macro to avoid the code coverage
- // tool from instrumenting as we rely on the code size here.
- int32_t sp_delta = (scope()->num_parameters() + 1) * kPointerSize;
- masm_->add(sp, sp, Operand(sp_delta));
- masm_->Jump(lr);
-
-#ifdef DEBUG
- // Check that the size of the code used for returning matches what is
- // expected by the debugger. If the sp_delts above cannot be encoded in
- // the add instruction the add will generate two instructions.
- int return_sequence_length =
- masm_->InstructionsGeneratedSince(&check_exit_codesize);
- CHECK(return_sequence_length ==
- Assembler::kJSReturnSequenceInstructions ||
- return_sequence_length ==
- Assembler::kJSReturnSequenceInstructions + 1);
-#endif
- }
+ GenerateReturnSequence();
}
// Adjust for function-level loop nesting.
switch (op) {
case Token::BIT_OR: __ orr(tos, tos, Operand(value)); break;
case Token::BIT_XOR: __ eor(tos, tos, Operand(value)); break;
- case Token::BIT_AND: __ and_(tos, tos, Operand(value)); break;
+ case Token::BIT_AND: __ And(tos, tos, Operand(value)); break;
default: UNREACHABLE();
}
frame_->EmitPush(tos, TypeInfo::Smi());
switch (op) {
case Token::BIT_OR: __ orr(tos, tos, Operand(value)); break;
case Token::BIT_XOR: __ eor(tos, tos, Operand(value)); break;
- case Token::BIT_AND: __ and_(tos, tos, Operand(value)); break;
+ case Token::BIT_AND: __ And(tos, tos, Operand(value)); break;
default: UNREACHABLE();
}
deferred->BindExit();
// returning thus making it easier to merge.
frame_->EmitPop(r0);
frame_->PrepareForReturn();
+ if (function_return_.is_bound()) {
+ // If the function return label is already bound we reuse the
+ // code by jumping to the return site.
+ function_return_.Jump();
+ } else {
+ function_return_.Bind();
+ GenerateReturnSequence();
+ }
+ }
+}
- function_return_.Jump();
+
+void CodeGenerator::GenerateReturnSequence() {
+ if (FLAG_trace) {
+ // Push the return value on the stack as the parameter.
+ // Runtime::TraceExit returns the parameter as it is.
+ frame_->EmitPush(r0);
+ frame_->CallRuntime(Runtime::kTraceExit, 1);
+ }
+
+#ifdef DEBUG
+ // Add a label for checking the size of the code used for returning.
+ Label check_exit_codesize;
+ masm_->bind(&check_exit_codesize);
+#endif
+ // Make sure that the constant pool is not emitted inside of the return
+ // sequence.
+ { Assembler::BlockConstPoolScope block_const_pool(masm_);
+ // Tear down the frame which will restore the caller's frame pointer and
+ // the link register.
+ frame_->Exit();
+
+ // Here we use masm_-> instead of the __ macro to avoid the code coverage
+ // tool from instrumenting as we rely on the code size here.
+ int32_t sp_delta = (scope()->num_parameters() + 1) * kPointerSize;
+ masm_->add(sp, sp, Operand(sp_delta));
+ masm_->Jump(lr);
+ DeleteFrame();
+
+#ifdef DEBUG
+ // Check that the size of the code used for returning matches what is
+ // expected by the debugger. If the sp_delts above cannot be encoded in
+ // the add instruction the add will generate two instructions.
+ int return_sequence_length =
+ masm_->InstructionsGeneratedSince(&check_exit_codesize);
+ CHECK(return_sequence_length ==
+ Assembler::kJSReturnSequenceInstructions ||
+ return_sequence_length ==
+ Assembler::kJSReturnSequenceInstructions + 1);
+#endif
}
}
VirtualFrame::SpilledScope spilled_scope(frame_);
Load(property->obj());
- if (!property->is_synthetic()) {
- // Duplicate receiver for later use.
- __ ldr(r0, MemOperand(sp, 0));
- frame_->EmitPush(r0);
- }
- Load(property->key());
- EmitKeyedLoad();
- // Put the function below the receiver.
if (property->is_synthetic()) {
+ Load(property->key());
+ EmitKeyedLoad();
+ // Put the function below the receiver.
// Use the global receiver.
frame_->EmitPush(r0); // Function.
LoadGlobalReceiver(r0);
+ // Call the function.
+ CallWithArguments(args, RECEIVER_MIGHT_BE_VALUE, node->position());
+ frame_->EmitPush(r0);
} else {
- // Switch receiver and function.
- frame_->EmitPop(r1); // Receiver.
- frame_->EmitPush(r0); // Function.
- frame_->EmitPush(r1); // Receiver.
- }
+ // Load the arguments.
+ int arg_count = args->length();
+ for (int i = 0; i < arg_count; i++) {
+ Load(args->at(i));
+ }
- // Call the function.
- CallWithArguments(args, RECEIVER_MIGHT_BE_VALUE, node->position());
- frame_->EmitPush(r0);
+ // Set the name register and call the IC initialization code.
+ Load(property->key());
+ frame_->EmitPop(r2); // Function name.
+
+ InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
+ Handle<Code> stub = ComputeKeyedCallInitialize(arg_count, in_loop);
+ CodeForSourcePosition(node->position());
+ frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET, arg_count + 1);
+ __ ldr(cp, frame_->Context());
+ frame_->EmitPush(r0);
+ }
}
} else {
// Gets the wrong answer for 0, but we already checked for that case above.
__ CountLeadingZeros(source_, mantissa, zeros_);
// Compute exponent and or it into the exponent register.
- // We use mantissa as a scratch register here.
- __ rsb(mantissa, zeros_, Operand(31 + HeapNumber::kExponentBias));
+ // We use mantissa as a scratch register here. Use a fudge factor to
+ // divide the constant 31 + HeapNumber::kExponentBias, 0x41d, into two parts
+ // that fit in the ARM's constant field.
+ int fudge = 0x400;
+ __ rsb(mantissa, zeros_, Operand(31 + HeapNumber::kExponentBias - fudge));
+ __ add(mantissa, mantissa, Operand(fudge));
__ orr(exponent,
exponent,
Operand(mantissa, LSL, HeapNumber::kExponentShift));
bool never_nan_nan) {
Label not_identical;
Label heap_number, return_equal;
- Register exp_mask_reg = r5;
__ cmp(r0, r1);
__ b(ne, ¬_identical);
// The two objects are identical. If we know that one of them isn't NaN then
// we now know they test equal.
if (cc != eq || !never_nan_nan) {
- __ mov(exp_mask_reg, Operand(HeapNumber::kExponentMask));
-
// Test for NaN. Sadly, we can't just compare to Factory::nan_value(),
// so we do the second best thing - test it ourselves.
// They are both equal and they are not both Smis so both of them are not
// Read top bits of double representation (second word of value).
__ ldr(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
// Test that exponent bits are all set.
- __ and_(r3, r2, Operand(exp_mask_reg));
- __ cmp(r3, Operand(exp_mask_reg));
+ __ Sbfx(r3, r2, HeapNumber::kExponentShift, HeapNumber::kExponentBits);
+ // NaNs have all-one exponents so they sign extend to -1.
+ __ cmp(r3, Operand(-1));
__ b(ne, &return_equal);
// Shift out flag and all exponent bits, retaining only mantissa.
Register rhs_mantissa = exp_first ? r1 : r0;
Register lhs_mantissa = exp_first ? r3 : r2;
Label one_is_nan, neither_is_nan;
- Label lhs_not_nan_exp_mask_is_loaded;
- Register exp_mask_reg = r5;
-
- __ mov(exp_mask_reg, Operand(HeapNumber::kExponentMask));
- __ and_(r4, lhs_exponent, Operand(exp_mask_reg));
- __ cmp(r4, Operand(exp_mask_reg));
- __ b(ne, &lhs_not_nan_exp_mask_is_loaded);
+ __ Sbfx(r4,
+ lhs_exponent,
+ HeapNumber::kExponentShift,
+ HeapNumber::kExponentBits);
+ // NaNs have all-one exponents so they sign extend to -1.
+ __ cmp(r4, Operand(-1));
+ __ b(ne, lhs_not_nan);
__ mov(r4,
Operand(lhs_exponent, LSL, HeapNumber::kNonMantissaBitsInTopWord),
SetCC);
__ b(ne, &one_is_nan);
__ bind(lhs_not_nan);
- __ mov(exp_mask_reg, Operand(HeapNumber::kExponentMask));
- __ bind(&lhs_not_nan_exp_mask_is_loaded);
- __ and_(r4, rhs_exponent, Operand(exp_mask_reg));
- __ cmp(r4, Operand(exp_mask_reg));
+ __ Sbfx(r4,
+ rhs_exponent,
+ HeapNumber::kExponentShift,
+ HeapNumber::kExponentBits);
+ // NaNs have all-one exponents so they sign extend to -1.
+ __ cmp(r4, Operand(-1));
__ b(ne, &neither_is_nan);
__ mov(r4,
Operand(rhs_exponent, LSL, HeapNumber::kNonMantissaBitsInTopWord),
// Get exponent word.
__ ldr(scratch, FieldMemOperand(source, HeapNumber::kExponentOffset));
// Get exponent alone in scratch2.
- __ and_(scratch2, scratch, Operand(HeapNumber::kExponentMask));
+ __ Ubfx(scratch2,
+ scratch,
+ HeapNumber::kExponentShift,
+ HeapNumber::kExponentBits);
// Load dest with zero. We use this either for the final shift or
// for the answer.
__ mov(dest, Operand(0));
// A non-Smi integer is 1.xxx * 2^30 so the exponent is 30 (biased). This is
// the exponent that we are fastest at and also the highest exponent we can
// handle here.
- const uint32_t non_smi_exponent =
- (HeapNumber::kExponentBias + 30) << HeapNumber::kExponentShift;
- __ cmp(scratch2, Operand(non_smi_exponent));
+ const uint32_t non_smi_exponent = HeapNumber::kExponentBias + 30;
+ // The non_smi_exponent, 0x41d, is too big for ARM's immediate field so we
+ // split it up to avoid a constant pool entry. You can't do that in general
+ // for cmp because of the overflow flag, but we know the exponent is in the
+ // range 0-2047 so there is no overflow.
+ int fudge_factor = 0x400;
+ __ sub(scratch2, scratch2, Operand(fudge_factor));
+ __ cmp(scratch2, Operand(non_smi_exponent - fudge_factor));
// If we have a match of the int32-but-not-Smi exponent then skip some logic.
__ b(eq, &right_exponent);
// If the exponent is higher than that then go to slow case. This catches
// We know the exponent is smaller than 30 (biased). If it is less than
// 0 (biased) then the number is smaller in magnitude than 1.0 * 2^0, ie
// it rounds to zero.
- const uint32_t zero_exponent =
- (HeapNumber::kExponentBias + 0) << HeapNumber::kExponentShift;
- __ sub(scratch2, scratch2, Operand(zero_exponent), SetCC);
+ const uint32_t zero_exponent = HeapNumber::kExponentBias + 0;
+ __ sub(scratch2, scratch2, Operand(zero_exponent - fudge_factor), SetCC);
// Dest already has a Smi zero.
__ b(lt, &done);
if (!CpuFeatures::IsSupported(VFP3)) {
- // We have a shifted exponent between 0 and 30 in scratch2.
- __ mov(dest, Operand(scratch2, LSR, HeapNumber::kExponentShift));
- // We now have the exponent in dest. Subtract from 30 to get
- // how much to shift down.
- __ rsb(dest, dest, Operand(30));
+ // We have an exponent between 0 and 30 in scratch2. Subtract from 30 to
+ // get how much to shift down.
+ __ rsb(dest, scratch2, Operand(30));
}
__ bind(&right_exponent);
if (CpuFeatures::IsSupported(VFP3)) {
__ bind(&loaded);
// r2 = low 32 bits of double value
// r3 = high 32 bits of double value
- // Compute hash:
+ // Compute hash (the shifts are arithmetic):
// h = (low ^ high); h ^= h >> 16; h ^= h >> 8; h = h & (cacheSize - 1);
__ eor(r1, r2, Operand(r3));
- __ eor(r1, r1, Operand(r1, LSR, 16));
- __ eor(r1, r1, Operand(r1, LSR, 8));
+ __ eor(r1, r1, Operand(r1, ASR, 16));
+ __ eor(r1, r1, Operand(r1, ASR, 8));
ASSERT(IsPowerOf2(TranscendentalCache::kCacheSize));
- if (CpuFeatures::IsSupported(ARMv7)) {
- const int kTranscendentalCacheSizeBits = 9;
- ASSERT_EQ(1 << kTranscendentalCacheSizeBits,
- TranscendentalCache::kCacheSize);
- __ ubfx(r1, r1, 0, kTranscendentalCacheSizeBits);
- } else {
- __ and_(r1, r1, Operand(TranscendentalCache::kCacheSize - 1));
- }
+ __ And(r1, r1, Operand(TranscendentalCache::kCacheSize - 1));
// r2 = low 32 bits of double value.
// r3 = high 32 bits of double value.
// regexp_data: RegExp data (FixedArray)
// Check the representation and encoding of the subject string.
Label seq_string;
- const int kStringRepresentationEncodingMask =
- kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask;
__ ldr(r0, FieldMemOperand(subject, HeapObject::kMapOffset));
__ ldrb(r0, FieldMemOperand(r0, Map::kInstanceTypeOffset));
- __ and_(r1, r0, Operand(kStringRepresentationEncodingMask));
- // First check for sequential string.
- ASSERT_EQ(0, kStringTag);
- ASSERT_EQ(0, kSeqStringTag);
- __ tst(r1, Operand(kIsNotStringMask | kStringRepresentationMask));
+ // First check for flat string.
+ __ tst(r0, Operand(kIsNotStringMask | kStringRepresentationMask));
+ ASSERT_EQ(0, kStringTag | kSeqStringTag);
__ b(eq, &seq_string);
// subject: Subject string
// string. In that case the subject string is just the first part of the cons
// string. Also in this case the first part of the cons string is known to be
// a sequential string or an external string.
- __ and_(r0, r0, Operand(kStringRepresentationMask));
- __ cmp(r0, Operand(kConsStringTag));
+ ASSERT(kExternalStringTag !=0);
+ ASSERT_EQ(0, kConsStringTag & kExternalStringTag);
+ __ tst(r0, Operand(kIsNotStringMask | kExternalStringTag));
__ b(ne, &runtime);
__ ldr(r0, FieldMemOperand(subject, ConsString::kSecondOffset));
__ LoadRoot(r1, Heap::kEmptyStringRootIndex);
__ ldr(subject, FieldMemOperand(subject, ConsString::kFirstOffset));
__ ldr(r0, FieldMemOperand(subject, HeapObject::kMapOffset));
__ ldrb(r0, FieldMemOperand(r0, Map::kInstanceTypeOffset));
+ // Is first part a flat string?
ASSERT_EQ(0, kSeqStringTag);
__ tst(r0, Operand(kStringRepresentationMask));
__ b(nz, &runtime);
- __ and_(r1, r0, Operand(kStringRepresentationEncodingMask));
__ bind(&seq_string);
- // r1: suject string type & kStringRepresentationEncodingMask
// subject: Subject string
// regexp_data: RegExp data (FixedArray)
- // Check that the irregexp code has been generated for an ascii string. If
- // it has, the field contains a code object otherwise it contains the hole.
-#ifdef DEBUG
- const int kSeqAsciiString = kStringTag | kSeqStringTag | kAsciiStringTag;
- const int kSeqTwoByteString = kStringTag | kSeqStringTag | kTwoByteStringTag;
- CHECK_EQ(4, kSeqAsciiString);
- CHECK_EQ(0, kSeqTwoByteString);
-#endif
+ // r0: Instance type of subject string
+ ASSERT_EQ(4, kAsciiStringTag);
+ ASSERT_EQ(0, kTwoByteStringTag);
// Find the code object based on the assumptions above.
- __ mov(r3, Operand(r1, ASR, 2), SetCC);
+ __ and_(r0, r0, Operand(kStringEncodingMask));
+ __ mov(r3, Operand(r0, ASR, 2), SetCC);
__ ldr(r7, FieldMemOperand(regexp_data, JSRegExp::kDataAsciiCodeOffset), ne);
__ ldr(r7, FieldMemOperand(regexp_data, JSRegExp::kDataUC16CodeOffset), eq);
// Main code generation function
void Generate(CompilationInfo* info);
+ // Generate the return sequence code. Should be called no more than
+ // once per compiled function, immediately after binding the return
+ // target (which can not be done more than once). The return value should
+ // be in r0.
+ void GenerateReturnSequence();
+
// Returns the arguments allocation mode.
ArgumentsAllocationMode ArgumentsMode();
}
+void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr,
+ Expression* key,
+ RelocInfo::Mode mode) {
+ // Code common for calls using the IC.
+ ZoneList<Expression*>* args = expr->arguments();
+ int arg_count = args->length();
+ for (int i = 0; i < arg_count; i++) {
+ VisitForValue(args->at(i), kStack);
+ }
+ VisitForValue(key, kAccumulator);
+ __ mov(r2, r0);
+ // Record source position for debugger.
+ SetSourcePosition(expr->position());
+ // Call the IC initialization code.
+ InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
+ Handle<Code> ic = CodeGenerator::ComputeKeyedCallInitialize(arg_count,
+ in_loop);
+ __ Call(ic, mode);
+ // Restore context register.
+ __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+ Apply(context_, r0);
+}
+
+
void FullCodeGenerator::EmitCallWithStub(Call* expr) {
// Code common for calls using the call stub.
ZoneList<Expression*>* args = expr->arguments();
VisitForValue(prop->obj(), kStack);
EmitCallWithIC(expr, key->handle(), RelocInfo::CODE_TARGET);
} else {
- // Call to a keyed property, use keyed load IC followed by function
- // call.
+ // Call to a keyed property.
+ // For a synthetic property use keyed load IC followed by function call,
+ // for a regular property use keyed CallIC.
VisitForValue(prop->obj(), kStack);
- VisitForValue(prop->key(), kAccumulator);
- // Record source code position for IC call.
- SetSourcePosition(prop->position());
if (prop->is_synthetic()) {
+ VisitForValue(prop->key(), kAccumulator);
+ // Record source code position for IC call.
+ SetSourcePosition(prop->position());
__ pop(r1); // We do not need to keep the receiver.
- } else {
- __ ldr(r1, MemOperand(sp, 0)); // Keep receiver, to call function on.
- }
- Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
- __ Call(ic, RelocInfo::CODE_TARGET);
- if (prop->is_synthetic()) {
+ Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
+ __ Call(ic, RelocInfo::CODE_TARGET);
// Push result (function).
__ push(r0);
// Push Global receiver.
__ ldr(r1, CodeGenerator::GlobalObject());
__ ldr(r1, FieldMemOperand(r1, GlobalObject::kGlobalReceiverOffset));
__ push(r1);
+ EmitCallWithStub(expr);
} else {
- // Pop receiver.
- __ pop(r1);
- // Push result (function).
- __ push(r0);
- __ push(r1);
+ EmitKeyedCallWithIC(expr, prop->key(), RelocInfo::CODE_TARGET);
}
- EmitCallWithStub(expr);
}
} else {
// Call to some other expression. If the expression is an anonymous
Label* miss,
Register elements,
Register key,
+ Register result,
Register t0,
Register t1,
Register t2) {
// Register use:
//
- // elements - holds the slow-case elements of the receiver and is unchanged.
+ // elements - holds the slow-case elements of the receiver on entry.
+ // Unchanged unless 'result' is the same register.
//
- // key - holds the smi key on entry and is unchanged if a branch is
- // performed to the miss label.
- // Holds the result on exit if the load succeeded.
+ // key - holds the smi key on entry.
+ // Unchanged unless 'result' is the same register.
+ //
+ // result - holds the result on exit if the load succeeded.
+ // Allowed to be the same as 'key' or 'result'.
+ // Unchanged on bailout so 'key' or 'result' can be used
+ // in further computation.
//
// Scratch registers:
//
// Get the value at the masked, scaled index and return.
const int kValueOffset =
NumberDictionary::kElementsStartOffset + kPointerSize;
- __ ldr(key, FieldMemOperand(t2, kValueOffset));
+ __ ldr(result, FieldMemOperand(t2, kValueOffset));
}
}
+// Checks the receiver for special cases (value type, slow case bits).
+// Falls through for regular JS object.
+static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm,
+ Register receiver,
+ Register scratch1,
+ Register scratch2,
+ Label* slow) {
+ // Check that the object isn't a smi.
+ __ BranchOnSmi(receiver, slow);
+ // Get the map of the receiver.
+ __ ldr(scratch1, FieldMemOperand(receiver, HeapObject::kMapOffset));
+ // Check bit field.
+ __ ldrb(scratch2, FieldMemOperand(scratch1, Map::kBitFieldOffset));
+ __ tst(scratch2, Operand(KeyedLoadIC::kSlowCaseBitFieldMask));
+ __ b(ne, slow);
+ // Check that the object is some kind of JS object EXCEPT JS Value type.
+ // In the case that the object is a value-wrapper object,
+ // we enter the runtime system to make sure that indexing into string
+ // objects work as intended.
+ ASSERT(JS_OBJECT_TYPE > JS_VALUE_TYPE);
+ __ ldrb(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset));
+ __ cmp(scratch1, Operand(JS_OBJECT_TYPE));
+ __ b(lt, slow);
+}
+
+
+// Loads an indexed element from a fast case array.
+static void GenerateFastArrayLoad(MacroAssembler* masm,
+ Register receiver,
+ Register key,
+ Register elements,
+ Register scratch1,
+ Register scratch2,
+ Register result,
+ Label* not_fast_array,
+ Label* out_of_range) {
+ // Register use:
+ //
+ // receiver - holds the receiver on entry.
+ // Unchanged unless 'result' is the same register.
+ //
+ // key - holds the smi key on entry.
+ // Unchanged unless 'result' is the same register.
+ //
+ // elements - holds the elements of the receiver on exit.
+ //
+ // result - holds the result on exit if the load succeeded.
+ // Allowed to be the the same as 'receiver' or 'key'.
+ // Unchanged on bailout so 'receiver' and 'key' can be safely
+ // used by further computation.
+ //
+ // Scratch registers:
+ //
+ // scratch1 - used to hold elements map and elements length.
+ // Holds the elements map if not_fast_array branch is taken.
+ //
+ // scratch2 - used to hold the loaded value.
+
+ __ ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
+ // Check that the object is in fast mode (not dictionary).
+ __ ldr(scratch1, FieldMemOperand(elements, HeapObject::kMapOffset));
+ __ LoadRoot(ip, Heap::kFixedArrayMapRootIndex);
+ __ cmp(scratch1, ip);
+ __ b(ne, not_fast_array);
+ // Check that the key (index) is within bounds.
+ __ ldr(scratch1, FieldMemOperand(elements, FixedArray::kLengthOffset));
+ __ cmp(key, Operand(scratch1));
+ __ b(hs, out_of_range);
+ // Fast case: Do the load.
+ __ add(scratch1, elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ // The key is a smi.
+ ASSERT(kSmiTag == 0 && kSmiTagSize < kPointerSizeLog2);
+ __ ldr(scratch2,
+ MemOperand(scratch1, key, LSL, kPointerSizeLog2 - kSmiTagSize));
+ __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
+ __ cmp(scratch2, ip);
+ // In case the loaded value is the_hole we have to consult GetProperty
+ // to ensure the prototype chain is searched.
+ __ b(eq, out_of_range);
+ __ mov(result, scratch2);
+}
+
+
+// Checks whether a key is an array index string or a symbol string.
+// Falls through if a key is a symbol.
+static void GenerateKeyStringCheck(MacroAssembler* masm,
+ Register key,
+ Register map,
+ Register hash,
+ Label* index_string,
+ Label* not_symbol) {
+ // The key is not a smi.
+ // Is it a string?
+ __ CompareObjectType(key, map, hash, FIRST_NONSTRING_TYPE);
+ __ b(ge, not_symbol);
+
+ // Is the string an array index, with cached numeric value?
+ __ ldr(hash, FieldMemOperand(key, String::kHashFieldOffset));
+ __ tst(hash, Operand(String::kContainsCachedArrayIndexMask));
+ __ b(eq, index_string);
+
+ // Is the string a symbol?
+ // map: key map
+ __ ldrb(hash, FieldMemOperand(map, Map::kInstanceTypeOffset));
+ ASSERT(kSymbolTag != 0);
+ __ tst(hash, Operand(kIsSymbolMask));
+ __ b(eq, not_symbol);
+}
+
+
+// Picks out an array index from the hash field.
+static void GenerateIndexFromHash(MacroAssembler* masm,
+ Register key,
+ Register hash) {
+ // Register use:
+ // key - holds the overwritten key on exit.
+ // hash - holds the key's hash. Clobbered.
+
+ // If the hash field contains an array index pick it out. The assert checks
+ // that the constants for the maximum number of digits for an array index
+ // cached in the hash field and the number of bits reserved for it does not
+ // conflict.
+ ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) <
+ (1 << String::kArrayIndexValueBits));
+ // We want the smi-tagged index in key. kArrayIndexValueMask has zeros in
+ // the low kHashShift bits.
+ ASSERT(String::kHashShift >= kSmiTagSize);
+ // Here we actually clobber the key which will be used if calling into
+ // runtime later. However as the new key is the numeric value of a string key
+ // there is no difference in using either key.
+ ASSERT(String::kHashShift >= kSmiTagSize);
+ __ Ubfx(hash, hash, String::kHashShift, String::kArrayIndexValueBits);
+ __ mov(key, Operand(hash, LSL, kSmiTagSize));
+}
+
+
// Defined in ic.cc.
Object* CallIC_Miss(Arguments args);
-void CallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
+// The generated code does not accept smi keys.
+// The generated code falls through if both probes miss.
+static void GenerateMonomorphicCacheProbe(MacroAssembler* masm,
+ int argc,
+ Code::Kind kind) {
// ----------- S t a t e -------------
+ // -- r1 : receiver
// -- r2 : name
- // -- lr : return address
// -----------------------------------
Label number, non_number, non_string, boolean, probe, miss;
- // Get the receiver of the function from the stack into r1.
- __ ldr(r1, MemOperand(sp, argc * kPointerSize));
-
// Probe the stub cache.
Code::Flags flags =
- Code::ComputeFlags(Code::CALL_IC, NOT_IN_LOOP, MONOMORPHIC, NORMAL, argc);
+ Code::ComputeFlags(kind, NOT_IN_LOOP, MONOMORPHIC, NORMAL, argc);
StubCache::GenerateProbe(masm, flags, r1, r2, r3, no_reg);
// If the stub cache probing failed, the receiver might be a value.
__ bind(&probe);
StubCache::GenerateProbe(masm, flags, r1, r2, r3, no_reg);
- // Cache miss: Jump to runtime.
__ bind(&miss);
- GenerateMiss(masm, argc);
}
}
-void CallIC::GenerateNormal(MacroAssembler* masm, int argc) {
+static void GenerateCallNormal(MacroAssembler* masm, int argc) {
// ----------- S t a t e -------------
// -- r2 : name
// -- lr : return address
__ CheckAccessGlobalProxy(r1, r0, &miss);
__ b(&invoke);
- // Cache miss: Jump to runtime.
__ bind(&miss);
- GenerateMiss(masm, argc);
}
-void CallIC::GenerateMiss(MacroAssembler* masm, int argc) {
+static void GenerateCallMiss(MacroAssembler* masm, int argc, IC::UtilityId id) {
// ----------- S t a t e -------------
// -- r2 : name
// -- lr : return address
// Call the entry.
__ mov(r0, Operand(2));
- __ mov(r1, Operand(ExternalReference(IC_Utility(kCallIC_Miss))));
+ __ mov(r1, Operand(ExternalReference(IC_Utility(id))));
CEntryStub stub(1);
__ CallStub(&stub);
}
+void CallIC::GenerateMiss(MacroAssembler* masm, int argc) {
+ // ----------- S t a t e -------------
+ // -- r2 : name
+ // -- lr : return address
+ // -----------------------------------
+
+ GenerateCallMiss(masm, argc, IC::kCallIC_Miss);
+}
+
+
+void CallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
+ // ----------- S t a t e -------------
+ // -- r2 : name
+ // -- lr : return address
+ // -----------------------------------
+
+ // Get the receiver of the function from the stack into r1.
+ __ ldr(r1, MemOperand(sp, argc * kPointerSize));
+ GenerateMonomorphicCacheProbe(masm, argc, Code::CALL_IC);
+ GenerateMiss(masm, argc);
+}
+
+
+void CallIC::GenerateNormal(MacroAssembler* masm, int argc) {
+ // ----------- S t a t e -------------
+ // -- r2 : name
+ // -- lr : return address
+ // -----------------------------------
+
+ GenerateCallNormal(masm, argc);
+ GenerateMiss(masm, argc);
+}
+
+
void KeyedCallIC::GenerateMiss(MacroAssembler* masm, int argc) {
- UNREACHABLE();
+ // ----------- S t a t e -------------
+ // -- r2 : name
+ // -- lr : return address
+ // -----------------------------------
+
+ GenerateCallMiss(masm, argc, IC::kKeyedCallIC_Miss);
}
void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
- UNREACHABLE();
+ // ----------- S t a t e -------------
+ // -- r2 : name
+ // -- lr : return address
+ // -----------------------------------
+
+ // Get the receiver of the function from the stack into r1.
+ __ ldr(r1, MemOperand(sp, argc * kPointerSize));
+
+ Label do_call, slow_call, slow_load, slow_reload_receiver;
+ Label check_number_dictionary, check_string, lookup_monomorphic_cache;
+ Label index_smi, index_string;
+
+ // Check that the key is a smi.
+ __ BranchOnNotSmi(r2, &check_string);
+ __ bind(&index_smi);
+ // Now the key is known to be a smi. This place is also jumped to from below
+ // where a numeric string is converted to a smi.
+
+ GenerateKeyedLoadReceiverCheck(masm, r1, r0, r3, &slow_call);
+
+ GenerateFastArrayLoad(
+ masm, r1, r2, r4, r3, r0, r1, &check_number_dictionary, &slow_load);
+ __ IncrementCounter(&Counters::keyed_call_generic_smi_fast, 1, r0, r3);
+
+ __ bind(&do_call);
+ // receiver in r1 is not used after this point.
+ // r2: key
+ // r1: function
+
+ // Check that the value in r1 is a JSFunction.
+ __ BranchOnSmi(r1, &slow_call);
+ __ CompareObjectType(r1, r0, r0, JS_FUNCTION_TYPE);
+ __ b(ne, &slow_call);
+ // Invoke the function.
+ ParameterCount actual(argc);
+ __ InvokeFunction(r1, actual, JUMP_FUNCTION);
+
+ __ bind(&check_number_dictionary);
+ // r2: key
+ // r3: elements map
+ // r4: elements
+ // Check whether the elements is a number dictionary.
+ __ LoadRoot(ip, Heap::kHashTableMapRootIndex);
+ __ cmp(r3, ip);
+ __ b(ne, &slow_load);
+ __ mov(r0, Operand(r2, ASR, kSmiTagSize));
+ // r0: untagged index
+ GenerateNumberDictionaryLoad(masm, &slow_load, r4, r2, r1, r0, r3, r5);
+ __ IncrementCounter(&Counters::keyed_call_generic_smi_dict, 1, r0, r3);
+ __ jmp(&do_call);
+
+ __ bind(&slow_load);
+ // This branch is taken when calling KeyedCallIC_Miss is neither required
+ // nor beneficial.
+ __ IncrementCounter(&Counters::keyed_call_generic_slow_load, 1, r0, r3);
+ __ EnterInternalFrame();
+ __ push(r2); // save the key
+ __ Push(r1, r2); // pass the receiver and the key
+ __ CallRuntime(Runtime::kKeyedGetProperty, 2);
+ __ pop(r2); // restore the key
+ __ LeaveInternalFrame();
+ __ mov(r1, r0);
+ __ jmp(&do_call);
+
+ __ bind(&check_string);
+ GenerateKeyStringCheck(masm, r2, r0, r3, &index_string, &slow_call);
+
+ // The key is known to be a symbol.
+ // If the receiver is a regular JS object with slow properties then do
+ // a quick inline probe of the receiver's dictionary.
+ // Otherwise do the monomorphic cache probe.
+ GenerateKeyedLoadReceiverCheck(masm, r1, r0, r3, &lookup_monomorphic_cache);
+
+ __ ldr(r3, FieldMemOperand(r1, JSObject::kPropertiesOffset));
+ __ ldr(r3, FieldMemOperand(r3, HeapObject::kMapOffset));
+ __ LoadRoot(ip, Heap::kHashTableMapRootIndex);
+ __ cmp(r3, ip);
+ __ b(ne, &lookup_monomorphic_cache);
+
+ GenerateDictionaryLoad(
+ masm, &slow_load, r1, r2, r1, r0, r3, r4, DICTIONARY_CHECK_DONE);
+ __ IncrementCounter(&Counters::keyed_call_generic_lookup_dict, 1, r0, r3);
+ __ jmp(&do_call);
+
+ __ bind(&lookup_monomorphic_cache);
+ __ IncrementCounter(&Counters::keyed_call_generic_lookup_cache, 1, r0, r3);
+ GenerateMonomorphicCacheProbe(masm, argc, Code::KEYED_CALL_IC);
+ // Fall through on miss.
+
+ __ bind(&slow_call);
+ // This branch is taken if:
+ // - the receiver requires boxing or access check,
+ // - the key is neither smi nor symbol,
+ // - the value loaded is not a function,
+ // - there is hope that the runtime will create a monomorphic call stub
+ // that will get fetched next time.
+ __ IncrementCounter(&Counters::keyed_call_generic_slow, 1, r0, r3);
+ GenerateMiss(masm, argc);
+
+ __ bind(&index_string);
+ GenerateIndexFromHash(masm, r2, r3);
+ // Now jump to the place where smi keys are handled.
+ __ jmp(&index_smi);
}
void KeyedCallIC::GenerateNormal(MacroAssembler* masm, int argc) {
- UNREACHABLE();
+ // ----------- S t a t e -------------
+ // -- r2 : name
+ // -- lr : return address
+ // -----------------------------------
+
+ GenerateCallNormal(masm, argc);
+ GenerateMiss(masm, argc);
}
Register key = r0;
Register receiver = r1;
- // Check that the object isn't a smi.
- __ BranchOnSmi(receiver, &slow);
- // Get the map of the receiver.
- __ ldr(r2, FieldMemOperand(receiver, HeapObject::kMapOffset));
- // Check bit field.
- __ ldrb(r3, FieldMemOperand(r2, Map::kBitFieldOffset));
- __ tst(r3, Operand(kSlowCaseBitFieldMask));
- __ b(ne, &slow);
- // Check that the object is some kind of JS object EXCEPT JS Value type.
- // In the case that the object is a value-wrapper object,
- // we enter the runtime system to make sure that indexing into string
- // objects work as intended.
- ASSERT(JS_OBJECT_TYPE > JS_VALUE_TYPE);
- __ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset));
- __ cmp(r2, Operand(JS_OBJECT_TYPE));
- __ b(lt, &slow);
+ GenerateKeyedLoadReceiverCheck(masm, receiver, r2, r3, &slow);
// Check that the key is a smi.
__ BranchOnNotSmi(key, &check_string);
__ bind(&index_smi);
// Now the key is known to be a smi. This place is also jumped to from below
// where a numeric string is converted to a smi.
- __ ldr(r4, FieldMemOperand(receiver, JSObject::kElementsOffset));
- // Check that the object is in fast mode (not dictionary).
- __ ldr(r3, FieldMemOperand(r4, HeapObject::kMapOffset));
- __ LoadRoot(ip, Heap::kFixedArrayMapRootIndex);
- __ cmp(r3, ip);
- __ b(ne, &check_pixel_array);
- // Check that the key (index) is within bounds.
- __ ldr(r3, FieldMemOperand(r4, FixedArray::kLengthOffset));
- __ cmp(key, Operand(r3));
- __ b(hs, &slow);
- // Fast case: Do the load.
- __ add(r3, r4, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
- // The key is a smi.
- ASSERT(kSmiTag == 0 && kSmiTagSize < kPointerSizeLog2);
- __ ldr(r2, MemOperand(r3, key, LSL, kPointerSizeLog2 - kSmiTagSize));
- __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
- __ cmp(r2, ip);
- // In case the loaded value is the_hole we have to consult GetProperty
- // to ensure the prototype chain is searched.
- __ b(eq, &slow);
- __ mov(r0, r2);
+
+ GenerateFastArrayLoad(
+ masm, receiver, key, r4, r3, r2, r0, &check_pixel_array, &slow);
__ IncrementCounter(&Counters::keyed_load_generic_smi, 1, r2, r3);
__ Ret();
__ cmp(r3, ip);
__ b(ne, &slow);
__ mov(r2, Operand(r0, ASR, kSmiTagSize));
- GenerateNumberDictionaryLoad(masm, &slow, r4, r0, r2, r3, r5);
+ GenerateNumberDictionaryLoad(masm, &slow, r4, r0, r0, r2, r3, r5);
__ Ret();
// Slow case, key and receiver still in r0 and r1.
GenerateRuntimeGetProperty(masm);
__ bind(&check_string);
- // The key is not a smi.
- // Is it a string?
- // r0: key
- // r1: receiver
- __ CompareObjectType(r0, r2, r3, FIRST_NONSTRING_TYPE);
- __ b(ge, &slow);
-
- // Is the string an array index, with cached numeric value?
- __ ldr(r3, FieldMemOperand(r0, String::kHashFieldOffset));
- __ tst(r3, Operand(String::kContainsCachedArrayIndexMask));
- __ b(eq, &index_string);
-
- // Is the string a symbol?
- // r2: key map
- __ ldrb(r3, FieldMemOperand(r2, Map::kInstanceTypeOffset));
- ASSERT(kSymbolTag != 0);
- __ tst(r3, Operand(kIsSymbolMask));
- __ b(eq, &slow);
+ GenerateKeyStringCheck(masm, key, r2, r3, &index_string, &slow);
// If the receiver is a fast-case object, check the keyed lookup
// cache. Otherwise probe the dictionary.
__ mov(r3, Operand(r2, ASR, KeyedLookupCache::kMapHashShift));
__ ldr(r4, FieldMemOperand(r0, String::kHashFieldOffset));
__ eor(r3, r3, Operand(r4, ASR, String::kHashShift));
- __ and_(r3, r3, Operand(KeyedLookupCache::kCapacityMask));
+ __ And(r3, r3, Operand(KeyedLookupCache::kCapacityMask));
// Load the key (consisting of map and symbol) from the cache and
// check for match.
__ IncrementCounter(&Counters::keyed_load_generic_symbol, 1, r2, r3);
__ Ret();
- __ b(&slow);
- // If the hash field contains an array index pick it out. The assert checks
- // that the constants for the maximum number of digits for an array index
- // cached in the hash field and the number of bits reserved for it does not
- // conflict.
- ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) <
- (1 << String::kArrayIndexValueBits));
__ bind(&index_string);
- // r0: key (string)
- // r1: receiver
- // r3: hash field
- // We want the smi-tagged index in r0. kArrayIndexValueMask has zeros in
- // the low kHashShift bits.
- ASSERT(String::kHashShift >= kSmiTagSize);
- __ and_(r3, r3, Operand(String::kArrayIndexValueMask));
- // Here we actually clobber the key (r0) which will be used if calling into
- // runtime later. However as the new key is the numeric value of a string key
- // there is no difference in using either key.
- __ mov(r0, Operand(r3, ASR, String::kHashShift - kSmiTagSize));
+ GenerateIndexFromHash(masm, key, r3);
// Now jump to the place where smi keys are handled.
__ jmp(&index_smi);
}
if (CpuFeatures::IsSupported(VFP3)) {
CpuFeatures::Scope scope(VFP3);
- // vldr requires offset to be a multiple of 4 so we can not
- // include -kHeapObjectTag into it.
- __ sub(r5, r0, Operand(kHeapObjectTag));
- __ vldr(d0, r5, HeapNumber::kValueOffset);
if (array_type == kExternalFloatArray) {
+ // vldr requires offset to be a multiple of 4 so we can not
+ // include -kHeapObjectTag into it.
+ __ sub(r5, r0, Operand(kHeapObjectTag));
+ __ vldr(d0, r5, HeapNumber::kValueOffset);
__ vcvt_f32_f64(s0, d0);
__ vmov(r5, s0);
__ str(r5, MemOperand(r3, r4, LSL, 2));
} else {
- Label done;
-
// Need to perform float-to-int conversion.
- // Test for NaN.
- __ vcmp(d0, d0);
- // Move vector status bits to normal status bits.
- __ vmrs(v8::internal::pc);
- __ mov(r5, Operand(0), LeaveCC, vs); // NaN converts to 0.
- __ b(vs, &done);
-
- // Test whether exponent equal to 0x7FF (infinity or NaN).
- __ vmov(r6, r7, d0);
- __ mov(r5, Operand(0x7FF00000));
- __ and_(r6, r6, Operand(r5));
- __ teq(r6, Operand(r5));
- __ mov(r6, Operand(0), LeaveCC, eq);
+ // Test for NaN or infinity (both give zero).
+ __ ldr(r6, FieldMemOperand(r5, HeapNumber::kExponentOffset));
+
+ // Hoisted load. vldr requires offset to be a multiple of 4 so we can not
+ // include -kHeapObjectTag into it.
+ __ sub(r5, r0, Operand(kHeapObjectTag));
+ __ vldr(d0, r5, HeapNumber::kValueOffset);
+
+ __ Sbfx(r6, r6, HeapNumber::kExponentShift, HeapNumber::kExponentBits);
+ // NaNs and Infinities have all-one exponents so they sign extend to -1.
+ __ cmp(r6, Operand(-1));
+ __ mov(r5, Operand(Smi::FromInt(0)), LeaveCC, eq);
// Not infinity or NaN simply convert to int.
if (IsElementTypeSigned(array_type)) {
} else {
__ vcvt_u32_f64(s0, d0, ne);
}
-
__ vmov(r5, s0, ne);
- __ bind(&done);
switch (array_type) {
case kExternalByteArray:
case kExternalUnsignedByteArray:
}
+void MacroAssembler::And(Register dst, Register src1, const Operand& src2,
+ Condition cond) {
+ if (!CpuFeatures::IsSupported(ARMv7) || src2.is_single_instruction()) {
+ and_(dst, src1, src2, LeaveCC, cond);
+ return;
+ }
+ int32_t immediate = src2.immediate();
+ if (immediate == 0) {
+ mov(dst, Operand(0), LeaveCC, cond);
+ return;
+ }
+ if (IsPowerOf2(immediate + 1) && ((immediate & 1) != 0)) {
+ ubfx(dst, src1, 0, WhichPowerOf2(immediate + 1), cond);
+ return;
+ }
+ and_(dst, src1, src2, LeaveCC, cond);
+}
+
+
+void MacroAssembler::Ubfx(Register dst, Register src1, int lsb, int width,
+ Condition cond) {
+ ASSERT(lsb < 32);
+ if (!CpuFeatures::IsSupported(ARMv7)) {
+ int mask = (1 << (width + lsb)) - 1 - ((1 << lsb) - 1);
+ and_(dst, src1, Operand(mask), LeaveCC, cond);
+ if (lsb != 0) {
+ mov(dst, Operand(dst, LSR, lsb), LeaveCC, cond);
+ }
+ } else {
+ ubfx(dst, src1, lsb, width, cond);
+ }
+}
+
+
+void MacroAssembler::Sbfx(Register dst, Register src1, int lsb, int width,
+ Condition cond) {
+ ASSERT(lsb < 32);
+ if (!CpuFeatures::IsSupported(ARMv7)) {
+ int mask = (1 << (width + lsb)) - 1 - ((1 << lsb) - 1);
+ and_(dst, src1, Operand(mask), LeaveCC, cond);
+ int shift_up = 32 - lsb - width;
+ int shift_down = lsb + shift_up;
+ if (shift_up != 0) {
+ mov(dst, Operand(dst, LSL, shift_up), LeaveCC, cond);
+ }
+ if (shift_down != 0) {
+ mov(dst, Operand(dst, ASR, shift_down), LeaveCC, cond);
+ }
+ } else {
+ sbfx(dst, src1, lsb, width, cond);
+ }
+}
+
+
void MacroAssembler::SmiJumpTable(Register index, Vector<Label*> targets) {
// Empty the const pool.
CheckConstPool(true, true);
Register scratch = no_reg,
Condition cond = al);
+
+ void And(Register dst, Register src1, const Operand& src2,
+ Condition cond = al);
+ void Ubfx(Register dst, Register src, int lsb, int width,
+ Condition cond = al);
+ void Sbfx(Register dst, Register src, int lsb, int width,
+ Condition cond = al);
+
void Call(Label* target);
void Move(Register dst, Handle<Object> value);
// May do nothing if the registers are identical.
}
+void CallStubCompiler::GenerateNameCheck(String* name, Label* miss) {
+ if (kind_ == Code::KEYED_CALL_IC) {
+ __ cmp(r2, Operand(Handle<String>(name)));
+ __ b(ne, miss);
+ }
+}
+
+
void CallStubCompiler::GenerateMissBranch() {
Handle<Code> ic = ComputeCallMiss(arguments().immediate(), kind_);
__ Jump(ic, RelocInfo::CODE_TARGET);
// -----------------------------------
Label miss;
+ GenerateNameCheck(name, &miss);
+
const int argc = arguments().immediate();
// Get the receiver of the function from the stack into r0.
Label miss;
+ GenerateNameCheck(name, &miss);
+
// Get the receiver from the stack
const int argc = arguments().immediate();
__ ldr(r1, MemOperand(sp, argc * kPointerSize));
Label miss;
+ GenerateNameCheck(name, &miss);
+
// Get the receiver from the stack
const int argc = arguments().immediate();
__ ldr(r1, MemOperand(sp, argc * kPointerSize));
Label miss_in_smi_check;
+ GenerateNameCheck(name, &miss_in_smi_check);
+
// Get the receiver from the stack
const int argc = arguments().immediate();
__ ldr(r1, MemOperand(sp, argc * kPointerSize));
Label miss;
+ GenerateNameCheck(name, &miss);
+
// Get the number of arguments.
const int argc = arguments().immediate();
// -----------------------------------
Label miss;
+ GenerateNameCheck(name, &miss);
+
// Get the number of arguments.
const int argc = arguments().immediate();
int dropped_args) {
switch (code->kind()) {
case Code::CALL_IC:
+ case Code::KEYED_CALL_IC:
case Code::FUNCTION:
break;
case Code::KEYED_LOAD_IC:
void Enter();
void Exit();
- // Prepare for returning from the frame by spilling locals and
- // dropping all non-locals elements in the virtual frame. This
+ // Prepare for returning from the frame by elements in the virtual frame. This
// avoids generating unnecessary merge code when jumping to the
- // shared return site. Emits code for spills.
+ // shared return site. No spill code emitted. Value to return should be in r0.
inline void PrepareForReturn();
// Number of local variables after when we use a loop for allocating.
#include "heap-profiler.h"
#include "frames-inl.h"
#include "global-handles.h"
+#include "profile-generator.h"
#include "string-stream.h"
-#include "zone-inl.h"
namespace v8 {
namespace internal {
} // namespace
+HeapProfiler* HeapProfiler::singleton_ = NULL;
+
+HeapProfiler::HeapProfiler()
+ : snapshots_(new HeapSnapshotsCollection()),
+ next_snapshot_uid_(1) {
+}
+
+
+HeapProfiler::~HeapProfiler() {
+ delete snapshots_;
+}
+
+
+void HeapProfiler::Setup() {
+ if (singleton_ == NULL) {
+ singleton_ = new HeapProfiler();
+ }
+}
+
+
+void HeapProfiler::TearDown() {
+ delete singleton_;
+ singleton_ = NULL;
+}
+
+
+HeapSnapshot* HeapProfiler::TakeSnapshot(const char* name) {
+ ASSERT(singleton_ != NULL);
+ return singleton_->TakeSnapshotImpl(name);
+}
+
+
+HeapSnapshot* HeapProfiler::TakeSnapshot(String* name) {
+ ASSERT(singleton_ != NULL);
+ return singleton_->TakeSnapshotImpl(name);
+}
+
+
+HeapSnapshot* HeapProfiler::TakeSnapshotImpl(const char* name) {
+ HeapSnapshot* result = snapshots_->NewSnapshot(name, next_snapshot_uid_++);
+ HeapSnapshotGenerator generator(result);
+ generator.GenerateSnapshot();
+ return result;
+}
+
+
+HeapSnapshot* HeapProfiler::TakeSnapshotImpl(String* name) {
+ return TakeSnapshotImpl(snapshots_->GetName(name));
+}
+
+
+int HeapProfiler::GetSnapshotsCount() {
+ ASSERT(singleton_ != NULL);
+ return singleton_->snapshots_->snapshots()->length();
+}
+
+
+HeapSnapshot* HeapProfiler::GetSnapshot(int index) {
+ ASSERT(singleton_ != NULL);
+ return singleton_->snapshots_->snapshots()->at(index);
+}
+
+
+HeapSnapshot* HeapProfiler::FindSnapshot(unsigned uid) {
+ ASSERT(singleton_ != NULL);
+ return singleton_->snapshots_->GetSnapshot(uid);
+}
+
+
const JSObjectsClusterTreeConfig::Key JSObjectsClusterTreeConfig::kNoKey;
const JSObjectsClusterTreeConfig::Value JSObjectsClusterTreeConfig::kNoValue;
#ifndef V8_HEAP_PROFILER_H_
#define V8_HEAP_PROFILER_H_
-#include "zone.h"
+#include "zone-inl.h"
namespace v8 {
namespace internal {
#ifdef ENABLE_LOGGING_AND_PROFILING
+class HeapSnapshot;
+class HeapSnapshotsCollection;
+
// The HeapProfiler writes data to the log files, which can be postprocessed
// to generate .hp files for use by the GHC/Valgrind tool hp2ps.
class HeapProfiler {
public:
+ static void Setup();
+ static void TearDown();
+ static HeapSnapshot* TakeSnapshot(const char* name);
+ static HeapSnapshot* TakeSnapshot(String* name);
+ static int GetSnapshotsCount();
+ static HeapSnapshot* GetSnapshot(int index);
+ static HeapSnapshot* FindSnapshot(unsigned uid);
+
+ // Obsolete interface.
// Write a single heap sample to the log file.
static void WriteSample();
private:
+ HeapProfiler();
+ ~HeapProfiler();
+ HeapSnapshot* TakeSnapshotImpl(const char* name);
+ HeapSnapshot* TakeSnapshotImpl(String* name);
+
+ // Obsolete interface.
// Update the array info with stats from obj.
static void CollectStats(HeapObject* obj, HistogramInfo* info);
+
+ HeapSnapshotsCollection* snapshots_;
+ unsigned next_snapshot_uid_;
+
+ static HeapProfiler* singleton_;
};
};
inline static int Hash(const Converter& c) {
uint32_t hash = (c.integers[0] ^ c.integers[1]);
- hash ^= hash >> 16;
- hash ^= hash >> 8;
+ hash ^= static_cast<int32_t>(hash) >> 16;
+ hash ^= static_cast<int32_t>(hash) >> 8;
return (hash & (kCacheSize - 1));
}
void CodeGenerator::VisitStatementsAndSpill(ZoneList<Statement*>* statements) {
+#ifdef DEBUG
+ int original_height = frame_->height();
+#endif
ASSERT(in_spilled_code());
set_in_spilled_code(false);
VisitStatements(statements);
frame_->SpillAll();
}
set_in_spilled_code(true);
+
+ ASSERT(!has_valid_frame() || frame_->height() == original_height);
}
void CodeGenerator::VisitStatements(ZoneList<Statement*>* statements) {
+#ifdef DEBUG
+ int original_height = frame_->height();
+#endif
ASSERT(!in_spilled_code());
for (int i = 0; has_valid_frame() && i < statements->length(); i++) {
Visit(statements->at(i));
}
+ ASSERT(!has_valid_frame() || frame_->height() == original_height);
}
__ bind(&cache_miss);
__ push(cache_); // store a reference to cache
__ push(key_); // store a key
- Handle<Object> receiver(Top::global_context()->global());
- __ push(Immediate(receiver));
+ __ push(Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX)));
__ push(key_);
// On ia32 function must be in edi.
__ mov(edi, FieldOperand(cache_, JSFunctionResultCache::kFactoryOffset));
// ST[0] == double value
// ebx = low 32 bits of double value
// edx = high 32 bits of double value
- // Compute hash:
+ // Compute hash (the shifts are arithmetic):
// h = (low ^ high); h ^= h >> 16; h ^= h >> 8; h = h & (cacheSize - 1);
__ mov(ecx, ebx);
__ xor_(ecx, Operand(edx));
__ mov(eax, ecx);
- __ shr(eax, 16);
+ __ sar(eax, 16);
__ xor_(ecx, Operand(eax));
__ mov(eax, ecx);
- __ shr(eax, 8);
+ __ sar(eax, 8);
__ xor_(ecx, Operand(eax));
ASSERT(IsPowerOf2(TranscendentalCache::kCacheSize));
__ and_(Operand(ecx), Immediate(TranscendentalCache::kCacheSize - 1));
// ecx: RegExp data (FixedArray)
// Check the representation and encoding of the subject string.
- Label seq_string, seq_two_byte_string, check_code;
- const int kStringRepresentationEncodingMask =
- kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask;
+ Label seq_ascii_string, seq_two_byte_string, check_code;
__ mov(eax, Operand(esp, kSubjectOffset));
__ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
__ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
- __ and_(ebx, kStringRepresentationEncodingMask);
- // First check for sequential string.
- ASSERT_EQ(0, kStringTag);
- ASSERT_EQ(0, kSeqStringTag);
+ // First check for flat two byte string.
+ __ and_(ebx,
+ kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask);
+ ASSERT_EQ(0, kStringTag | kSeqStringTag | kTwoByteStringTag);
+ __ j(zero, &seq_two_byte_string);
+ // Any other flat string must be a flat ascii string.
__ test(Operand(ebx),
Immediate(kIsNotStringMask | kStringRepresentationMask));
- __ j(zero, &seq_string);
+ __ j(zero, &seq_ascii_string);
// Check for flat cons string.
// A flat cons string is a cons string where the second part is the empty
// string. In that case the subject string is just the first part of the cons
// string. Also in this case the first part of the cons string is known to be
// a sequential string or an external string.
- __ and_(ebx, kStringRepresentationMask);
- __ cmp(ebx, kConsStringTag);
- __ j(not_equal, &runtime);
+ ASSERT(kExternalStringTag !=0);
+ ASSERT_EQ(0, kConsStringTag & kExternalStringTag);
+ __ test(Operand(ebx),
+ Immediate(kIsNotStringMask | kExternalStringTag));
+ __ j(not_zero, &runtime);
+ // String is a cons string.
__ mov(edx, FieldOperand(eax, ConsString::kSecondOffset));
__ cmp(Operand(edx), Factory::empty_string());
__ j(not_equal, &runtime);
__ mov(eax, FieldOperand(eax, ConsString::kFirstOffset));
__ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
- __ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
- ASSERT_EQ(0, kSeqStringTag);
- __ test(ebx, Immediate(kStringRepresentationMask));
+ // String is a cons string with empty second part.
+ // eax: first part of cons string.
+ // ebx: map of first part of cons string.
+ // Is first part a flat two byte string?
+ __ test_b(FieldOperand(ebx, Map::kInstanceTypeOffset),
+ kStringRepresentationMask | kStringEncodingMask);
+ ASSERT_EQ(0, kSeqStringTag | kTwoByteStringTag);
+ __ j(zero, &seq_two_byte_string);
+ // Any other flat string must be ascii.
+ __ test_b(FieldOperand(ebx, Map::kInstanceTypeOffset),
+ kStringRepresentationMask);
__ j(not_zero, &runtime);
- __ and_(ebx, kStringRepresentationEncodingMask);
- __ bind(&seq_string);
- // eax: subject string (sequential either ascii to two byte)
- // ebx: suject string type & kStringRepresentationEncodingMask
+ __ bind(&seq_ascii_string);
+ // eax: subject string (flat ascii)
// ecx: RegExp data (FixedArray)
- // Check that the irregexp code has been generated for an ascii string. If
- // it has, the field contains a code object otherwise it contains the hole.
- const int kSeqTwoByteString = kStringTag | kSeqStringTag | kTwoByteStringTag;
- __ cmp(ebx, kSeqTwoByteString);
- __ j(equal, &seq_two_byte_string);
- if (FLAG_debug_code) {
- __ cmp(ebx, kStringTag | kSeqStringTag | kAsciiStringTag);
- __ Check(equal, "Expected sequential ascii string");
- }
__ mov(edx, FieldOperand(ecx, JSRegExp::kDataAsciiCodeOffset));
__ Set(edi, Immediate(1)); // Type is ascii.
__ jmp(&check_code);
__ bind(&seq_two_byte_string);
- // eax: subject string
+ // eax: subject string (flat two byte)
// ecx: RegExp data (FixedArray)
__ mov(edx, FieldOperand(ecx, JSRegExp::kDataUC16CodeOffset));
__ Set(edi, Immediate(0)); // Type is two byte.
__ j(zero, &string_add_runtime);
__ bind(&make_flat_ascii_string);
- // Both strings are ascii strings. As they are short they are both flat.
+ // Both strings are ascii strings. As they are short they are both flat.
// ebx: length of resulting flat string as a smi
__ SmiUntag(ebx);
__ AllocateAsciiString(eax, ebx, ecx, edx, edi, &string_add_runtime);
// Falls through for regular JS object.
static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm,
Register receiver,
- Register r0,
+ Register map,
Label* slow) {
// Register use:
// receiver - holds the receiver and is unchanged.
// Scratch registers:
- // r0 - used to hold the map of the receiver.
+ // map - used to hold the map of the receiver.
// Check that the object isn't a smi.
__ test(receiver, Immediate(kSmiTagMask));
__ j(zero, slow, not_taken);
// Get the map of the receiver.
- __ mov(r0, FieldOperand(receiver, HeapObject::kMapOffset));
+ __ mov(map, FieldOperand(receiver, HeapObject::kMapOffset));
// Check bit field.
- __ test_b(FieldOperand(r0, Map::kBitFieldOffset),
+ __ test_b(FieldOperand(map, Map::kBitFieldOffset),
KeyedLoadIC::kSlowCaseBitFieldMask);
__ j(not_zero, slow, not_taken);
// Check that the object is some kind of JS object EXCEPT JS Value type.
// into string objects works as intended.
ASSERT(JS_OBJECT_TYPE > JS_VALUE_TYPE);
- __ CmpInstanceType(r0, JS_OBJECT_TYPE);
+ __ CmpInstanceType(map, JS_OBJECT_TYPE);
__ j(below, slow, not_taken);
}
// Checks whether a key is an array index string or a symbol string.
-// Falls through if a key is a symbol.
+// Falls through if the key is a symbol.
static void GenerateKeyStringCheck(MacroAssembler* masm,
Register key,
Register map,
// Picks out an array index from the hash field.
-// The generated code never falls through.
static void GenerateIndexFromHash(MacroAssembler* masm,
Register key,
- Register hash,
- Label* index_smi) {
+ Register hash) {
// Register use:
// key - holds the overwritten key on exit.
// hash - holds the key's hash. Clobbered.
(1 << String::kArrayIndexValueBits));
// We want the smi-tagged index in key. kArrayIndexValueMask has zeros in
// the low kHashShift bits.
- // key: string key
- // ebx: hash field.
ASSERT(String::kHashShift >= kSmiTagSize);
__ and_(hash, String::kArrayIndexValueMask);
__ shr(hash, String::kHashShift - kSmiTagSize);
// runtime later. However as the new key is the numeric value of a string key
// there is no difference in using either key.
__ mov(key, hash);
- // Now jump to the place where smi keys are handled.
- __ jmp(index_smi);
}
__ ret(0);
__ bind(&index_string);
- GenerateIndexFromHash(masm, eax, ebx, &index_smi);
+ GenerateIndexFromHash(masm, eax, ebx);
+ // Now jump to the place where smi keys are handled.
+ __ jmp(&index_smi);
}
// The generated code falls through if both probes miss.
static void GenerateMonomorphicCacheProbe(MacroAssembler* masm,
int argc,
- Code::Kind kind,
- Label* miss) {
+ Code::Kind kind) {
// ----------- S t a t e -------------
// -- ecx : name
// -- edx : receiver
// -----------------------------------
- Label number, non_number, non_string, boolean, probe;
+ Label number, non_number, non_string, boolean, probe, miss;
// Probe the stub cache.
Code::Flags flags =
__ cmp(edx, Factory::true_value());
__ j(equal, &boolean, not_taken);
__ cmp(edx, Factory::false_value());
- __ j(not_equal, miss, taken);
+ __ j(not_equal, &miss, taken);
__ bind(&boolean);
StubCompiler::GenerateLoadGlobalFunctionPrototype(
masm, Context::BOOLEAN_FUNCTION_INDEX, edx);
// Probe the stub cache for the value object.
__ bind(&probe);
StubCache::GenerateProbe(masm, flags, edx, ecx, ebx, no_reg);
+ __ bind(&miss);
}
__ InvokeFunction(edi, actual, JUMP_FUNCTION);
}
-// The generated code never falls through.
-static void GenerateCallNormal(MacroAssembler* masm, int argc, Label* miss) {
+// The generated code falls through if the call should be handled by runtime.
+static void GenerateCallNormal(MacroAssembler* masm, int argc) {
// ----------- S t a t e -------------
// -- ecx : name
// -- esp[0] : return address
// -- ...
// -- esp[(argc + 1) * 4] : receiver
// -----------------------------------
- Label global_object, non_global_object;
+ Label miss, global_object, non_global_object;
// Get the receiver of the function from the stack; 1 ~ return address.
__ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
// Check that the receiver isn't a smi.
__ test(edx, Immediate(kSmiTagMask));
- __ j(zero, miss, not_taken);
+ __ j(zero, &miss, not_taken);
// Check that the receiver is a valid JS object.
__ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset));
__ movzx_b(eax, FieldOperand(ebx, Map::kInstanceTypeOffset));
__ cmp(eax, FIRST_JS_OBJECT_TYPE);
- __ j(below, miss, not_taken);
+ __ j(below, &miss, not_taken);
// If this assert fails, we have to check upper bound too.
ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
// Check that the global object does not require access checks.
__ test_b(FieldOperand(ebx, Map::kBitFieldOffset),
1 << Map::kIsAccessCheckNeeded);
- __ j(not_equal, miss, not_taken);
- GenerateNormalHelper(masm, argc, true, miss);
+ __ j(not_equal, &miss, not_taken);
+ GenerateNormalHelper(masm, argc, true, &miss);
// Accessing non-global object: Check for access to global proxy.
Label global_proxy, invoke;
// require access checks.
__ test_b(FieldOperand(ebx, Map::kBitFieldOffset),
1 << Map::kIsAccessCheckNeeded);
- __ j(not_equal, miss, not_taken);
+ __ j(not_equal, &miss, not_taken);
__ bind(&invoke);
- GenerateNormalHelper(masm, argc, false, miss);
+ GenerateNormalHelper(masm, argc, false, &miss);
// Global object proxy access: Check access rights.
__ bind(&global_proxy);
- __ CheckAccessGlobalProxy(edx, eax, miss);
+ __ CheckAccessGlobalProxy(edx, eax, &miss);
__ jmp(&invoke);
+
+ __ bind(&miss);
}
// -- esp[(argc + 1) * 4] : receiver
// -----------------------------------
- Label miss;
// Get the receiver of the function from the stack; 1 ~ return address.
__ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
- GenerateMonomorphicCacheProbe(masm, argc, Code::CALL_IC, &miss);
- __ bind(&miss);
+ GenerateMonomorphicCacheProbe(masm, argc, Code::CALL_IC);
GenerateMiss(masm, argc);
}
void CallIC::GenerateNormal(MacroAssembler* masm, int argc) {
- Label miss;
- GenerateCallNormal(masm, argc, &miss);
- __ bind(&miss);
+ // ----------- S t a t e -------------
+ // -- ecx : name
+ // -- esp[0] : return address
+ // -- esp[(argc - n) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- esp[(argc + 1) * 4] : receiver
+ // -----------------------------------
+
+ GenerateCallNormal(masm, argc);
GenerateMiss(masm, argc);
}
void CallIC::GenerateMiss(MacroAssembler* masm, int argc) {
+ // ----------- S t a t e -------------
+ // -- ecx : name
+ // -- esp[0] : return address
+ // -- esp[(argc - n) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- esp[(argc + 1) * 4] : receiver
+ // -----------------------------------
+
GenerateCallMiss(masm, argc, IC::kCallIC_Miss);
}
GenerateKeyedLoadReceiverCheck(masm, edx, eax, &slow_call);
- GenerateFastArrayLoad(masm,
- edx,
- ecx,
- eax,
- edi,
- &check_number_dictionary,
- &slow_load);
+ GenerateFastArrayLoad(
+ masm, edx, ecx, eax, edi, &check_number_dictionary, &slow_load);
__ IncrementCounter(&Counters::keyed_call_generic_smi_fast, 1);
__ bind(&do_call);
__ SmiUntag(ebx);
// ebx: untagged index
// Receiver in edx will be clobbered, need to reload it on miss.
- GenerateNumberDictionaryLoad(masm,
- &slow_reload_receiver,
- eax,
- ecx,
- ebx,
- edx,
- edi,
- edi);
+ GenerateNumberDictionaryLoad(
+ masm, &slow_reload_receiver, eax, ecx, ebx, edx, edi, edi);
__ IncrementCounter(&Counters::keyed_call_generic_smi_dict, 1);
__ jmp(&do_call);
Immediate(Factory::hash_table_map()));
__ j(not_equal, &lookup_monomorphic_cache, not_taken);
- GenerateDictionaryLoad(masm,
- &slow_load,
- edx,
- ecx,
- ebx,
- eax,
- edi,
- edi,
- DICTIONARY_CHECK_DONE);
+ GenerateDictionaryLoad(
+ masm, &slow_load, edx, ecx, ebx, eax, edi, edi, DICTIONARY_CHECK_DONE);
__ IncrementCounter(&Counters::keyed_call_generic_lookup_dict, 1);
__ jmp(&do_call);
__ bind(&lookup_monomorphic_cache);
__ IncrementCounter(&Counters::keyed_call_generic_lookup_cache, 1);
- GenerateMonomorphicCacheProbe(masm, argc, Code::KEYED_CALL_IC, &slow_call);
+ GenerateMonomorphicCacheProbe(masm, argc, Code::KEYED_CALL_IC);
// Fall through on miss.
__ bind(&slow_call);
GenerateMiss(masm, argc);
__ bind(&index_string);
- GenerateIndexFromHash(masm, ecx, ebx, &index_smi);
+ GenerateIndexFromHash(masm, ecx, ebx);
+ // Now jump to the place where smi keys are handled.
+ __ jmp(&index_smi);
}
void KeyedCallIC::GenerateNormal(MacroAssembler* masm, int argc) {
- Label miss;
- GenerateCallNormal(masm, argc, &miss);
- __ bind(&miss);
+ // ----------- S t a t e -------------
+ // -- ecx : name
+ // -- esp[0] : return address
+ // -- esp[(argc - n) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- esp[(argc + 1) * 4] : receiver
+ // -----------------------------------
+
+ GenerateCallNormal(masm, argc);
GenerateMiss(masm, argc);
}
void KeyedCallIC::GenerateMiss(MacroAssembler* masm, int argc) {
+ // ----------- S t a t e -------------
+ // -- ecx : name
+ // -- esp[0] : return address
+ // -- esp[(argc - n) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- esp[(argc + 1) * 4] : receiver
+ // -----------------------------------
+
GenerateCallMiss(masm, argc, IC::kKeyedCallIC_Miss);
}
return *delegate;
}
+
void CallICBase::ReceiverToObject(Handle<Object> object) {
HandleScope scope;
Handle<Object> receiver(object);
state == MONOMORPHIC ||
state == MONOMORPHIC_PROTOTYPE_FAILURE) {
set_target(Code::cast(code));
+ } else if (state == MEGAMORPHIC) {
+ // Update the stub cache.
+ StubCache::Set(*name, GetCodeCacheMapForObject(*object), Code::cast(code));
}
#ifdef DEBUG
Code* target = NULL;
target = Builtins::builtin(Builtins::LoadIC_StringLength);
set_target(target);
- StubCache::Set(*name, map, target);
return Smi::FromInt(String::cast(*object)->length());
}
Code* target = Builtins::builtin(Builtins::LoadIC_ArrayLength);
set_target(target);
- StubCache::Set(*name, map, target);
return JSArray::cast(*object)->length();
}
#endif
Code* target = Builtins::builtin(Builtins::LoadIC_FunctionPrototype);
set_target(target);
- StubCache::Set(*name, HeapObject::cast(*object)->map(), target);
return Accessors::FunctionGetPrototype(*object, 0);
}
}
set_target(Code::cast(code));
} else if (state == MONOMORPHIC) {
set_target(megamorphic_stub());
+ } else if (state == MEGAMORPHIC) {
+ // Update the stub cache.
+ StubCache::Set(*name, GetCodeCacheMapForObject(*object), Code::cast(code));
}
#ifdef DEBUG
return *value;
}
-
// Use specialized code for setting the length of arrays.
if (receiver->IsJSArray()
&& name->Equals(Heap::length_symbol())
#endif
Code* target = Builtins::builtin(Builtins::StoreIC_ArrayLength);
set_target(target);
- StubCache::Set(*name, HeapObject::cast(*object)->map(), target);
return receiver->SetProperty(*name, *value, NONE);
}
if (state == UNINITIALIZED || state == MONOMORPHIC_PROTOTYPE_FAILURE) {
set_target(Code::cast(code));
} else if (state == MONOMORPHIC) {
- // Only move to mega morphic if the target changes.
+ // Only move to megamorphic if the target changes.
if (target() != Code::cast(code)) set_target(megamorphic_stub());
+ } else if (state == MEGAMORPHIC) {
+ // Update the stub cache.
+ StubCache::Set(*name, receiver->map(), Code::cast(code));
}
#ifdef DEBUG
CustomArguments args(interceptor->data(), receiver, this);
v8::AccessorInfo info(args.end());
if (!interceptor->query()->IsUndefined()) {
- v8::NamedPropertyQueryImpl query =
- v8::ToCData<v8::NamedPropertyQueryImpl>(interceptor->query());
+ v8::NamedPropertyQuery query =
+ v8::ToCData<v8::NamedPropertyQuery>(interceptor->query());
LOG(ApiNamedPropertyAccess("interceptor-named-has", *holder_handle, name));
- v8::Handle<v8::Value> result;
+ v8::Handle<v8::Integer> result;
{
// Leaving JavaScript.
VMState state(EXTERNAL);
result = query(v8::Utils::ToLocal(name_handle), info);
}
if (!result.IsEmpty()) {
- // Temporary complicated logic, would be removed soon.
- if (result->IsBoolean()) {
- // Convert the boolean result to a property attribute
- // specification.
- return result->IsTrue() ? NONE : ABSENT;
- } else {
- ASSERT(result->IsInt32());
- return static_cast<PropertyAttributes>(result->Int32Value());
- }
+ ASSERT(result->IsInt32());
+ return static_cast<PropertyAttributes>(result->Int32Value());
}
} else if (!interceptor->getter()->IsUndefined()) {
v8::NamedPropertyGetter getter =
static const uint32_t kExponentMask = 0x7ff00000u;
static const uint32_t kMantissaMask = 0xfffffu;
static const int kMantissaBits = 52;
- static const int KExponentBits = 11;
+ static const int kExponentBits = 11;
static const int kExponentBias = 1023;
static const int kExponentShift = 20;
static const int kMantissaBitsInTopWord = 20;
// Set the value for entry.
void ValueAtPut(int entry, Object* value) {
+ // Check that this value can actually be written.
+ PropertyDetails details = DetailsAt(entry);
+ // If a value has not been initilized we allow writing to it even if
+ // it is read only (a declared const that has not been initialized).
+ if (details.IsReadOnly() && !ValueAt(entry)->IsTheHole()) return;
this->set(HashTable<Shape, Key>::EntryToIndex(entry)+1, value);
}
// Flags layout.
static const int kFlagsICStateShift = 0;
static const int kFlagsICInLoopShift = 3;
- static const int kFlagsKindShift = 4;
- static const int kFlagsTypeShift = 8;
+ static const int kFlagsTypeShift = 4;
+ static const int kFlagsKindShift = 7;
static const int kFlagsArgumentsCountShift = 11;
static const int kFlagsICStateMask = 0x00000007; // 00000000111
static const int kFlagsICInLoopMask = 0x00000008; // 00000001000
- static const int kFlagsKindMask = 0x000000F0; // 00011110000
- static const int kFlagsTypeMask = 0x00000700; // 11100000000
+ static const int kFlagsTypeMask = 0x00000070; // 00001110000
+ static const int kFlagsKindMask = 0x00000780; // 11110000000
static const int kFlagsArgumentsCountMask = 0xFFFFF800;
static const int kFlagsNotUsedInLookup =
const char* HeapEntry::TypeAsString() {
switch (type_) {
case INTERNAL: return "/internal/";
- case JS_OBJECT: return "/object/";
+ case OBJECT: return "/object/";
case CLOSURE: return "/closure/";
case STRING: return "/string/";
case CODE: return "/code/";
return AddEntry(object, HeapEntry::CLOSURE, collection_->GetName(name));
} else if (object->IsJSObject()) {
return AddEntry(object,
- HeapEntry::JS_OBJECT,
+ HeapEntry::OBJECT,
collection_->GetName(
JSObject::cast(object)->constructor_name()));
} else if (object->IsJSGlobalPropertyCell()) {
return AddEntry(object,
HeapEntry::STRING,
collection_->GetName(String::cast(object)));
- } else if (object->IsCode()
- || object->IsSharedFunctionInfo()
- || object->IsScript()) {
+ } else if (object->IsCode()) {
return AddEntry(object, HeapEntry::CODE);
+ } else if (object->IsSharedFunctionInfo()) {
+ SharedFunctionInfo* shared = SharedFunctionInfo::cast(object);
+ String* name = String::cast(shared->name())->length() > 0 ?
+ String::cast(shared->name()) : shared->inferred_name();
+ return AddEntry(object, HeapEntry::CODE, collection_->GetName(name));
+ } else if (object->IsScript()) {
+ Script* script = Script::cast(object);
+ return AddEntry(object,
+ HeapEntry::CODE,
+ script->name()->IsString() ?
+ collection_->GetName(String::cast(script->name())) : "");
} else if (object->IsFixedArray()) {
return AddEntry(object, HeapEntry::ARRAY);
}
class HeapGraphEdge {
public:
enum Type {
- CONTEXT_VARIABLE,
- ELEMENT,
- PROPERTY
+ CONTEXT_VARIABLE = v8::HeapGraphEdge::CONTEXT_VARIABLE,
+ ELEMENT = v8::HeapGraphEdge::ELEMENT,
+ PROPERTY = v8::HeapGraphEdge::PROPERTY
};
HeapGraphEdge(Type type, const char* name, HeapEntry* from, HeapEntry* to);
class HeapEntry {
public:
enum Type {
- INTERNAL,
- ARRAY,
- STRING,
- JS_OBJECT,
- CODE,
- CLOSURE
+ INTERNAL = v8::HeapGraphNode::INTERNAL,
+ ARRAY = v8::HeapGraphNode::ARRAY,
+ STRING = v8::HeapGraphNode::STRING,
+ OBJECT = v8::HeapGraphNode::OBJECT,
+ CODE = v8::HeapGraphNode::CODE,
+ CLOSURE = v8::HeapGraphNode::CLOSURE
};
explicit HeapEntry(HeapSnapshot* snapshot)
PropertyDetails details = dictionary->DetailsAt(entry);
elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
elms->set(VALUE_INDEX, dictionary->ValueAt(entry));
- elms->set(WRITABLE_INDEX, Heap::ToBoolean(!details.IsDontDelete()));
+ elms->set(WRITABLE_INDEX, Heap::ToBoolean(!details.IsReadOnly()));
elms->set(ENUMERABLE_INDEX, Heap::ToBoolean(!details.IsDontEnum()));
- elms->set(CONFIGURABLE_INDEX, Heap::ToBoolean(!details.IsReadOnly()));
+ elms->set(CONFIGURABLE_INDEX, Heap::ToBoolean(!details.IsDontDelete()));
return *desc;
} else {
// Elements that are stored as array elements always has:
int unchecked = flag->value();
RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
+ PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
+
+ // Check if this is an element.
+ uint32_t index;
+ bool is_element = name->AsArrayIndex(&index);
+
+ // Special case for elements if any of the flags are true.
+ // If elements are in fast case we always implicitly assume that:
+ // DONT_DELETE: false, DONT_ENUM: false, READ_ONLY: false.
+ if (((unchecked & (DONT_DELETE | DONT_ENUM | READ_ONLY)) != 0) &&
+ is_element) {
+ // Normalize the elements to enable attributes on the property.
+ js_object->NormalizeElements();
+ NumberDictionary* dictionary = js_object->element_dictionary();
+ // Make sure that we never go back to fast case.
+ dictionary->set_requires_slow_elements();
+ PropertyDetails details = PropertyDetails(attr, NORMAL);
+ dictionary->Set(index, *obj_value, details);
+ }
+
LookupResult result;
js_object->LocalLookupRealNamedProperty(*name, &result);
- PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
-
// Take special care when attributes are different and there is already
// a property. For simplicity we normalize the property which enables us
// to not worry about changing the instance_descriptor and creating a new
*obj_value,
attr);
}
+
return Runtime::SetObjectProperty(js_object, name, obj_value, attr);
}
UNCLASSIFIED,
6,
"RegExpStack::limit_address()");
- Add(ExternalReference::new_space_start().address(),
+ Add(ExternalReference::address_of_regexp_stack_memory_address().address(),
UNCLASSIFIED,
7,
+ "RegExpStack::memory_address()");
+ Add(ExternalReference::address_of_regexp_stack_memory_size().address(),
+ UNCLASSIFIED,
+ 8,
+ "RegExpStack::memory_size()");
+ Add(ExternalReference::address_of_static_offsets_vector().address(),
+ UNCLASSIFIED,
+ 9,
+ "OffsetsVector::static_offsets_vector");
+ Add(ExternalReference::new_space_start().address(),
+ UNCLASSIFIED,
+ 10,
"Heap::NewSpaceStart()");
Add(ExternalReference::new_space_mask().address(),
UNCLASSIFIED,
- 8,
+ 11,
"Heap::NewSpaceMask()");
Add(ExternalReference::heap_always_allocate_scope_depth().address(),
UNCLASSIFIED,
- 9,
+ 12,
"Heap::always_allocate_scope_depth()");
Add(ExternalReference::new_space_allocation_limit_address().address(),
UNCLASSIFIED,
- 10,
+ 13,
"Heap::NewSpaceAllocationLimitAddress()");
Add(ExternalReference::new_space_allocation_top_address().address(),
UNCLASSIFIED,
- 11,
+ 14,
"Heap::NewSpaceAllocationTopAddress()");
#ifdef ENABLE_DEBUGGER_SUPPORT
Add(ExternalReference::debug_break().address(),
UNCLASSIFIED,
- 12,
+ 15,
"Debug::Break()");
Add(ExternalReference::debug_step_in_fp_address().address(),
UNCLASSIFIED,
- 13,
+ 16,
"Debug::step_in_fp_addr()");
#endif
Add(ExternalReference::double_fp_operation(Token::ADD).address(),
UNCLASSIFIED,
- 14,
+ 17,
"add_two_doubles");
Add(ExternalReference::double_fp_operation(Token::SUB).address(),
UNCLASSIFIED,
- 15,
+ 18,
"sub_two_doubles");
Add(ExternalReference::double_fp_operation(Token::MUL).address(),
UNCLASSIFIED,
- 16,
+ 19,
"mul_two_doubles");
Add(ExternalReference::double_fp_operation(Token::DIV).address(),
UNCLASSIFIED,
- 17,
+ 20,
"div_two_doubles");
Add(ExternalReference::double_fp_operation(Token::MOD).address(),
UNCLASSIFIED,
- 18,
+ 21,
"mod_two_doubles");
Add(ExternalReference::compare_doubles().address(),
UNCLASSIFIED,
- 19,
+ 22,
"compare_doubles");
#ifndef V8_INTERPRETED_REGEXP
Add(ExternalReference::re_case_insensitive_compare_uc16().address(),
UNCLASSIFIED,
- 20,
+ 23,
"NativeRegExpMacroAssembler::CaseInsensitiveCompareUC16()");
Add(ExternalReference::re_check_stack_guard_state().address(),
UNCLASSIFIED,
- 21,
+ 24,
"RegExpMacroAssembler*::CheckStackGuardState()");
Add(ExternalReference::re_grow_stack().address(),
UNCLASSIFIED,
- 22,
+ 25,
"NativeRegExpMacroAssembler::GrowStack()");
Add(ExternalReference::re_word_character_map().address(),
UNCLASSIFIED,
- 23,
+ 26,
"NativeRegExpMacroAssembler::word_character_map");
#endif // V8_INTERPRETED_REGEXP
// Keyed lookup cache.
Add(ExternalReference::keyed_lookup_cache_keys().address(),
UNCLASSIFIED,
- 24,
+ 27,
"KeyedLookupCache::keys()");
Add(ExternalReference::keyed_lookup_cache_field_offsets().address(),
UNCLASSIFIED,
- 25,
+ 28,
"KeyedLookupCache::field_offsets()");
Add(ExternalReference::transcendental_cache_array_address().address(),
UNCLASSIFIED,
- 26,
+ 29,
"TranscendentalCache::caches()");
}
receiver->map()->UpdateCodeCache(cache_name, Code::cast(code));
if (result->IsFailure()) return result;
}
- return Set(name, receiver->map(), Code::cast(code));
+ return code;
}
Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
if (result->IsFailure()) return result;
}
- return Set(name, receiver->map(), Code::cast(code));
+ return code;
}
Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
if (result->IsFailure()) return result;
}
- return Set(name, receiver->map(), Code::cast(code));
+ return code;
}
Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
if (result->IsFailure()) return result;
}
- return Set(name, receiver->map(), Code::cast(code));
+ return code;
}
Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
if (result->IsFailure()) return result;
}
- return Set(name, receiver->map(), Code::cast(code));
+ return code;
}
Object* StubCache::ComputeLoadNormal(String* name, JSObject* receiver) {
- Code* code = Builtins::builtin(Builtins::LoadIC_Normal);
- return Set(name, receiver->map(), code);
+ return Builtins::builtin(Builtins::LoadIC_Normal);
}
Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
if (result->IsFailure()) return result;
}
- return Set(name, receiver->map(), Code::cast(code));
+ return code;
}
Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
if (result->IsFailure()) return result;
}
- return Set(name, receiver->map(), Code::cast(code));
+ return code;
}
Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
if (result->IsFailure()) return result;
}
- return Set(name, receiver->map(), Code::cast(code));
+ return code;
}
Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
if (result->IsFailure()) return result;
}
- return Set(name, receiver->map(), Code::cast(code));
+ return code;
}
Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
if (result->IsFailure()) return result;
}
- return Set(name, receiver->map(), Code::cast(code));
+ return code;
}
Object* result = map->UpdateCodeCache(name, Code::cast(code));
if (result->IsFailure()) return result;
}
- return Set(name, map, Code::cast(code));
+ return code;
}
Object* result = map->UpdateCodeCache(name, Code::cast(code));
if (result->IsFailure()) return result;
}
- return Set(name, map, Code::cast(code));
+ return code;
}
Object* result = map->UpdateCodeCache(name, Code::cast(code));
if (result->IsFailure()) return result;
}
- return Set(name, map, Code::cast(code));
+ return code;
}
JSObject* receiver) {
Object* code = ComputeCallNormal(argc, in_loop, kind);
if (code->IsFailure()) return code;
- return Set(name, receiver->map(), Code::cast(code));
+ return code;
}
Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
if (result->IsFailure()) return result;
}
- return Set(name, receiver->map(), Code::cast(code));
+ return code;
}
}
+// X must be a power of 2. Returns the number of trailing zeros.
+template <typename T>
+static inline int WhichPowerOf2(T x) {
+ ASSERT(IsPowerOf2(x));
+ ASSERT(x != 0);
+ if (x < 0) return 31;
+ int bits = 0;
+#ifdef DEBUG
+ int original_x = x;
+#endif
+ if (x >= 0x10000) {
+ bits += 16;
+ x >>= 16;
+ }
+ if (x >= 0x100) {
+ bits += 8;
+ x >>= 8;
+ }
+ if (x >= 0x10) {
+ bits += 4;
+ x >>= 4;
+ }
+ switch (x) {
+ default: UNREACHABLE();
+ case 8: bits++; // Fall through.
+ case 4: bits++; // Fall through.
+ case 2: bits++; // Fall through.
+ case 1: break;
+ }
+ ASSERT_EQ(1 << bits, original_x);
+ return bits;
+ return 0;
+}
+
+
// The C++ standard leaves the semantics of '>>' undefined for
// negative signed operands. Most implementations do the right thing,
// though.
#include "serialize.h"
#include "simulator.h"
#include "stub-cache.h"
+#include "heap-profiler.h"
#include "oprofile-agent.h"
#include "log.h"
Logger::Setup();
CpuProfiler::Setup();
+ HeapProfiler::Setup();
// Setup the platform OS support.
OS::Setup();
Top::TearDown();
+ HeapProfiler::TearDown();
+
CpuProfiler::TearDown();
Heap::TearDown();
// cannot be changed without changing the SCons build script.
#define MAJOR_VERSION 2
#define MINOR_VERSION 2
-#define BUILD_NUMBER 17
+#define BUILD_NUMBER 18
#define PATCH_LEVEL 0
#define CANDIDATE_VERSION false
void VirtualFrame::PrepareForReturn() {
- SpillAll();
+ // Don't bother flushing tos registers as returning does not require more
+ // access to the expression stack.
+ top_of_stack_state_ = NO_TOS_REGISTERS;
}
ref.GetValue();
// Use global object as receiver.
LoadGlobalReceiver();
+ // Call the function.
+ CallWithArguments(args, RECEIVER_MIGHT_BE_VALUE, node->position());
} else {
- Reference ref(this, property, false);
- ASSERT(ref.size() == 2);
- Result key = frame_->Pop();
- frame_->Dup(); // Duplicate the receiver.
- frame_->Push(&key);
- ref.GetValue();
- // Top of frame contains function to call, with duplicate copy of
- // receiver below it. Swap them.
- Result function = frame_->Pop();
- Result receiver = frame_->Pop();
- frame_->Push(&function);
- frame_->Push(&receiver);
- }
+ // Push the receiver onto the frame.
+ Load(property->obj());
- // Call the function.
- CallWithArguments(args, RECEIVER_MIGHT_BE_VALUE, node->position());
- }
+ // Load the arguments.
+ int arg_count = args->length();
+ for (int i = 0; i < arg_count; i++) {
+ Load(args->at(i));
+ frame_->SpillTop();
+ }
+ // Load the name of the function.
+ Load(property->key());
+
+ // Call the IC initialization code.
+ CodeForSourcePosition(node->position());
+ Result result = frame_->CallKeyedCallIC(RelocInfo::CODE_TARGET,
+ arg_count,
+ loop_nesting());
+ frame_->RestoreContextRegister();
+ frame_->Push(&result);
+ }
+ }
} else {
// ----------------------------------
// JavaScript example: 'foo(1, 2, 3)' // foo is not global
__ bind(&cache_miss);
__ push(cache_); // store a reference to cache
__ push(key_); // store a key
- Handle<Object> receiver(Top::global_context()->global());
- __ Push(receiver);
+ __ push(Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX)));
__ push(key_);
// On x64 function must be in rdi.
__ movq(rdi, FieldOperand(cache_, JSFunctionResultCache::kFactoryOffset));
// ST[0] == double value
// rbx = bits of double value.
// rdx = also bits of double value.
- // Compute hash (h is 32 bits, bits are 64):
+ // Compute hash (h is 32 bits, bits are 64 and the shifts are arithmetic):
// h = h0 = bits ^ (bits >> 32);
// h ^= h >> 16;
// h ^= h >> 8;
__ movl(rcx, rdx);
__ movl(rax, rdx);
__ movl(rdi, rdx);
- __ shrl(rdx, Immediate(8));
- __ shrl(rcx, Immediate(16));
- __ shrl(rax, Immediate(24));
+ __ sarl(rdx, Immediate(8));
+ __ sarl(rcx, Immediate(16));
+ __ sarl(rax, Immediate(24));
__ xorl(rcx, rdx);
__ xorl(rax, rdi);
__ xorl(rcx, rax);
// Move exponent and sign bits to low bits.
__ shr(rdi, Immediate(HeapNumber::kMantissaBits));
// Remove sign bit.
- __ andl(rdi, Immediate((1 << HeapNumber::KExponentBits) - 1));
+ __ andl(rdi, Immediate((1 << HeapNumber::kExponentBits) - 1));
int supported_exponent_limit = (63 + HeapNumber::kExponentBias);
__ cmpl(rdi, Immediate(supported_exponent_limit));
__ j(below, &in_range);
// Double to remove sign bit, shift exponent down to least significant bits.
// and subtract bias to get the unshifted, unbiased exponent.
__ lea(double_exponent, Operand(double_value, double_value, times_1, 0));
- __ shr(double_exponent, Immediate(64 - HeapNumber::KExponentBits));
+ __ shr(double_exponent, Immediate(64 - HeapNumber::kExponentBits));
__ subl(double_exponent, Immediate(HeapNumber::kExponentBias));
// Check whether the exponent is too big for a 63 bit unsigned integer.
__ cmpl(double_exponent, Immediate(63));
__ cmpl(rdx, rax);
__ j(greater, &runtime);
- // ecx: RegExp data (FixedArray)
+ // rcx: RegExp data (FixedArray)
// Check the representation and encoding of the subject string.
- Label seq_string, seq_two_byte_string, check_code;
- const int kStringRepresentationEncodingMask =
- kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask;
+ Label seq_ascii_string, seq_two_byte_string, check_code;
__ movq(rax, Operand(rsp, kSubjectOffset));
__ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset));
__ movzxbl(rbx, FieldOperand(rbx, Map::kInstanceTypeOffset));
- __ andb(rbx, Immediate(kStringRepresentationEncodingMask));
- // First check for sequential string.
- ASSERT_EQ(0, kStringTag);
- ASSERT_EQ(0, kSeqStringTag);
+ // First check for flat two byte string.
+ __ andb(rbx, Immediate(
+ kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask));
+ ASSERT_EQ(0, kStringTag | kSeqStringTag | kTwoByteStringTag);
+ __ j(zero, &seq_two_byte_string);
+ // Any other flat string must be a flat ascii string.
__ testb(rbx, Immediate(kIsNotStringMask | kStringRepresentationMask));
- __ j(zero, &seq_string);
+ __ j(zero, &seq_ascii_string);
// Check for flat cons string.
// A flat cons string is a cons string where the second part is the empty
// string. In that case the subject string is just the first part of the cons
// string. Also in this case the first part of the cons string is known to be
// a sequential string or an external string.
- __ andb(rbx, Immediate(kStringRepresentationMask));
- __ cmpb(rbx, Immediate(kConsStringTag));
- __ j(not_equal, &runtime);
+ ASSERT(kExternalStringTag !=0);
+ ASSERT_EQ(0, kConsStringTag & kExternalStringTag);
+ __ testb(rbx, Immediate(kIsNotStringMask | kExternalStringTag));
+ __ j(not_zero, &runtime);
+ // String is a cons string.
__ movq(rdx, FieldOperand(rax, ConsString::kSecondOffset));
__ Cmp(rdx, Factory::empty_string());
__ j(not_equal, &runtime);
__ movq(rax, FieldOperand(rax, ConsString::kFirstOffset));
__ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset));
- __ movzxbl(rbx, FieldOperand(rbx, Map::kInstanceTypeOffset));
- ASSERT_EQ(0, kSeqStringTag);
- __ testb(rbx, Immediate(kStringRepresentationMask));
+ // String is a cons string with empty second part.
+ // eax: first part of cons string.
+ // ebx: map of first part of cons string.
+ // Is first part a flat two byte string?
+ __ testb(FieldOperand(rbx, Map::kInstanceTypeOffset),
+ Immediate(kStringRepresentationMask | kStringEncodingMask));
+ ASSERT_EQ(0, kSeqStringTag | kTwoByteStringTag);
+ __ j(zero, &seq_two_byte_string);
+ // Any other flat string must be ascii.
+ __ testb(FieldOperand(rbx, Map::kInstanceTypeOffset),
+ Immediate(kStringRepresentationMask));
__ j(not_zero, &runtime);
- __ andb(rbx, Immediate(kStringRepresentationEncodingMask));
- __ bind(&seq_string);
- // rax: subject string (sequential either ascii to two byte)
- // rbx: suject string type & kStringRepresentationEncodingMask
+ __ bind(&seq_ascii_string);
+ // rax: subject string (sequential ascii)
// rcx: RegExp data (FixedArray)
- // Check that the irregexp code has been generated for an ascii string. If
- // it has, the field contains a code object otherwise it contains the hole.
- const int kSeqTwoByteString = kStringTag | kSeqStringTag | kTwoByteStringTag;
- __ cmpb(rbx, Immediate(kSeqTwoByteString));
- __ j(equal, &seq_two_byte_string);
- if (FLAG_debug_code) {
- __ cmpb(rbx, Immediate(kStringTag | kSeqStringTag | kAsciiStringTag));
- __ Check(equal, "Expected sequential ascii string");
- }
__ movq(r12, FieldOperand(rcx, JSRegExp::kDataAsciiCodeOffset));
__ Set(rdi, 1); // Type is ascii.
__ jmp(&check_code);
__ bind(&seq_two_byte_string);
- // rax: subject string
+ // rax: subject string (flat two-byte)
// rcx: RegExp data (FixedArray)
__ movq(r12, FieldOperand(rcx, JSRegExp::kDataUC16CodeOffset));
__ Set(rdi, 0); // Type is two byte.
}
+void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr,
+ Expression* key,
+ RelocInfo::Mode mode) {
+ // Code common for calls using the IC.
+ ZoneList<Expression*>* args = expr->arguments();
+ int arg_count = args->length();
+ for (int i = 0; i < arg_count; i++) {
+ VisitForValue(args->at(i), kStack);
+ }
+ VisitForValue(key, kAccumulator);
+ __ movq(rcx, rax);
+ // Record source position for debugger.
+ SetSourcePosition(expr->position());
+ // Call the IC initialization code.
+ InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
+ Handle<Code> ic = CodeGenerator::ComputeKeyedCallInitialize(arg_count,
+ in_loop);
+ __ Call(ic, mode);
+ // Restore context register.
+ __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
+ Apply(context_, rax);
+}
+
+
void FullCodeGenerator::EmitCallWithStub(Call* expr) {
// Code common for calls using the call stub.
ZoneList<Expression*>* args = expr->arguments();
VisitForValue(prop->obj(), kStack);
EmitCallWithIC(expr, key->handle(), RelocInfo::CODE_TARGET);
} else {
- // Call to a keyed property, use keyed load IC followed by function
- // call.
+ // Call to a keyed property.
+ // For a synthetic property use keyed load IC followed by function call,
+ // for a regular property use KeyedCallIC.
VisitForValue(prop->obj(), kStack);
- VisitForValue(prop->key(), kAccumulator);
- __ movq(rdx, Operand(rsp, 0));
- // Record source code position for IC call.
- SetSourcePosition(prop->position());
- Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
- __ call(ic, RelocInfo::CODE_TARGET);
- // By emitting a nop we make sure that we do not have a "test rax,..."
- // instruction after the call it is treated specially by the LoadIC code.
- __ nop();
- // Pop receiver.
- __ pop(rbx);
- // Push result (function).
- __ push(rax);
- // Push receiver object on stack.
if (prop->is_synthetic()) {
+ VisitForValue(prop->key(), kAccumulator);
+ __ movq(rdx, Operand(rsp, 0));
+ // Record source code position for IC call.
+ SetSourcePosition(prop->position());
+ Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
+ __ call(ic, RelocInfo::CODE_TARGET);
+ // By emitting a nop we make sure that we do not have a "test rax,..."
+ // instruction after the call as it is treated specially
+ // by the LoadIC code.
+ __ nop();
+ // Pop receiver.
+ __ pop(rbx);
+ // Push result (function).
+ __ push(rax);
+ // Push receiver object on stack.
__ movq(rcx, CodeGenerator::GlobalObject());
__ push(FieldOperand(rcx, GlobalObject::kGlobalReceiverOffset));
+ EmitCallWithStub(expr);
} else {
- __ push(rbx);
+ EmitKeyedCallWithIC(expr, prop->key(), RelocInfo::CODE_TARGET);
}
- EmitCallWithStub(expr);
}
} else {
// Call to some other expression. If the expression is an anonymous
Register r2,
Register name,
Register r4,
+ Register result,
DictionaryCheck check_dictionary) {
// Register use:
//
- // r0 - used to hold the property dictionary.
+ // r0 - used to hold the property dictionary and is unchanged.
//
- // r1 - initially the receiver.
- // - unchanged on any jump to miss_label.
- // - holds the result on exit.
+ // r1 - used to hold the receiver and is unchanged.
//
// r2 - used to hold the capacity of the property dictionary.
//
// name - holds the name of the property and is unchanged.
+ //
// r4 - used to hold the index into the property dictionary.
+ //
+ // result - holds the result on exit if the load succeeded.
Label done;
// Get the value at the masked, scaled index.
const int kValueOffset = kElementsStartOffset + kPointerSize;
- __ movq(r1,
+ __ movq(result,
Operand(r0, r4, times_pointer_size, kValueOffset - kHeapObjectTag));
}
Register key,
Register r0,
Register r1,
- Register r2) {
+ Register r2,
+ Register result) {
// Register use:
//
- // elements - holds the slow-case elements of the receiver and is unchanged.
+ // elements - holds the slow-case elements of the receiver on entry.
+ // Unchanged unless 'result' is the same register.
//
- // key - holds the smi key on entry and is unchanged if a branch is
- // performed to the miss label.
- // Holds the result on exit if the load succeeded.
+ // key - holds the smi key on entry.
+ // Unchanged unless 'result' is the same register.
//
// Scratch registers:
//
// r1 - used to hold the capacity mask of the dictionary
//
// r2 - used for the index into the dictionary.
+ //
+ // result - holds the result on exit if the load succeeded.
+ // Allowed to be the same as 'key' or 'result'.
+ // Unchanged on bailout so 'key' or 'result' can be used
+ // in further computation.
+
Label done;
// Compute the hash code from the untagged key. This must be kept in sync
// Get the value at the masked, scaled index.
const int kValueOffset =
NumberDictionary::kElementsStartOffset + kPointerSize;
- __ movq(key, FieldOperand(elements, r2, times_pointer_size, kValueOffset));
+ __ movq(result, FieldOperand(elements, r2, times_pointer_size, kValueOffset));
}
}
-void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
- // ----------- S t a t e -------------
- // -- rax : key
- // -- rdx : receiver
- // -- rsp[0] : return address
- // -----------------------------------
- Label slow, check_string, index_smi, index_string;
- Label check_pixel_array, probe_dictionary, check_number_dictionary;
+// Checks the receiver for special cases (value type, slow case bits).
+// Falls through for regular JS object.
+static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm,
+ Register receiver,
+ Register map,
+ Label* slow) {
+ // Register use:
+ // receiver - holds the receiver and is unchanged.
+ // Scratch registers:
+ // map - used to hold the map of the receiver.
// Check that the object isn't a smi.
- __ JumpIfSmi(rdx, &slow);
+ __ JumpIfSmi(receiver, slow);
// Check that the object is some kind of JS object EXCEPT JS Value type.
// In the case that the object is a value-wrapper object,
// we enter the runtime system to make sure that indexing
// into string objects work as intended.
ASSERT(JS_OBJECT_TYPE > JS_VALUE_TYPE);
- __ CmpObjectType(rdx, JS_OBJECT_TYPE, rcx);
- __ j(below, &slow);
+ __ CmpObjectType(receiver, JS_OBJECT_TYPE, map);
+ __ j(below, slow);
// Check bit field.
- __ testb(FieldOperand(rcx, Map::kBitFieldOffset),
- Immediate(kSlowCaseBitFieldMask));
- __ j(not_zero, &slow);
+ __ testb(FieldOperand(map, Map::kBitFieldOffset),
+ Immediate(KeyedLoadIC::kSlowCaseBitFieldMask));
+ __ j(not_zero, slow);
+}
- // Check that the key is a smi.
- __ JumpIfNotSmi(rax, &check_string);
- __ bind(&index_smi);
- // Now the key is known to be a smi. This place is also jumped to from below
- // where a numeric string is converted to a smi.
- __ movq(rcx, FieldOperand(rdx, JSObject::kElementsOffset));
+
+// Loads an indexed element from a fast case array.
+static void GenerateFastArrayLoad(MacroAssembler* masm,
+ Register receiver,
+ Register key,
+ Register elements,
+ Register scratch,
+ Register result,
+ Label* not_fast_array,
+ Label* out_of_range) {
+ // Register use:
+ //
+ // receiver - holds the receiver on entry.
+ // Unchanged unless 'result' is the same register.
+ //
+ // key - holds the smi key on entry.
+ // Unchanged unless 'result' is the same register.
+ //
+ // elements - holds the elements of the receiver on exit.
+ //
+ // result - holds the result on exit if the load succeeded.
+ // Allowed to be the the same as 'receiver' or 'key'.
+ // Unchanged on bailout so 'receiver' and 'key' can be safely
+ // used by further computation.
+ //
+ // Scratch registers:
+ //
+ // scratch - used to hold elements of the receiver and the loaded value.
+
+ __ movq(elements, FieldOperand(receiver, JSObject::kElementsOffset));
// Check that the object is in fast mode (not dictionary).
- __ CompareRoot(FieldOperand(rcx, HeapObject::kMapOffset),
+ __ CompareRoot(FieldOperand(elements, HeapObject::kMapOffset),
Heap::kFixedArrayMapRootIndex);
- __ j(not_equal, &check_pixel_array);
+ __ j(not_equal, not_fast_array);
// Check that the key (index) is within bounds.
- __ SmiCompare(rax, FieldOperand(rcx, FixedArray::kLengthOffset));
- __ j(above_equal, &slow); // Unsigned comparison rejects negative indices.
+ __ SmiCompare(key, FieldOperand(elements, FixedArray::kLengthOffset));
+ // Unsigned comparison rejects negative indices.
+ __ j(above_equal, out_of_range);
// Fast case: Do the load.
- SmiIndex index = masm->SmiToIndex(rbx, rax, kPointerSizeLog2);
- __ movq(rbx, FieldOperand(rcx,
- index.reg,
- index.scale,
- FixedArray::kHeaderSize));
- __ CompareRoot(rbx, Heap::kTheHoleValueRootIndex);
+ SmiIndex index = masm->SmiToIndex(scratch, key, kPointerSizeLog2);
+ __ movq(scratch, FieldOperand(elements,
+ index.reg,
+ index.scale,
+ FixedArray::kHeaderSize));
+ __ CompareRoot(scratch, Heap::kTheHoleValueRootIndex);
// In case the loaded value is the_hole we have to consult GetProperty
// to ensure the prototype chain is searched.
- __ j(equal, &slow);
- __ movq(rax, rbx);
+ __ j(equal, out_of_range);
+ if (!result.is(scratch)) {
+ __ movq(result, scratch);
+ }
+}
+
+
+// Checks whether a key is an array index string or a symbol string.
+// Falls through if the key is a symbol.
+static void GenerateKeyStringCheck(MacroAssembler* masm,
+ Register key,
+ Register map,
+ Register hash,
+ Label* index_string,
+ Label* not_symbol) {
+ // Register use:
+ // key - holds the key and is unchanged. Assumed to be non-smi.
+ // Scratch registers:
+ // map - used to hold the map of the key.
+ // hash - used to hold the hash of the key.
+ __ CmpObjectType(key, FIRST_NONSTRING_TYPE, map);
+ __ j(above_equal, not_symbol);
+ // Is the string an array index, with cached numeric value?
+ __ movl(hash, FieldOperand(key, String::kHashFieldOffset));
+ __ testl(hash, Immediate(String::kContainsCachedArrayIndexMask));
+ __ j(zero, index_string); // The value in hash is used at jump target.
+
+ // Is the string a symbol?
+ ASSERT(kSymbolTag != 0);
+ __ testb(FieldOperand(map, Map::kInstanceTypeOffset),
+ Immediate(kIsSymbolMask));
+ __ j(zero, not_symbol);
+}
+
+
+// Picks out an array index from the hash field.
+static void GenerateIndexFromHash(MacroAssembler* masm,
+ Register key,
+ Register hash) {
+ // Register use:
+ // key - holds the overwritten key on exit.
+ // hash - holds the key's hash. Clobbered.
+
+ // The assert checks that the constants for the maximum number of digits
+ // for an array index cached in the hash field and the number of bits
+ // reserved for it does not conflict.
+ ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) <
+ (1 << String::kArrayIndexValueBits));
+ // We want the smi-tagged index in key. Even if we subsequently go to
+ // the slow case, converting the key to a smi is always valid.
+ // key: string key
+ // hash: key's hash field, including its array index value.
+ __ and_(hash, Immediate(String::kArrayIndexValueMask));
+ __ shr(hash, Immediate(String::kHashShift));
+ // Here we actually clobber the key which will be used if calling into
+ // runtime later. However as the new key is the numeric value of a string key
+ // there is no difference in using either key.
+ __ Integer32ToSmi(key, hash);
+}
+
+
+void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- rax : key
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+ Label slow, check_string, index_smi, index_string;
+ Label check_pixel_array, probe_dictionary, check_number_dictionary;
+
+ GenerateKeyedLoadReceiverCheck(masm, rdx, rcx, &slow);
+
+ // Check that the key is a smi.
+ __ JumpIfNotSmi(rax, &check_string);
+ __ bind(&index_smi);
+ // Now the key is known to be a smi. This place is also jumped to from below
+ // where a numeric string is converted to a smi.
+
+ GenerateFastArrayLoad(masm,
+ rdx,
+ rax,
+ rcx,
+ rbx,
+ rax,
+ &check_pixel_array,
+ &slow);
__ IncrementCounter(&Counters::keyed_load_generic_smi, 1);
__ ret(0);
__ CompareRoot(FieldOperand(rcx, HeapObject::kMapOffset),
Heap::kHashTableMapRootIndex);
__ j(not_equal, &slow);
- GenerateNumberDictionaryLoad(masm, &slow, rcx, rax, rbx, r9, rdi);
+ GenerateNumberDictionaryLoad(masm, &slow, rcx, rax, rbx, r9, rdi, rax);
__ ret(0);
__ bind(&slow);
GenerateRuntimeGetProperty(masm);
__ bind(&check_string);
- // The key is not a smi.
- // Is it a string?
- // rdx: receiver
- // rax: key
- __ CmpObjectType(rax, FIRST_NONSTRING_TYPE, rcx);
- __ j(above_equal, &slow);
- // Is the string an array index, with cached numeric value?
- __ movl(rbx, FieldOperand(rax, String::kHashFieldOffset));
- __ testl(rbx, Immediate(String::kContainsCachedArrayIndexMask));
- __ j(zero, &index_string); // The value in rbx is used at jump target.
-
- // Is the string a symbol?
- ASSERT(kSymbolTag != 0);
- __ testb(FieldOperand(rcx, Map::kInstanceTypeOffset),
- Immediate(kIsSymbolMask));
- __ j(zero, &slow);
+ GenerateKeyStringCheck(masm, rax, rcx, rbx, &index_string, &slow);
// If the receiver is a fast-case object, check the keyed lookup
// cache. Otherwise probe the dictionary leaving result in rcx.
rcx,
rax,
rdi,
+ rax,
DICTIONARY_CHECK_DONE);
- __ movq(rax, rdx);
__ IncrementCounter(&Counters::keyed_load_generic_symbol, 1);
__ ret(0);
- // If the hash field contains an array index pick it out. The assert checks
- // that the constants for the maximum number of digits for an array index
- // cached in the hash field and the number of bits reserved for it does not
- // conflict.
- ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) <
- (1 << String::kArrayIndexValueBits));
+
__ bind(&index_string);
- // We want the smi-tagged index in rax. Even if we subsequently go to
- // the slow case, converting the key to a smi is always valid.
- // rdx: receiver
- // rax: key (a string)
- // rbx: key's hash field, including its array index value.
- __ and_(rbx, Immediate(String::kArrayIndexValueMask));
- __ shr(rbx, Immediate(String::kHashShift));
- // Here we actually clobber the key (rax) which will be used if calling into
- // runtime later. However as the new key is the numeric value of a string key
- // there is no difference in using either key.
- __ Integer32ToSmi(rax, rbx);
- // Now jump to the place where smi keys are handled.
+ GenerateIndexFromHash(masm, rax, rbx);
__ jmp(&index_smi);
}
}
-void CallIC::GenerateMiss(MacroAssembler* masm, int argc) {
+// Defined in ic.cc.
+Object* CallIC_Miss(Arguments args);
+
+
+static void GenerateCallMiss(MacroAssembler* masm, int argc, IC::UtilityId id) {
// ----------- S t a t e -------------
// rcx : function name
// rsp[0] : return address
// Call the entry.
CEntryStub stub(1);
__ movq(rax, Immediate(2));
- __ movq(rbx, ExternalReference(IC_Utility(kCallIC_Miss)));
+ __ movq(rbx, ExternalReference(IC_Utility(id)));
__ CallStub(&stub);
// Move result to rdi and exit the internal frame.
}
-// Defined in ic.cc.
-Object* CallIC_Miss(Arguments args);
-
-void CallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
+// The generated code does not accept smi keys.
+// The generated code falls through if both probes miss.
+static void GenerateMonomorphicCacheProbe(MacroAssembler* masm,
+ int argc,
+ Code::Kind kind) {
// ----------- S t a t e -------------
// rcx : function name
- // rsp[0] : return address
- // rsp[8] : argument argc
- // rsp[16] : argument argc - 1
- // ...
- // rsp[argc * 8] : argument 1
- // rsp[(argc + 1) * 8] : argument 0 = receiver
+ // rdx : receiver
// -----------------------------------
Label number, non_number, non_string, boolean, probe, miss;
- // Get the receiver of the function from the stack; 1 ~ return address.
- __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
-
// Probe the stub cache.
Code::Flags flags =
- Code::ComputeFlags(Code::CALL_IC, NOT_IN_LOOP, MONOMORPHIC, NORMAL, argc);
+ Code::ComputeFlags(kind, NOT_IN_LOOP, MONOMORPHIC, NORMAL, argc);
StubCache::GenerateProbe(masm, flags, rdx, rcx, rbx, rax);
// If the stub cache probing failed, the receiver might be a value.
__ bind(&probe);
StubCache::GenerateProbe(masm, flags, rdx, rcx, rbx, no_reg);
- // Cache miss: Jump to runtime.
__ bind(&miss);
- GenerateMiss(masm, argc);
}
// rsp[(argc + 1) * 8] : argument 0 = receiver
// -----------------------------------
// Search dictionary - put result in register rdx.
- GenerateDictionaryLoad(masm, miss, rax, rdx, rbx, rcx, rdi, CHECK_DICTIONARY);
-
- // Move the result to register rdi and check that it isn't a smi.
- __ movq(rdi, rdx);
- __ JumpIfSmi(rdx, miss);
+ GenerateDictionaryLoad(
+ masm, miss, rax, rdx, rbx, rcx, rdi, rdi, CHECK_DICTIONARY);
+ __ JumpIfSmi(rdi, miss);
// Check that the value is a JavaScript function.
- __ CmpObjectType(rdx, JS_FUNCTION_TYPE, rdx);
+ __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rdx);
__ j(not_equal, miss);
// Patch the receiver with the global proxy if necessary.
if (is_global_object) {
- __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
__ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalReceiverOffset));
__ movq(Operand(rsp, (argc + 1) * kPointerSize), rdx);
}
}
-void CallIC::GenerateNormal(MacroAssembler* masm, int argc) {
+// The generated code falls through if the call should be handled by runtime.
+static void GenerateCallNormal(MacroAssembler* masm, int argc) {
// ----------- S t a t e -------------
// rcx : function name
// rsp[0] : return address
__ CheckAccessGlobalProxy(rdx, rax, &miss);
__ jmp(&invoke);
- // Cache miss: Jump to runtime.
__ bind(&miss);
+}
+
+
+void CallIC::GenerateMiss(MacroAssembler* masm, int argc) {
+ // ----------- S t a t e -------------
+ // rcx : function name
+ // rsp[0] : return address
+ // rsp[8] : argument argc
+ // rsp[16] : argument argc - 1
+ // ...
+ // rsp[argc * 8] : argument 1
+ // rsp[(argc + 1) * 8] : argument 0 = receiver
+ // -----------------------------------
+ GenerateCallMiss(masm, argc, IC::kCallIC_Miss);
+}
+
+
+void CallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
+ // ----------- S t a t e -------------
+ // rcx : function name
+ // rsp[0] : return address
+ // rsp[8] : argument argc
+ // rsp[16] : argument argc - 1
+ // ...
+ // rsp[argc * 8] : argument 1
+ // rsp[(argc + 1) * 8] : argument 0 = receiver
+ // -----------------------------------
+
+ // Get the receiver of the function from the stack; 1 ~ return address.
+ __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
+ GenerateMonomorphicCacheProbe(masm, argc, Code::CALL_IC);
+ GenerateMiss(masm, argc);
+}
+
+
+void CallIC::GenerateNormal(MacroAssembler* masm, int argc) {
+ // ----------- S t a t e -------------
+ // rcx : function name
+ // rsp[0] : return address
+ // rsp[8] : argument argc
+ // rsp[16] : argument argc - 1
+ // ...
+ // rsp[argc * 8] : argument 1
+ // rsp[(argc + 1) * 8] : argument 0 = receiver
+ // -----------------------------------
+
+ GenerateCallNormal(masm, argc);
GenerateMiss(masm, argc);
}
void KeyedCallIC::GenerateMiss(MacroAssembler* masm, int argc) {
- UNREACHABLE();
+ // ----------- S t a t e -------------
+ // rcx : function name
+ // rsp[0] : return address
+ // rsp[8] : argument argc
+ // rsp[16] : argument argc - 1
+ // ...
+ // rsp[argc * 8] : argument 1
+ // rsp[(argc + 1) * 8] : argument 0 = receiver
+ // -----------------------------------
+
+ GenerateCallMiss(masm, argc, IC::kKeyedCallIC_Miss);
}
void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
- UNREACHABLE();
+ // ----------- S t a t e -------------
+ // rcx : function name
+ // rsp[0] : return address
+ // rsp[8] : argument argc
+ // rsp[16] : argument argc - 1
+ // ...
+ // rsp[argc * 8] : argument 1
+ // rsp[(argc + 1) * 8] : argument 0 = receiver
+ // -----------------------------------
+
+ // Get the receiver of the function from the stack; 1 ~ return address.
+ __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
+
+ Label do_call, slow_call, slow_load, slow_reload_receiver;
+ Label check_number_dictionary, check_string, lookup_monomorphic_cache;
+ Label index_smi, index_string;
+
+ // Check that the key is a smi.
+ __ JumpIfNotSmi(rcx, &check_string);
+
+ __ bind(&index_smi);
+ // Now the key is known to be a smi. This place is also jumped to from below
+ // where a numeric string is converted to a smi.
+
+ GenerateKeyedLoadReceiverCheck(masm, rdx, rax, &slow_call);
+
+ GenerateFastArrayLoad(
+ masm, rdx, rcx, rax, rbx, rdi, &check_number_dictionary, &slow_load);
+ __ IncrementCounter(&Counters::keyed_call_generic_smi_fast, 1);
+
+ __ bind(&do_call);
+ // receiver in rdx is not used after this point.
+ // rcx: key
+ // rdi: function
+
+ // Check that the value in edi is a JavaScript function.
+ __ JumpIfSmi(rdi, &slow_call);
+ __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rax);
+ __ j(not_equal, &slow_call);
+ // Invoke the function.
+ ParameterCount actual(argc);
+ __ InvokeFunction(rdi, actual, JUMP_FUNCTION);
+
+ __ bind(&check_number_dictionary);
+ // eax: elements
+ // ecx: smi key
+ // Check whether the elements is a number dictionary.
+ __ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset),
+ Heap::kHashTableMapRootIndex);
+ __ SmiToInteger32(rbx, rcx);
+ // ebx: untagged index
+ GenerateNumberDictionaryLoad(masm, &slow_load, rax, rcx, rbx, r9, rdi, rdi);
+ __ IncrementCounter(&Counters::keyed_call_generic_smi_dict, 1);
+ __ jmp(&do_call);
+
+ __ bind(&slow_load);
+ // This branch is taken when calling KeyedCallIC_Miss is neither required
+ // nor beneficial.
+ __ IncrementCounter(&Counters::keyed_call_generic_slow_load, 1);
+ __ EnterInternalFrame();
+ __ push(rcx); // save the key
+ __ push(rdx); // pass the receiver
+ __ push(rcx); // pass the key
+ __ CallRuntime(Runtime::kKeyedGetProperty, 2);
+ __ pop(rcx); // restore the key
+ __ LeaveInternalFrame();
+ __ movq(rdi, rax);
+ __ jmp(&do_call);
+
+ __ bind(&check_string);
+ GenerateKeyStringCheck(masm, rcx, rax, rbx, &index_string, &slow_call);
+
+ // The key is known to be a symbol.
+ // If the receiver is a regular JS object with slow properties then do
+ // a quick inline probe of the receiver's dictionary.
+ // Otherwise do the monomorphic cache probe.
+ GenerateKeyedLoadReceiverCheck(masm, rdx, rax, &lookup_monomorphic_cache);
+
+ __ movq(rbx, FieldOperand(rdx, JSObject::kPropertiesOffset));
+ __ CompareRoot(FieldOperand(rbx, HeapObject::kMapOffset),
+ Heap::kHashTableMapRootIndex);
+ __ j(not_equal, &lookup_monomorphic_cache);
+
+ GenerateDictionaryLoad(
+ masm, &slow_load, rbx, rdx, rax, rcx, rdi, rdi, DICTIONARY_CHECK_DONE);
+ __ IncrementCounter(&Counters::keyed_call_generic_lookup_dict, 1);
+ __ jmp(&do_call);
+
+ __ bind(&lookup_monomorphic_cache);
+ __ IncrementCounter(&Counters::keyed_call_generic_lookup_cache, 1);
+ GenerateMonomorphicCacheProbe(masm, argc, Code::KEYED_CALL_IC);
+ // Fall through on miss.
+
+ __ bind(&slow_call);
+ // This branch is taken if:
+ // - the receiver requires boxing or access check,
+ // - the key is neither smi nor symbol,
+ // - the value loaded is not a function,
+ // - there is hope that the runtime will create a monomorphic call stub
+ // that will get fetched next time.
+ __ IncrementCounter(&Counters::keyed_call_generic_slow, 1);
+ GenerateMiss(masm, argc);
+
+ __ bind(&index_string);
+ GenerateIndexFromHash(masm, rcx, rbx);
+ // Now jump to the place where smi keys are handled.
+ __ jmp(&index_smi);
}
void KeyedCallIC::GenerateNormal(MacroAssembler* masm, int argc) {
- UNREACHABLE();
+ // ----------- S t a t e -------------
+ // rcx : function name
+ // rsp[0] : return address
+ // rsp[8] : argument argc
+ // rsp[16] : argument argc - 1
+ // ...
+ // rsp[argc * 8] : argument 1
+ // rsp[(argc + 1) * 8] : argument 0 = receiver
+ // -----------------------------------
+
+ GenerateCallNormal(masm, argc);
+ GenerateMiss(masm, argc);
}
// Search the dictionary placing the result in rax.
__ bind(&probe);
GenerateDictionaryLoad(masm, &miss, rdx, rax, rbx,
- rcx, rdi, CHECK_DICTIONARY);
+ rcx, rdi, rax, CHECK_DICTIONARY);
__ ret(0);
// Global object access: Check access rights.
#define __ ACCESS_MASM((masm()))
+
+void CallStubCompiler::GenerateNameCheck(String* name, Label* miss) {
+ if (kind_ == Code::KEYED_CALL_IC) {
+ __ Cmp(rcx, Handle<String>(name));
+ __ j(not_equal, miss);
+ }
+}
+
+
void CallStubCompiler::GenerateMissBranch() {
Handle<Code> ic = ComputeCallMiss(arguments().immediate(), kind_);
__ Jump(ic, RelocInfo::CODE_TARGET);
Label miss_in_smi_check;
+ GenerateNameCheck(name, &miss_in_smi_check);
+
// Get the receiver from the stack.
const int argc = arguments().immediate();
__ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
// -----------------------------------
Label miss;
+ GenerateNameCheck(name, &miss);
+
// Get the receiver from the stack.
const int argc = arguments().immediate();
__ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
Label miss;
+ GenerateNameCheck(name, &miss);
+
// Get the receiver from the stack.
const int argc = arguments().immediate();
__ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
Label miss, return_undefined, call_builtin;
+ GenerateNameCheck(name, &miss);
+
// Get the receiver from the stack.
const int argc = arguments().immediate();
__ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
// -----------------------------------
Label miss;
+ GenerateNameCheck(name, &miss);
+
// Get the number of arguments.
const int argc = arguments().immediate();
// rsp[(argc + 1) * 8] : argument 0 = receiver
Label miss;
+ GenerateNameCheck(name, &miss);
+
// Get the number of arguments.
const int argc = arguments().immediate();
}
+Result VirtualFrame::CallKeyedCallIC(RelocInfo::Mode mode,
+ int arg_count,
+ int loop_nesting) {
+ // Function name, arguments, and receiver are found on top of the frame
+ // and dropped by the call. The IC expects the name in rcx and the rest
+ // on the stack, and drops them all.
+ InLoopFlag in_loop = loop_nesting > 0 ? IN_LOOP : NOT_IN_LOOP;
+ Handle<Code> ic =
+ cgen()->ComputeKeyedCallInitialize(arg_count, in_loop);
+ Result name = Pop();
+ // Spill args, receiver, and function. The call will drop args and
+ // receiver.
+ PrepareForCall(arg_count + 1, arg_count + 1);
+ name.ToRegister(rcx);
+ name.Unuse();
+ return RawCallCodeObject(ic, mode);
+}
+
+
Result VirtualFrame::CallConstructor(int arg_count) {
// Arguments, receiver, and function are on top of the frame. The
// IC expects arg count in rax, function in rdi, and the arguments
// The argument count does not include the receiver.
Result CallCallIC(RelocInfo::Mode mode, int arg_count, int loop_nesting);
+ Result CallKeyedCallIC(RelocInfo::Mode mode, int arg_count, int loop_nesting);
+
// Allocate and call JS function as constructor. Arguments,
// receiver (global object), and function are found on top of the
// frame. Function is not dropped. The argument count does not
#include <limits.h>
-#define USE_NEW_QUERY_CALLBACKS
-
#include "v8.h"
#include "api.h"
}
-template <class ExternalArrayClass, class ElementType>
-static void ExternalArrayTestHelper(v8::ExternalArrayType array_type,
- int64_t low,
- int64_t high) {
+THREADED_TEST(PixelArrayInfo) {
v8::HandleScope scope;
LocalContext context;
- const int kElementCount = 40;
- int element_size = 0;
+ for (int size = 0; size < 100; size += 10) {
+ uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(size));
+ v8::Handle<v8::Object> obj = v8::Object::New();
+ obj->SetIndexedPropertiesToPixelData(pixel_data, size);
+ CHECK(obj->HasIndexedPropertiesInPixelData());
+ CHECK_EQ(pixel_data, obj->GetIndexedPropertiesPixelData());
+ CHECK_EQ(size, obj->GetIndexedPropertiesPixelDataLength());
+ free(pixel_data);
+ }
+}
+
+
+static int ExternalArrayElementSize(v8::ExternalArrayType array_type) {
switch (array_type) {
case v8::kExternalByteArray:
case v8::kExternalUnsignedByteArray:
- element_size = 1;
+ return 1;
break;
case v8::kExternalShortArray:
case v8::kExternalUnsignedShortArray:
- element_size = 2;
+ return 2;
break;
case v8::kExternalIntArray:
case v8::kExternalUnsignedIntArray:
case v8::kExternalFloatArray:
- element_size = 4;
+ return 4;
break;
default:
UNREACHABLE();
- break;
+ return -1;
}
+ UNREACHABLE();
+ return -1;
+}
+
+
+template <class ExternalArrayClass, class ElementType>
+static void ExternalArrayTestHelper(v8::ExternalArrayType array_type,
+ int64_t low,
+ int64_t high) {
+ v8::HandleScope scope;
+ LocalContext context;
+ const int kElementCount = 40;
+ int element_size = ExternalArrayElementSize(array_type);
ElementType* array_data =
static_cast<ElementType*>(malloc(kElementCount * element_size));
i::Handle<ExternalArrayClass> array =
}
+void ExternalArrayInfoTestHelper(v8::ExternalArrayType array_type) {
+ v8::HandleScope scope;
+ LocalContext context;
+ for (int size = 0; size < 100; size += 10) {
+ int element_size = ExternalArrayElementSize(array_type);
+ void* external_data = malloc(size * element_size);
+ v8::Handle<v8::Object> obj = v8::Object::New();
+ obj->SetIndexedPropertiesToExternalArrayData(
+ external_data, array_type, size);
+ CHECK(obj->HasIndexedPropertiesInExternalArrayData());
+ CHECK_EQ(external_data, obj->GetIndexedPropertiesExternalArrayData());
+ CHECK_EQ(array_type, obj->GetIndexedPropertiesExternalArrayDataType());
+ CHECK_EQ(size, obj->GetIndexedPropertiesExternalArrayDataLength());
+ free(external_data);
+ }
+}
+
+
+THREADED_TEST(ExternalArrayInfo) {
+ ExternalArrayInfoTestHelper(v8::kExternalByteArray);
+ ExternalArrayInfoTestHelper(v8::kExternalUnsignedByteArray);
+ ExternalArrayInfoTestHelper(v8::kExternalShortArray);
+ ExternalArrayInfoTestHelper(v8::kExternalUnsignedShortArray);
+ ExternalArrayInfoTestHelper(v8::kExternalIntArray);
+ ExternalArrayInfoTestHelper(v8::kExternalUnsignedIntArray);
+ ExternalArrayInfoTestHelper(v8::kExternalFloatArray);
+}
+
+
THREADED_TEST(ScriptContextDependence) {
v8::HandleScope scope;
LocalContext c1;
#include <stdlib.h>
-#define USE_NEW_QUERY_CALLBACKS
-
#include "v8.h"
#include "api.h"
#include <stdlib.h>
-#define USE_NEW_QUERY_CALLBACKS
-
#include "v8.h"
#include "heap.h"
COMPARE(mvn(r5, Operand(r4), SetCC, cc),
"31f05004 mvnccs r5, r4");
+ // Instructions autotransformed by the assembler.
+ // mov -> mvn.
+ COMPARE(mov(r3, Operand(-1), LeaveCC, al),
+ "e3e03000 mvn r3, #0");
+ COMPARE(mov(r4, Operand(-2), SetCC, al),
+ "e3f04001 mvns r4, #1");
+ COMPARE(mov(r5, Operand(0x0ffffff0), SetCC, ne),
+ "13f052ff mvnnes r5, #-268435441");
+ COMPARE(mov(r6, Operand(-1), LeaveCC, ne),
+ "13e06000 mvnne r6, #0");
+
+ // mvn -> mov.
+ COMPARE(mvn(r3, Operand(-1), LeaveCC, al),
+ "e3a03000 mov r3, #0");
+ COMPARE(mvn(r4, Operand(-2), SetCC, al),
+ "e3b04001 movs r4, #1");
+ COMPARE(mvn(r5, Operand(0x0ffffff0), SetCC, ne),
+ "13b052ff movnes r5, #-268435441");
+ COMPARE(mvn(r6, Operand(-1), LeaveCC, ne),
+ "13a06000 movne r6, #0");
+
+ // and <-> bic.
+ COMPARE(and_(r3, r5, Operand(0xfc03ffff)),
+ "e3c537ff bic r3, r5, #66846720");
+ COMPARE(bic(r3, r5, Operand(0xfc03ffff)),
+ "e20537ff and r3, r5, #66846720");
+
+ // sub <-> add.
+ COMPARE(add(r3, r5, Operand(-1024)),
+ "e2453b01 sub r3, r5, #1024");
+ COMPARE(sub(r3, r5, Operand(-1024)),
+ "e2853b01 add r3, r5, #1024");
+
+ // cmp <-> cmn.
+ COMPARE(cmp(r3, Operand(-1024)),
+ "e3730b01 cmn r3, #1024");
+ COMPARE(cmn(r3, Operand(-1024)),
+ "e3530b01 cmp r3, #1024");
+
// Miscellaneous instructions encoded as type 0.
COMPARE(blx(ip),
"e12fff3c blx ip");
#include "v8.h"
#include "heap-profiler.h"
+#include "snapshot.h"
#include "string-stream.h"
#include "cctest.h"
#include "zone-inl.h"
+#include "../include/v8-profiler.h"
namespace i = v8::internal;
using i::ClustersCoarser;
CHECK_EQ("(global property);1", printer.GetRetainers("C"));
}
+
+namespace {
+
+class NamedEntriesDetector {
+ public:
+ NamedEntriesDetector()
+ : has_A1(false), has_B1(false), has_C1(false),
+ has_A2(false), has_B2(false), has_C2(false) {
+ }
+
+ void Apply(i::HeapEntry* entry) {
+ const char* node_name = entry->name();
+ if (strcmp("A1", node_name) == 0
+ && entry->GetRetainingPaths()->length() > 0) has_A1 = true;
+ if (strcmp("B1", node_name) == 0
+ && entry->GetRetainingPaths()->length() > 0) has_B1 = true;
+ if (strcmp("C1", node_name) == 0
+ && entry->GetRetainingPaths()->length() > 0) has_C1 = true;
+ if (strcmp("A2", node_name) == 0
+ && entry->GetRetainingPaths()->length() > 0) has_A2 = true;
+ if (strcmp("B2", node_name) == 0
+ && entry->GetRetainingPaths()->length() > 0) has_B2 = true;
+ if (strcmp("C2", node_name) == 0
+ && entry->GetRetainingPaths()->length() > 0) has_C2 = true;
+ }
+
+ bool has_A1;
+ bool has_B1;
+ bool has_C1;
+ bool has_A2;
+ bool has_B2;
+ bool has_C2;
+};
+
+} // namespace
+
+TEST(HeapSnapshot) {
+ v8::HandleScope scope;
+
+ v8::Handle<v8::String> token1 = v8::String::New("token1");
+ v8::Handle<v8::Context> env1 = v8::Context::New();
+ env1->SetSecurityToken(token1);
+ env1->Enter();
+
+ CompileAndRunScript(
+ "function A1() {}\n"
+ "function B1(x) { this.x = x; }\n"
+ "function C1(x) { this.x1 = x; this.x2 = x; }\n"
+ "var a1 = new A1();\n"
+ "var b1_1 = new B1(a1), b1_2 = new B1(a1);\n"
+ "var c1 = new C1(a1);");
+
+ v8::Handle<v8::String> token2 = v8::String::New("token2");
+ v8::Handle<v8::Context> env2 = v8::Context::New();
+ env2->SetSecurityToken(token2);
+ env2->Enter();
+
+ CompileAndRunScript(
+ "function A2() {}\n"
+ "function B2(x) { return function() { return typeof x; }; }\n"
+ "function C2(x) { this.x1 = x; this.x2 = x; this[1] = x; }\n"
+ "var a2 = new A2();\n"
+ "var b2_1 = new B2(a2), b2_2 = new B2(a2);\n"
+ "var c2 = new C2(a2);");
+ const v8::HeapSnapshot* snapshot_env2 =
+ v8::HeapProfiler::TakeSnapshot(v8::String::New("env2"));
+ const v8::HeapGraphNode* global_env2;
+ if (i::Snapshot::IsEnabled()) {
+ // In case if snapshots are enabled, there will present a
+ // vanilla deserealized global object, without properties
+ // added by the test code.
+ CHECK_EQ(2, snapshot_env2->GetHead()->GetChildrenCount());
+ // Choose the global object of a bigger size.
+ const v8::HeapGraphNode* node0 =
+ snapshot_env2->GetHead()->GetChild(0)->GetToNode();
+ const v8::HeapGraphNode* node1 =
+ snapshot_env2->GetHead()->GetChild(1)->GetToNode();
+ global_env2 = node0->GetTotalSize() > node1->GetTotalSize() ?
+ node0 : node1;
+ } else {
+ CHECK_EQ(1, snapshot_env2->GetHead()->GetChildrenCount());
+ global_env2 = snapshot_env2->GetHead()->GetChild(0)->GetToNode();
+ }
+
+ // Verify, that JS global object of env2 doesn't have '..1'
+ // properties, but has '..2' properties.
+ bool has_a1 = false, has_b1_1 = false, has_b1_2 = false, has_c1 = false;
+ bool has_a2 = false, has_b2_1 = false, has_b2_2 = false, has_c2 = false;
+ // This will be needed further.
+ const v8::HeapGraphNode* a2_node = NULL;
+ for (int i = 0, count = global_env2->GetChildrenCount(); i < count; ++i) {
+ const v8::HeapGraphEdge* prop = global_env2->GetChild(i);
+ v8::String::AsciiValue prop_name(prop->GetName());
+ if (strcmp("a1", *prop_name) == 0) has_a1 = true;
+ if (strcmp("b1_1", *prop_name) == 0) has_b1_1 = true;
+ if (strcmp("b1_2", *prop_name) == 0) has_b1_2 = true;
+ if (strcmp("c1", *prop_name) == 0) has_c1 = true;
+ if (strcmp("a2", *prop_name) == 0) {
+ has_a2 = true;
+ a2_node = prop->GetToNode();
+ }
+ if (strcmp("b2_1", *prop_name) == 0) has_b2_1 = true;
+ if (strcmp("b2_2", *prop_name) == 0) has_b2_2 = true;
+ if (strcmp("c2", *prop_name) == 0) has_c2 = true;
+ }
+ CHECK(!has_a1);
+ CHECK(!has_b1_1);
+ CHECK(!has_b1_2);
+ CHECK(!has_c1);
+ CHECK(has_a2);
+ CHECK(has_b2_1);
+ CHECK(has_b2_2);
+ CHECK(has_c2);
+
+ // Verify that anything related to '[ABC]1' is not reachable.
+ NamedEntriesDetector det;
+ i::HeapSnapshot* i_snapshot_env2 =
+ const_cast<i::HeapSnapshot*>(
+ reinterpret_cast<const i::HeapSnapshot*>(snapshot_env2));
+ i_snapshot_env2->IterateEntries(&det);
+ CHECK(!det.has_A1);
+ CHECK(!det.has_B1);
+ CHECK(!det.has_C1);
+ CHECK(det.has_A2);
+ CHECK(det.has_B2);
+ CHECK(det.has_C2);
+
+ // Verify 'a2' object retainers. They are:
+ // - (global object).a2
+ // - c2.x1, c2.x2, c2[1]
+ // - b2_1 and b2_2 closures: via 'x' variable
+ CHECK_EQ(6, a2_node->GetRetainingPathsCount());
+ bool has_global_obj_a2_ref = false;
+ bool has_c2_x1_ref = false, has_c2_x2_ref = false, has_c2_1_ref = false;
+ bool has_b2_1_x_ref = false, has_b2_2_x_ref = false;
+ for (int i = 0; i < a2_node->GetRetainingPathsCount(); ++i) {
+ const v8::HeapGraphPath* path = a2_node->GetRetainingPath(i);
+ const int edges_count = path->GetEdgesCount();
+ CHECK_GT(edges_count, 0);
+ const v8::HeapGraphEdge* last_edge = path->GetEdge(edges_count - 1);
+ v8::String::AsciiValue last_edge_name(last_edge->GetName());
+ if (strcmp("a2", *last_edge_name) == 0
+ && last_edge->GetType() == v8::HeapGraphEdge::PROPERTY) {
+ has_global_obj_a2_ref = true;
+ continue;
+ }
+ CHECK_GT(edges_count, 1);
+ const v8::HeapGraphEdge* prev_edge = path->GetEdge(edges_count - 2);
+ v8::String::AsciiValue prev_edge_name(prev_edge->GetName());
+ if (strcmp("x1", *last_edge_name) == 0
+ && last_edge->GetType() == v8::HeapGraphEdge::PROPERTY
+ && strcmp("c2", *prev_edge_name) == 0) has_c2_x1_ref = true;
+ if (strcmp("x2", *last_edge_name) == 0
+ && last_edge->GetType() == v8::HeapGraphEdge::PROPERTY
+ && strcmp("c2", *prev_edge_name) == 0) has_c2_x2_ref = true;
+ if (strcmp("1", *last_edge_name) == 0
+ && last_edge->GetType() == v8::HeapGraphEdge::ELEMENT
+ && strcmp("c2", *prev_edge_name) == 0) has_c2_1_ref = true;
+ if (strcmp("x", *last_edge_name) == 0
+ && last_edge->GetType() == v8::HeapGraphEdge::CONTEXT_VARIABLE
+ && strcmp("b2_1", *prev_edge_name) == 0) has_b2_1_x_ref = true;
+ if (strcmp("x", *last_edge_name) == 0
+ && last_edge->GetType() == v8::HeapGraphEdge::CONTEXT_VARIABLE
+ && strcmp("b2_2", *prev_edge_name) == 0) has_b2_2_x_ref = true;
+ }
+ CHECK(has_global_obj_a2_ref);
+ CHECK(has_c2_x1_ref);
+ CHECK(has_c2_x2_ref);
+ CHECK(has_c2_1_ref);
+ CHECK(has_b2_1_x_ref);
+ CHECK(has_b2_2_x_ref);
+}
+
#endif // ENABLE_LOGGING_AND_PROFILING
ExternalReference::address_of_real_stack_limit();
CHECK_EQ(make_code(UNCLASSIFIED, 5),
encoder.Encode(real_stack_limit_address.address()));
- CHECK_EQ(make_code(UNCLASSIFIED, 12),
+ CHECK_EQ(make_code(UNCLASSIFIED, 15),
encoder.Encode(ExternalReference::debug_break().address()));
- CHECK_EQ(make_code(UNCLASSIFIED, 7),
+ CHECK_EQ(make_code(UNCLASSIFIED, 10),
encoder.Encode(ExternalReference::new_space_start().address()));
CHECK_EQ(make_code(UNCLASSIFIED, 3),
encoder.Encode(ExternalReference::roots_address().address()));
CHECK_EQ(ExternalReference::address_of_real_stack_limit().address(),
decoder.Decode(make_code(UNCLASSIFIED, 5)));
CHECK_EQ(ExternalReference::debug_break().address(),
- decoder.Decode(make_code(UNCLASSIFIED, 12)));
+ decoder.Decode(make_code(UNCLASSIFIED, 15)));
CHECK_EQ(ExternalReference::new_space_start().address(),
- decoder.Decode(make_code(UNCLASSIFIED, 7)));
+ decoder.Decode(make_code(UNCLASSIFIED, 10)));
}
testMany(dict_array, first3num, first3num);
testMany(fast_prop, first3str, first3num);
testMany(normal_prop, first3str, first3num);
+
+
+function testException(receiver, keys, exceptions) {
+ for (var i = 0; i != 10; i++) {
+ for (var k = 0; k != keys.length; k++) {
+ var thrown = false;
+ try {
+ var result = receiver[keys[k]]();
+ } catch (e) {
+ thrown = true;
+ }
+ assertEquals(exceptions[k], thrown);
+ }
+ }
+}
+
+testException([zero, one, /* hole */ ], [0, 1, 2], [false, false, true]);
} catch (e) {
assertTrue(/Cannot redefine property/.test(e));
}
+
+
+var obj6 = {};
+obj6[1] = 'foo';
+obj6[2] = 'bar';
+obj6[3] = '42';
+obj6[4] = '43';
+obj6[5] = '44';
+
+var descElement = { value: 'foobar' };
+var descElementNonConfigurable = { value: 'barfoo', configurable: false };
+var descElementNonWritable = { value: 'foofoo', writable: false };
+var descElementNonEnumerable = { value: 'barbar', enumerable: false };
+var descElementAllFalse = { value: 'foofalse',
+ configurable: false,
+ writable: false,
+ enumerable: false };
+
+
+// Redefine existing property.
+Object.defineProperty(obj6, '1', descElement);
+desc = Object.getOwnPropertyDescriptor(obj6, '1');
+assertEquals(desc.value, 'foobar');
+assertTrue(desc.writable);
+assertTrue(desc.enumerable);
+assertTrue(desc.configurable);
+
+// Redefine existing property with configurable: false.
+Object.defineProperty(obj6, '2', descElementNonConfigurable);
+desc = Object.getOwnPropertyDescriptor(obj6, '2');
+assertEquals(desc.value, 'barfoo');
+assertTrue(desc.writable);
+assertTrue(desc.enumerable);
+assertFalse(desc.configurable);
+
+// Ensure that we can't overwrite the non configurable element.
+try {
+ Object.defineProperty(obj6, '2', descElement);
+ assertUnreachable();
+} catch (e) {
+ assertTrue(/Cannot redefine property/.test(e));
+}
+
+Object.defineProperty(obj6, '3', descElementNonWritable);
+desc = Object.getOwnPropertyDescriptor(obj6, '3');
+assertEquals(desc.value, 'foofoo');
+assertFalse(desc.writable);
+assertTrue(desc.enumerable);
+assertTrue(desc.configurable);
+
+// Redefine existing property with configurable: false.
+Object.defineProperty(obj6, '4', descElementNonEnumerable);
+desc = Object.getOwnPropertyDescriptor(obj6, '4');
+assertEquals(desc.value, 'barbar');
+assertTrue(desc.writable);
+assertFalse(desc.enumerable);
+assertTrue(desc.configurable);
+
+// Redefine existing property with configurable: false.
+Object.defineProperty(obj6, '5', descElementAllFalse);
+desc = Object.getOwnPropertyDescriptor(obj6, '5');
+assertEquals(desc.value, 'foofalse');
+assertFalse(desc.writable);
+assertFalse(desc.enumerable);
+assertFalse(desc.configurable);
+
+// Define non existing property - all attributes should default to false.
+Object.defineProperty(obj6, '15', descElement);
+desc = Object.getOwnPropertyDescriptor(obj6, '15');
+assertEquals(desc.value, 'foobar');
+assertFalse(desc.writable);
+assertFalse(desc.enumerable);
+assertFalse(desc.configurable);
+
+// Make sure that we can't redefine using direct access.
+obj6[15] ='overwrite';
+assertEquals(obj6[15],'foobar');
+
+
+// Repeat the above tests on an array.
+var arr = new Array();
+arr[1] = 'foo';
+arr[2] = 'bar';
+arr[3] = '42';
+arr[4] = '43';
+arr[5] = '44';
+
+var descElement = { value: 'foobar' };
+var descElementNonConfigurable = { value: 'barfoo', configurable: false };
+var descElementNonWritable = { value: 'foofoo', writable: false };
+var descElementNonEnumerable = { value: 'barbar', enumerable: false };
+var descElementAllFalse = { value: 'foofalse',
+ configurable: false,
+ writable: false,
+ enumerable: false };
+
+
+// Redefine existing property.
+Object.defineProperty(arr, '1', descElement);
+desc = Object.getOwnPropertyDescriptor(arr, '1');
+assertEquals(desc.value, 'foobar');
+assertTrue(desc.writable);
+assertTrue(desc.enumerable);
+assertTrue(desc.configurable);
+
+// Redefine existing property with configurable: false.
+Object.defineProperty(arr, '2', descElementNonConfigurable);
+desc = Object.getOwnPropertyDescriptor(arr, '2');
+assertEquals(desc.value, 'barfoo');
+assertTrue(desc.writable);
+assertTrue(desc.enumerable);
+assertFalse(desc.configurable);
+
+// Ensure that we can't overwrite the non configurable element.
+try {
+ Object.defineProperty(arr, '2', descElement);
+ assertUnreachable();
+} catch (e) {
+ assertTrue(/Cannot redefine property/.test(e));
+}
+
+Object.defineProperty(arr, '3', descElementNonWritable);
+desc = Object.getOwnPropertyDescriptor(arr, '3');
+assertEquals(desc.value, 'foofoo');
+assertFalse(desc.writable);
+assertTrue(desc.enumerable);
+assertTrue(desc.configurable);
+
+// Redefine existing property with configurable: false.
+Object.defineProperty(arr, '4', descElementNonEnumerable);
+desc = Object.getOwnPropertyDescriptor(arr, '4');
+assertEquals(desc.value, 'barbar');
+assertTrue(desc.writable);
+assertFalse(desc.enumerable);
+assertTrue(desc.configurable);
+
+// Redefine existing property with configurable: false.
+Object.defineProperty(arr, '5', descElementAllFalse);
+desc = Object.getOwnPropertyDescriptor(arr, '5');
+assertEquals(desc.value, 'foofalse');
+assertFalse(desc.writable);
+assertFalse(desc.enumerable);
+assertFalse(desc.configurable);
+
+// Define non existing property - all attributes should default to false.
+Object.defineProperty(arr, '15', descElement);
+desc = Object.getOwnPropertyDescriptor(arr, '15');
+assertEquals(desc.value, 'foobar');
+assertFalse(desc.writable);
+assertFalse(desc.enumerable);
+assertFalse(desc.configurable);
+
+
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-// When this bug is corrected move to object-define-property and add
-// additional tests for configurable in the same manner as existing tests
-// there.
+// Tests that Object.defineProperty works correctly on array indices.
+// Please see http://code.google.com/p/v8/issues/detail?id=619 for details.
var obj = {};
obj[1] = 42;