aboutsummaryrefslogtreecommitdiff
path: root/src/arm/stub-cache-arm.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/arm/stub-cache-arm.cc')
-rw-r--r--src/arm/stub-cache-arm.cc305
1 files changed, 267 insertions, 38 deletions
diff --git a/src/arm/stub-cache-arm.cc b/src/arm/stub-cache-arm.cc
index 0e649ccd..ff3007c4 100644
--- a/src/arm/stub-cache-arm.cc
+++ b/src/arm/stub-cache-arm.cc
@@ -83,6 +83,119 @@ static void ProbeTable(MacroAssembler* masm,
}
+// Helper function used to check that the dictionary doesn't contain
+// the property. This function may return false negatives, so miss_label
+// must always call a backup property check that is complete.
+// This function is safe to call if the receiver has fast properties.
+// Name must be a symbol and receiver must be a heap object.
+static void GenerateDictionaryNegativeLookup(MacroAssembler* masm,
+ Label* miss_label,
+ Register receiver,
+ String* name,
+ Register scratch0,
+ Register scratch1) {
+ ASSERT(name->IsSymbol());
+ __ IncrementCounter(&Counters::negative_lookups, 1, scratch0, scratch1);
+ __ IncrementCounter(&Counters::negative_lookups_miss, 1, scratch0, scratch1);
+
+ Label done;
+
+ const int kInterceptorOrAccessCheckNeededMask =
+ (1 << Map::kHasNamedInterceptor) | (1 << Map::kIsAccessCheckNeeded);
+
+ // Bail out if the receiver has a named interceptor or requires access checks.
+ Register map = scratch1;
+ __ ldr(map, FieldMemOperand(receiver, HeapObject::kMapOffset));
+ __ ldrb(scratch0, FieldMemOperand(map, Map::kBitFieldOffset));
+ __ tst(scratch0, Operand(kInterceptorOrAccessCheckNeededMask));
+ __ b(ne, miss_label);
+
+ // Check that receiver is a JSObject.
+ __ ldrb(scratch0, FieldMemOperand(map, Map::kInstanceTypeOffset));
+ __ cmp(scratch0, Operand(FIRST_JS_OBJECT_TYPE));
+ __ b(lt, miss_label);
+
+ // Load properties array.
+ Register properties = scratch0;
+ __ ldr(properties, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
+ // Check that the properties array is a dictionary.
+ __ ldr(map, FieldMemOperand(properties, HeapObject::kMapOffset));
+ Register tmp = properties;
+ __ LoadRoot(tmp, Heap::kHashTableMapRootIndex);
+ __ cmp(map, tmp);
+ __ b(ne, miss_label);
+
+ // Restore the temporarily used register.
+ __ ldr(properties, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
+
+ // Compute the capacity mask.
+ const int kCapacityOffset =
+ StringDictionary::kHeaderSize +
+ StringDictionary::kCapacityIndex * kPointerSize;
+
+ // Generate an unrolled loop that performs a few probes before
+ // giving up.
+ static const int kProbes = 4;
+ const int kElementsStartOffset =
+ StringDictionary::kHeaderSize +
+ StringDictionary::kElementsStartIndex * kPointerSize;
+
+ // If names of slots in range from 1 to kProbes - 1 for the hash value are
+ // not equal to the name and kProbes-th slot is not used (its name is the
+ // undefined value), it guarantees the hash table doesn't contain the
+ // property. It's true even if some slots represent deleted properties
+ // (their names are the null value).
+ for (int i = 0; i < kProbes; i++) {
+ // scratch0 points to properties hash.
+ // Compute the masked index: (hash + i + i * i) & mask.
+ Register index = scratch1;
+ // Capacity is smi 2^n.
+ __ ldr(index, FieldMemOperand(properties, kCapacityOffset));
+ __ sub(index, index, Operand(1));
+ __ and_(index, index, Operand(
+ Smi::FromInt(name->Hash() + StringDictionary::GetProbeOffset(i))));
+
+ // Scale the index by multiplying by the entry size.
+ ASSERT(StringDictionary::kEntrySize == 3);
+ __ add(index, index, Operand(index, LSL, 1)); // index *= 3.
+
+ Register entity_name = scratch1;
+ // Having undefined at this place means the name is not contained.
+ ASSERT_EQ(kSmiTagSize, 1);
+ Register tmp = properties;
+ __ add(tmp, properties, Operand(index, LSL, 1));
+ __ ldr(entity_name, FieldMemOperand(tmp, kElementsStartOffset));
+
+ ASSERT(!tmp.is(entity_name));
+ __ LoadRoot(tmp, Heap::kUndefinedValueRootIndex);
+ __ cmp(entity_name, tmp);
+ if (i != kProbes - 1) {
+ __ b(eq, &done);
+
+ // Stop if found the property.
+ __ cmp(entity_name, Operand(Handle<String>(name)));
+ __ b(eq, miss_label);
+
+ // Check if the entry name is not a symbol.
+ __ ldr(entity_name, FieldMemOperand(entity_name, HeapObject::kMapOffset));
+ __ ldrb(entity_name,
+ FieldMemOperand(entity_name, Map::kInstanceTypeOffset));
+ __ tst(entity_name, Operand(kIsSymbolMask));
+ __ b(eq, miss_label);
+
+ // Restore the properties.
+ __ ldr(properties,
+ FieldMemOperand(receiver, JSObject::kPropertiesOffset));
+ } else {
+ // Give up probing if still not found the undefined value.
+ __ b(ne, miss_label);
+ }
+ }
+ __ bind(&done);
+ __ DecrementCounter(&Counters::negative_lookups_miss, 1, scratch0, scratch1);
+}
+
+
void StubCache::GenerateProbe(MacroAssembler* masm,
Code::Flags flags,
Register receiver,
@@ -517,6 +630,7 @@ class CallInterceptorCompiler BASE_EMBEDDED {
Register receiver,
Register scratch1,
Register scratch2,
+ Register scratch3,
Label* miss) {
ASSERT(holder->HasNamedInterceptor());
ASSERT(!holder->GetNamedInterceptor()->getter()->IsUndefined());
@@ -532,6 +646,7 @@ class CallInterceptorCompiler BASE_EMBEDDED {
receiver,
scratch1,
scratch2,
+ scratch3,
holder,
lookup,
name,
@@ -543,6 +658,7 @@ class CallInterceptorCompiler BASE_EMBEDDED {
receiver,
scratch1,
scratch2,
+ scratch3,
name,
holder,
miss);
@@ -555,6 +671,7 @@ class CallInterceptorCompiler BASE_EMBEDDED {
Register receiver,
Register scratch1,
Register scratch2,
+ Register scratch3,
JSObject* interceptor_holder,
LookupResult* lookup,
String* name,
@@ -596,7 +713,7 @@ class CallInterceptorCompiler BASE_EMBEDDED {
Register holder =
stub_compiler_->CheckPrototypes(object, receiver,
interceptor_holder, scratch1,
- scratch2, name, depth1, miss);
+ scratch2, scratch3, name, depth1, miss);
// Invoke an interceptor and if it provides a value,
// branch to |regular_invoke|.
@@ -612,7 +729,7 @@ class CallInterceptorCompiler BASE_EMBEDDED {
if (interceptor_holder != lookup->holder()) {
stub_compiler_->CheckPrototypes(interceptor_holder, receiver,
lookup->holder(), scratch1,
- scratch2, name, depth2, miss);
+ scratch2, scratch3, name, depth2, miss);
} else {
// CheckPrototypes has a side effect of fetching a 'holder'
// for API (object which is instanceof for the signature). It's
@@ -648,12 +765,13 @@ class CallInterceptorCompiler BASE_EMBEDDED {
Register receiver,
Register scratch1,
Register scratch2,
+ Register scratch3,
String* name,
JSObject* interceptor_holder,
Label* miss_label) {
Register holder =
stub_compiler_->CheckPrototypes(object, receiver, interceptor_holder,
- scratch1, scratch2, name,
+ scratch1, scratch2, scratch3, name,
miss_label);
// Call a runtime function to load the interceptor property.
@@ -738,36 +856,134 @@ Register StubCompiler::CheckPrototypes(JSObject* object,
Register object_reg,
JSObject* holder,
Register holder_reg,
- Register scratch,
+ Register scratch1,
+ Register scratch2,
String* name,
int save_at_depth,
- Label* miss,
- Register extra) {
- // Check that the maps haven't changed.
- Register result =
- masm()->CheckMaps(object, object_reg, holder, holder_reg, scratch,
- save_at_depth, miss);
+ Label* miss) {
+ // Make sure there's no overlap between holder and object registers.
+ ASSERT(!scratch1.is(object_reg) && !scratch1.is(holder_reg));
+ ASSERT(!scratch2.is(object_reg) && !scratch2.is(holder_reg)
+ && !scratch2.is(scratch1));
+
+ // Keep track of the current object in register reg.
+ Register reg = object_reg;
+ int depth = 0;
+
+ if (save_at_depth == depth) {
+ __ str(reg, MemOperand(sp));
+ }
+
+ // Check the maps in the prototype chain.
+ // Traverse the prototype chain from the object and do map checks.
+ JSObject* current = object;
+ while (current != holder) {
+ depth++;
+
+ // Only global objects and objects that do not require access
+ // checks are allowed in stubs.
+ ASSERT(current->IsJSGlobalProxy() || !current->IsAccessCheckNeeded());
+
+ JSObject* prototype = JSObject::cast(current->GetPrototype());
+ if (!current->HasFastProperties() &&
+ !current->IsJSGlobalObject() &&
+ !current->IsJSGlobalProxy()) {
+ if (!name->IsSymbol()) {
+ Object* lookup_result = Heap::LookupSymbol(name);
+ if (lookup_result->IsFailure()) {
+ set_failure(Failure::cast(lookup_result));
+ return reg;
+ } else {
+ name = String::cast(lookup_result);
+ }
+ }
+ ASSERT(current->property_dictionary()->FindEntry(name) ==
+ StringDictionary::kNotFound);
+
+ GenerateDictionaryNegativeLookup(masm(),
+ miss,
+ reg,
+ name,
+ scratch1,
+ scratch2);
+ __ ldr(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset));
+ reg = holder_reg; // from now the object is in holder_reg
+ __ ldr(reg, FieldMemOperand(scratch1, Map::kPrototypeOffset));
+ } else {
+ // Get the map of the current object.
+ __ ldr(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset));
+ __ cmp(scratch1, Operand(Handle<Map>(current->map())));
+
+ // Branch on the result of the map check.
+ __ b(ne, miss);
+
+ // Check access rights to the global object. This has to happen
+ // after the map check so that we know that the object is
+ // actually a global object.
+ if (current->IsJSGlobalProxy()) {
+ __ CheckAccessGlobalProxy(reg, scratch1, miss);
+ // Restore scratch register to be the map of the object. In the
+ // new space case below, we load the prototype from the map in
+ // the scratch register.
+ __ ldr(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset));
+ }
+
+ reg = holder_reg; // from now the object is in holder_reg
+ if (Heap::InNewSpace(prototype)) {
+ // The prototype is in new space; we cannot store a reference
+ // to it in the code. Load it from the map.
+ __ ldr(reg, FieldMemOperand(scratch1, Map::kPrototypeOffset));
+ } else {
+ // The prototype is in old space; load it directly.
+ __ mov(reg, Operand(Handle<JSObject>(prototype)));
+ }
+ }
+
+ if (save_at_depth == depth) {
+ __ str(reg, MemOperand(sp));
+ }
+
+ // Go to the next object in the prototype chain.
+ current = prototype;
+ }
+
+ // Check the holder map.
+ __ ldr(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset));
+ __ cmp(scratch1, Operand(Handle<Map>(current->map())));
+ __ b(ne, miss);
+
+ // Log the check depth.
+ LOG(IntEvent("check-maps-depth", depth + 1));
+
+ // Perform security check for access to the global object and return
+ // the holder register.
+ ASSERT(current == holder);
+ ASSERT(current->IsJSGlobalProxy() || !current->IsAccessCheckNeeded());
+ if (current->IsJSGlobalProxy()) {
+ __ CheckAccessGlobalProxy(reg, scratch1, miss);
+ }
// If we've skipped any global objects, it's not enough to verify
// that their maps haven't changed. We also need to check that the
// property cell for the property is still empty.
- while (object != holder) {
- if (object->IsGlobalObject()) {
+ current = object;
+ while (current != holder) {
+ if (current->IsGlobalObject()) {
Object* cell = GenerateCheckPropertyCell(masm(),
- GlobalObject::cast(object),
+ GlobalObject::cast(current),
name,
- scratch,
+ scratch1,
miss);
if (cell->IsFailure()) {
set_failure(Failure::cast(cell));
- return result;
+ return reg;
}
}
- object = JSObject::cast(object->GetPrototype());
+ current = JSObject::cast(current->GetPrototype());
}
// Return the register containing the holder.
- return result;
+ return reg;
}
@@ -776,6 +992,7 @@ void StubCompiler::GenerateLoadField(JSObject* object,
Register receiver,
Register scratch1,
Register scratch2,
+ Register scratch3,
int index,
String* name,
Label* miss) {
@@ -785,7 +1002,8 @@ void StubCompiler::GenerateLoadField(JSObject* object,
// Check that the maps haven't changed.
Register reg =
- CheckPrototypes(object, receiver, holder, scratch1, scratch2, name, miss);
+ CheckPrototypes(object, receiver, holder, scratch1, scratch2, scratch3,
+ name, miss);
GenerateFastPropertyLoad(masm(), r0, reg, holder, index);
__ Ret();
}
@@ -796,6 +1014,7 @@ void StubCompiler::GenerateLoadConstant(JSObject* object,
Register receiver,
Register scratch1,
Register scratch2,
+ Register scratch3,
Object* value,
String* name,
Label* miss) {
@@ -805,7 +1024,8 @@ void StubCompiler::GenerateLoadConstant(JSObject* object,
// Check that the maps haven't changed.
Register reg =
- CheckPrototypes(object, receiver, holder, scratch1, scratch2, name, miss);
+ CheckPrototypes(object, receiver, holder,
+ scratch1, scratch2, scratch3, name, miss);
// Return the constant value.
__ mov(r0, Operand(Handle<Object>(value)));
@@ -819,6 +1039,7 @@ bool StubCompiler::GenerateLoadCallback(JSObject* object,
Register name_reg,
Register scratch1,
Register scratch2,
+ Register scratch3,
AccessorInfo* callback,
String* name,
Label* miss,
@@ -829,7 +1050,8 @@ bool StubCompiler::GenerateLoadCallback(JSObject* object,
// Check that the maps haven't changed.
Register reg =
- CheckPrototypes(object, receiver, holder, scratch1, scratch2, name, miss);
+ CheckPrototypes(object, receiver, holder, scratch1, scratch2, scratch3,
+ name, miss);
// Push the arguments on the JS stack of the caller.
__ push(receiver); // Receiver.
@@ -854,6 +1076,7 @@ void StubCompiler::GenerateLoadInterceptor(JSObject* object,
Register name_reg,
Register scratch1,
Register scratch2,
+ Register scratch3,
String* name,
Label* miss) {
ASSERT(interceptor_holder->HasNamedInterceptor());
@@ -881,7 +1104,8 @@ void StubCompiler::GenerateLoadInterceptor(JSObject* object,
// property from further up the prototype chain if the call fails.
// Check that the maps haven't changed.
Register holder_reg = CheckPrototypes(object, receiver, interceptor_holder,
- scratch1, scratch2, name, miss);
+ scratch1, scratch2, scratch3,
+ name, miss);
ASSERT(holder_reg.is(receiver) || holder_reg.is(scratch1));
// Save necessary data before invoking an interceptor.
@@ -930,6 +1154,7 @@ void StubCompiler::GenerateLoadInterceptor(JSObject* object,
lookup->holder(),
scratch1,
scratch2,
+ scratch3,
name,
miss);
}
@@ -975,7 +1200,8 @@ void StubCompiler::GenerateLoadInterceptor(JSObject* object,
// Call the runtime system to load the interceptor.
// Check that the maps haven't changed.
Register holder_reg = CheckPrototypes(object, receiver, interceptor_holder,
- scratch1, scratch2, name, miss);
+ scratch1, scratch2, scratch3,
+ name, miss);
PushInterceptorArguments(masm(), receiver, holder_reg,
name_reg, interceptor_holder);
@@ -1053,7 +1279,7 @@ Object* CallStubCompiler::CompileCallField(JSObject* object,
__ b(eq, &miss);
// Do the right check and compute the holder register.
- Register reg = CheckPrototypes(object, r0, holder, r1, r3, name, &miss);
+ Register reg = CheckPrototypes(object, r0, holder, r1, r3, r4, name, &miss);
GenerateFastPropertyLoad(masm(), r1, reg, holder, index);
GenerateCallFunction(masm(), object, arguments(), &miss);
@@ -1098,7 +1324,7 @@ Object* CallStubCompiler::CompileArrayPushCall(Object* object,
__ b(eq, &miss);
// Check that the maps haven't changed.
- CheckPrototypes(JSObject::cast(object), r1, holder, r3, r0, name, &miss);
+ CheckPrototypes(JSObject::cast(object), r1, holder, r3, r0, r4, name, &miss);
if (object->IsGlobalObject()) {
__ ldr(r3, FieldMemOperand(r1, GlobalObject::kGlobalReceiverOffset));
@@ -1149,7 +1375,7 @@ Object* CallStubCompiler::CompileArrayPopCall(Object* object,
__ b(eq, &miss);
// Check that the maps haven't changed.
- CheckPrototypes(JSObject::cast(object), r1, holder, r3, r0, name, &miss);
+ CheckPrototypes(JSObject::cast(object), r1, holder, r3, r0, r4, name, &miss);
if (object->IsGlobalObject()) {
__ ldr(r3, FieldMemOperand(r1, GlobalObject::kGlobalReceiverOffset));
@@ -1246,7 +1472,7 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
}
// Check that the maps haven't changed.
- CheckPrototypes(JSObject::cast(object), r1, holder, r0, r3, name,
+ CheckPrototypes(JSObject::cast(object), r1, holder, r0, r3, r4, name,
depth, &miss);
// Patch the receiver on the stack with the global proxy if
@@ -1270,7 +1496,7 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
GenerateDirectLoadGlobalFunctionPrototype(
masm(), Context::STRING_FUNCTION_INDEX, r0);
CheckPrototypes(JSObject::cast(object->GetPrototype()), r0, holder, r3,
- r1, name, &miss);
+ r1, r4, name, &miss);
}
break;
@@ -1290,7 +1516,7 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
GenerateDirectLoadGlobalFunctionPrototype(
masm(), Context::NUMBER_FUNCTION_INDEX, r0);
CheckPrototypes(JSObject::cast(object->GetPrototype()), r0, holder, r3,
- r1, name, &miss);
+ r1, r4, name, &miss);
}
break;
}
@@ -1313,7 +1539,7 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
GenerateDirectLoadGlobalFunctionPrototype(
masm(), Context::BOOLEAN_FUNCTION_INDEX, r0);
CheckPrototypes(JSObject::cast(object->GetPrototype()), r0, holder, r3,
- r1, name, &miss);
+ r1, r4, name, &miss);
}
break;
}
@@ -1372,6 +1598,7 @@ Object* CallStubCompiler::CompileCallInterceptor(JSObject* object,
r1,
r3,
r4,
+ r0,
&miss);
// Move returned value, the function to call, to r1.
@@ -1418,7 +1645,7 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object,
}
// Check that the maps haven't changed.
- CheckPrototypes(object, r0, holder, r3, r1, name, &miss);
+ CheckPrototypes(object, r0, holder, r3, r1, r4, name, &miss);
// Get the value from the cell.
__ mov(r3, Operand(Handle<JSGlobalPropertyCell>(cell)));
@@ -1642,7 +1869,7 @@ Object* LoadStubCompiler::CompileLoadNonexistent(String* name,
__ b(eq, &miss);
// Check the maps of the full prototype chain.
- CheckPrototypes(object, r0, last, r3, r1, name, &miss);
+ CheckPrototypes(object, r0, last, r3, r1, r4, name, &miss);
// If the last object in the prototype chain is a global object,
// check that the global property cell is empty.
@@ -1679,7 +1906,7 @@ Object* LoadStubCompiler::CompileLoadField(JSObject* object,
// -----------------------------------
Label miss;
- GenerateLoadField(object, holder, r0, r3, r1, index, name, &miss);
+ GenerateLoadField(object, holder, r0, r3, r1, r4, index, name, &miss);
__ bind(&miss);
GenerateLoadMiss(masm(), Code::LOAD_IC);
@@ -1700,7 +1927,7 @@ Object* LoadStubCompiler::CompileLoadCallback(String* name,
Label miss;
Failure* failure = Failure::InternalError();
- bool success = GenerateLoadCallback(object, holder, r0, r2, r3, r1,
+ bool success = GenerateLoadCallback(object, holder, r0, r2, r3, r1, r4,
callback, name, &miss, &failure);
if (!success) return failure;
@@ -1723,7 +1950,7 @@ Object* LoadStubCompiler::CompileLoadConstant(JSObject* object,
// -----------------------------------
Label miss;
- GenerateLoadConstant(object, holder, r0, r3, r1, value, name, &miss);
+ GenerateLoadConstant(object, holder, r0, r3, r1, r4, value, name, &miss);
__ bind(&miss);
GenerateLoadMiss(masm(), Code::LOAD_IC);
@@ -1751,6 +1978,7 @@ Object* LoadStubCompiler::CompileLoadInterceptor(JSObject* object,
r2,
r3,
r1,
+ r4,
name,
&miss);
__ bind(&miss);
@@ -1782,7 +2010,7 @@ Object* LoadStubCompiler::CompileLoadGlobal(JSObject* object,
}
// Check that the map of the global has not changed.
- CheckPrototypes(object, r0, holder, r3, r4, name, &miss);
+ CheckPrototypes(object, r0, holder, r3, r4, r1, name, &miss);
// Get the value from the cell.
__ mov(r3, Operand(Handle<JSGlobalPropertyCell>(cell)));
@@ -1823,7 +2051,7 @@ Object* KeyedLoadStubCompiler::CompileLoadField(String* name,
__ cmp(r0, Operand(Handle<String>(name)));
__ b(ne, &miss);
- GenerateLoadField(receiver, holder, r1, r2, r3, index, name, &miss);
+ GenerateLoadField(receiver, holder, r1, r2, r3, r4, index, name, &miss);
__ bind(&miss);
GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
@@ -1847,7 +2075,7 @@ Object* KeyedLoadStubCompiler::CompileLoadCallback(String* name,
__ b(ne, &miss);
Failure* failure = Failure::InternalError();
- bool success = GenerateLoadCallback(receiver, holder, r1, r0, r2, r3,
+ bool success = GenerateLoadCallback(receiver, holder, r1, r0, r2, r3, r4,
callback, name, &miss, &failure);
if (!success) return failure;
@@ -1873,7 +2101,7 @@ Object* KeyedLoadStubCompiler::CompileLoadConstant(String* name,
__ cmp(r0, Operand(Handle<String>(name)));
__ b(ne, &miss);
- GenerateLoadConstant(receiver, holder, r1, r2, r3, value, name, &miss);
+ GenerateLoadConstant(receiver, holder, r1, r2, r3, r4, value, name, &miss);
__ bind(&miss);
GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
@@ -1905,6 +2133,7 @@ Object* KeyedLoadStubCompiler::CompileLoadInterceptor(JSObject* receiver,
r0,
r2,
r3,
+ r4,
name,
&miss);
__ bind(&miss);