Port number dictionary probing in generated code to ARM.
Fix bug in ARM pixel array load code and a typo in the x64 number dictionary load code. Fix bug in string dictionary probing where we did not bail out if the object has an interceptor. BUG=640 Review URL: http://codereview.chromium.org/1332003 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4292 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
2d0c823305
commit
2bcc49740c
@ -65,11 +65,11 @@ static void GenerateDictionaryLoad(MacroAssembler* masm,
|
||||
// Check for the absence of an interceptor.
|
||||
// Load the map into t0.
|
||||
__ ldr(t0, FieldMemOperand(t1, JSObject::kMapOffset));
|
||||
// Test the has_named_interceptor bit in the map.
|
||||
__ ldr(r3, FieldMemOperand(t0, Map::kInstanceAttributesOffset));
|
||||
__ tst(r3, Operand(1 << (Map::kHasNamedInterceptor + (3 * 8))));
|
||||
// Jump to miss if the interceptor bit is set.
|
||||
__ b(ne, miss);
|
||||
|
||||
// Bail out if the receiver has a named interceptor.
|
||||
__ ldrb(r3, FieldMemOperand(t0, Map::kBitFieldOffset));
|
||||
__ tst(r3, Operand(1 << Map::kHasNamedInterceptor));
|
||||
__ b(nz, miss);
|
||||
|
||||
// Bail out if we have a JS global proxy object.
|
||||
__ ldrb(r3, FieldMemOperand(t0, Map::kInstanceTypeOffset));
|
||||
@ -144,6 +144,95 @@ static void GenerateDictionaryLoad(MacroAssembler* masm,
|
||||
}
|
||||
|
||||
|
||||
static void GenerateNumberDictionaryLoad(MacroAssembler* masm,
|
||||
Label* miss,
|
||||
Register elements,
|
||||
Register key,
|
||||
Register t0,
|
||||
Register t1,
|
||||
Register t2) {
|
||||
// Register use:
|
||||
//
|
||||
// elements - holds the slow-case elements of the receiver and is unchanged.
|
||||
//
|
||||
// key - holds the smi key on entry and is unchanged if a branch is
|
||||
// performed to the miss label.
|
||||
//
|
||||
// Scratch registers:
|
||||
//
|
||||
// t0 - holds the untagged key on entry and holds the hash once computed.
|
||||
// Holds the result on exit if the load succeeded.
|
||||
//
|
||||
// t1 - used to hold the capacity mask of the dictionary
|
||||
//
|
||||
// t2 - used for the index into the dictionary.
|
||||
Label done;
|
||||
|
||||
// Compute the hash code from the untagged key. This must be kept in sync
|
||||
// with ComputeIntegerHash in utils.h.
|
||||
//
|
||||
// hash = ~hash + (hash << 15);
|
||||
__ mvn(t1, Operand(t0));
|
||||
__ add(t0, t1, Operand(t0, LSL, 15));
|
||||
// hash = hash ^ (hash >> 12);
|
||||
__ eor(t0, t0, Operand(t0, LSR, 12));
|
||||
// hash = hash + (hash << 2);
|
||||
__ add(t0, t0, Operand(t0, LSL, 2));
|
||||
// hash = hash ^ (hash >> 4);
|
||||
__ eor(t0, t0, Operand(t0, LSR, 4));
|
||||
// hash = hash * 2057;
|
||||
__ mov(t1, Operand(2057));
|
||||
__ mul(t0, t0, t1);
|
||||
// hash = hash ^ (hash >> 16);
|
||||
__ eor(t0, t0, Operand(t0, LSR, 16));
|
||||
|
||||
// Compute the capacity mask.
|
||||
__ ldr(t1, FieldMemOperand(elements, NumberDictionary::kCapacityOffset));
|
||||
__ mov(t1, Operand(t1, ASR, kSmiTagSize)); // convert smi to int
|
||||
__ sub(t1, t1, Operand(1));
|
||||
|
||||
// Generate an unrolled loop that performs a few probes before giving up.
|
||||
static const int kProbes = 4;
|
||||
for (int i = 0; i < kProbes; i++) {
|
||||
// Use t2 for index calculations and keep the hash intact in t0.
|
||||
__ mov(t2, t0);
|
||||
// Compute the masked index: (hash + i + i * i) & mask.
|
||||
if (i > 0) {
|
||||
__ add(t2, t2, Operand(NumberDictionary::GetProbeOffset(i)));
|
||||
}
|
||||
__ and_(t2, t2, Operand(t1));
|
||||
|
||||
// Scale the index by multiplying by the element size.
|
||||
ASSERT(NumberDictionary::kEntrySize == 3);
|
||||
__ add(t2, t2, Operand(t2, LSL, 1)); // t2 = t2 * 3
|
||||
|
||||
// Check if the key is identical to the name.
|
||||
__ add(t2, elements, Operand(t2, LSL, kPointerSizeLog2));
|
||||
__ ldr(ip, FieldMemOperand(t2, NumberDictionary::kElementsStartOffset));
|
||||
__ cmp(key, Operand(ip));
|
||||
if (i != kProbes - 1) {
|
||||
__ b(eq, &done);
|
||||
} else {
|
||||
__ b(ne, miss);
|
||||
}
|
||||
}
|
||||
|
||||
__ bind(&done);
|
||||
// Check that the value is a normal property.
|
||||
// t2: elements + (index * kPointerSize)
|
||||
const int kDetailsOffset =
|
||||
NumberDictionary::kElementsStartOffset + 2 * kPointerSize;
|
||||
__ ldr(t1, FieldMemOperand(t2, kDetailsOffset));
|
||||
__ tst(t1, Operand(Smi::FromInt(PropertyDetails::TypeField::mask())));
|
||||
__ b(ne, miss);
|
||||
|
||||
// Get the value at the masked, scaled index and return.
|
||||
const int kValueOffset =
|
||||
NumberDictionary::kElementsStartOffset + kPointerSize;
|
||||
__ ldr(t0, FieldMemOperand(t2, kValueOffset));
|
||||
}
|
||||
|
||||
|
||||
void LoadIC::GenerateArrayLength(MacroAssembler* masm) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- r2 : name
|
||||
@ -530,7 +619,7 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
|
||||
// -- sp[0] : key
|
||||
// -- sp[4] : receiver
|
||||
// -----------------------------------
|
||||
Label slow, fast, check_pixel_array;
|
||||
Label slow, fast, check_pixel_array, check_number_dictionary;
|
||||
|
||||
// Get the key and receiver object from the stack.
|
||||
__ ldm(ia, sp, r0.bit() | r1.bit());
|
||||
@ -554,6 +643,8 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
|
||||
|
||||
// Check that the key is a smi.
|
||||
__ BranchOnNotSmi(r0, &slow);
|
||||
// Save key in r2 in case we want it for the number dictionary case.
|
||||
__ mov(r2, r0);
|
||||
__ mov(r0, Operand(r0, ASR, kSmiTagSize));
|
||||
|
||||
// Get the elements array of the object.
|
||||
@ -562,17 +653,26 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
|
||||
__ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset));
|
||||
__ LoadRoot(ip, Heap::kFixedArrayMapRootIndex);
|
||||
__ cmp(r3, ip);
|
||||
__ b(ne, &slow);
|
||||
__ b(ne, &check_pixel_array);
|
||||
// Check that the key (index) is within bounds.
|
||||
__ ldr(r3, FieldMemOperand(r1, Array::kLengthOffset));
|
||||
__ cmp(r0, Operand(r3));
|
||||
__ b(lo, &fast);
|
||||
__ b(ge, &slow);
|
||||
// Fast case: Do the load.
|
||||
__ add(r3, r1, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
|
||||
__ ldr(r0, MemOperand(r3, r0, LSL, kPointerSizeLog2));
|
||||
__ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
|
||||
__ cmp(r0, ip);
|
||||
// In case the loaded value is the_hole we have to consult GetProperty
|
||||
// to ensure the prototype chain is searched.
|
||||
__ b(eq, &slow);
|
||||
__ Ret();
|
||||
|
||||
// Check whether the elements is a pixel array.
|
||||
__ bind(&check_pixel_array);
|
||||
__ LoadRoot(ip, Heap::kPixelArrayMapRootIndex);
|
||||
__ cmp(r3, ip);
|
||||
__ b(ne, &slow);
|
||||
__ b(ne, &check_number_dictionary);
|
||||
__ ldr(ip, FieldMemOperand(r1, PixelArray::kLengthOffset));
|
||||
__ cmp(r0, ip);
|
||||
__ b(hs, &slow);
|
||||
@ -581,22 +681,21 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
|
||||
__ mov(r0, Operand(r0, LSL, kSmiTagSize)); // Tag result as smi.
|
||||
__ Ret();
|
||||
|
||||
__ bind(&check_number_dictionary);
|
||||
// Check whether the elements is a number dictionary.
|
||||
// r0: untagged index
|
||||
// r1: elements
|
||||
// r2: key
|
||||
__ LoadRoot(ip, Heap::kHashTableMapRootIndex);
|
||||
__ cmp(r3, ip);
|
||||
__ b(ne, &slow);
|
||||
GenerateNumberDictionaryLoad(masm, &slow, r1, r2, r0, r3, r4);
|
||||
__ Ret();
|
||||
|
||||
// Slow case: Push extra copies of the arguments (2).
|
||||
__ bind(&slow);
|
||||
__ IncrementCounter(&Counters::keyed_load_generic_slow, 1, r0, r1);
|
||||
GenerateRuntimeGetProperty(masm);
|
||||
|
||||
// Fast case: Do the load.
|
||||
__ bind(&fast);
|
||||
__ add(r3, r1, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
|
||||
__ ldr(r0, MemOperand(r3, r0, LSL, kPointerSizeLog2));
|
||||
__ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
|
||||
__ cmp(r0, ip);
|
||||
// In case the loaded value is the_hole we have to consult GetProperty
|
||||
// to ensure the prototype chain is searched.
|
||||
__ b(eq, &slow);
|
||||
|
||||
__ Ret();
|
||||
}
|
||||
|
||||
|
||||
|
@ -73,11 +73,10 @@ static void GenerateDictionaryLoad(MacroAssembler* masm,
|
||||
// Check for the absence of an interceptor.
|
||||
// Load the map into r0.
|
||||
__ mov(r0, FieldOperand(receiver, JSObject::kMapOffset));
|
||||
// Test the has_named_interceptor bit in the map.
|
||||
__ test(FieldOperand(r0, Map::kInstanceAttributesOffset),
|
||||
Immediate(1 << (Map::kHasNamedInterceptor + (3 * 8))));
|
||||
|
||||
// Jump to miss if the interceptor bit is set.
|
||||
// Bail out if the receiver has a named interceptor.
|
||||
__ test(FieldOperand(r0, Map::kBitFieldOffset),
|
||||
Immediate(1 << Map::kHasNamedInterceptor));
|
||||
__ j(not_zero, miss_label, not_taken);
|
||||
|
||||
// Bail out if we have a JS global proxy object.
|
||||
@ -202,17 +201,10 @@ static void GenerateNumberDictionaryLoad(MacroAssembler* masm,
|
||||
__ xor_(r0, Operand(r1));
|
||||
|
||||
// Compute capacity mask.
|
||||
const int kCapacityOffset =
|
||||
NumberDictionary::kHeaderSize +
|
||||
NumberDictionary::kCapacityIndex * kPointerSize;
|
||||
__ mov(r1, FieldOperand(elements, kCapacityOffset));
|
||||
__ mov(r1, FieldOperand(elements, NumberDictionary::kCapacityOffset));
|
||||
__ shr(r1, kSmiTagSize); // convert smi to int
|
||||
__ dec(r1);
|
||||
|
||||
const int kElementsStartOffset =
|
||||
NumberDictionary::kHeaderSize +
|
||||
NumberDictionary::kElementsStartIndex * kPointerSize;
|
||||
|
||||
// Generate an unrolled loop that performs a few probes before giving up.
|
||||
const int kProbes = 4;
|
||||
for (int i = 0; i < kProbes; i++) {
|
||||
@ -232,7 +224,7 @@ static void GenerateNumberDictionaryLoad(MacroAssembler* masm,
|
||||
__ cmp(key, FieldOperand(elements,
|
||||
r2,
|
||||
times_pointer_size,
|
||||
kElementsStartOffset));
|
||||
NumberDictionary::kElementsStartOffset));
|
||||
if (i != (kProbes - 1)) {
|
||||
__ j(equal, &done, taken);
|
||||
} else {
|
||||
@ -242,14 +234,16 @@ static void GenerateNumberDictionaryLoad(MacroAssembler* masm,
|
||||
|
||||
__ bind(&done);
|
||||
// Check that the value is a normal propety.
|
||||
const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
|
||||
const int kDetailsOffset =
|
||||
NumberDictionary::kElementsStartOffset + 2 * kPointerSize;
|
||||
ASSERT_EQ(NORMAL, 0);
|
||||
__ test(FieldOperand(elements, r2, times_pointer_size, kDetailsOffset),
|
||||
Immediate(PropertyDetails::TypeField::mask() << kSmiTagSize));
|
||||
__ j(not_zero, miss);
|
||||
|
||||
// Get the value at the masked, scaled index.
|
||||
const int kValueOffset = kElementsStartOffset + kPointerSize;
|
||||
const int kValueOffset =
|
||||
NumberDictionary::kElementsStartOffset + kPointerSize;
|
||||
__ mov(key, FieldOperand(elements, r2, times_pointer_size, kValueOffset));
|
||||
}
|
||||
|
||||
|
@ -72,11 +72,10 @@ static void GenerateDictionaryLoad(MacroAssembler* masm,
|
||||
// Check for the absence of an interceptor.
|
||||
// Load the map into r0.
|
||||
__ movq(r0, FieldOperand(r1, JSObject::kMapOffset));
|
||||
// Test the has_named_interceptor bit in the map.
|
||||
__ testl(FieldOperand(r0, Map::kInstanceAttributesOffset),
|
||||
Immediate(1 << (Map::kHasNamedInterceptor + (3 * 8))));
|
||||
|
||||
// Jump to miss if the interceptor bit is set.
|
||||
// Bail out if the receiver has a named interceptor.
|
||||
__ testl(FieldOperand(r0, Map::kBitFieldOffset),
|
||||
Immediate(1 << Map::kHasNamedInterceptor));
|
||||
__ j(not_zero, miss_label);
|
||||
|
||||
// Bail out if we have a JS global proxy object.
|
||||
@ -201,17 +200,10 @@ static void GenerateNumberDictionaryLoad(MacroAssembler* masm,
|
||||
__ xorl(r0, r1);
|
||||
|
||||
// Compute capacity mask.
|
||||
const int kCapacityOffset =
|
||||
StringDictionary::kHeaderSize +
|
||||
StringDictionary::kCapacityIndex * kPointerSize;
|
||||
__ movq(r1, FieldOperand(elements, kCapacityOffset));
|
||||
__ movq(r1, FieldOperand(elements, NumberDictionary::kCapacityOffset));
|
||||
__ SmiToInteger32(r1, r1);
|
||||
__ decl(r1);
|
||||
|
||||
const int kElementsStartOffset =
|
||||
NumberDictionary::kHeaderSize +
|
||||
NumberDictionary::kElementsStartIndex * kPointerSize;
|
||||
|
||||
// Generate an unrolled loop that performs a few probes before giving up.
|
||||
const int kProbes = 4;
|
||||
for (int i = 0; i < kProbes; i++) {
|
||||
@ -231,7 +223,7 @@ static void GenerateNumberDictionaryLoad(MacroAssembler* masm,
|
||||
__ cmpq(key, FieldOperand(elements,
|
||||
r2,
|
||||
times_pointer_size,
|
||||
kElementsStartOffset));
|
||||
NumberDictionary::kElementsStartOffset));
|
||||
if (i != (kProbes - 1)) {
|
||||
__ j(equal, &done);
|
||||
} else {
|
||||
@ -241,14 +233,16 @@ static void GenerateNumberDictionaryLoad(MacroAssembler* masm,
|
||||
|
||||
__ bind(&done);
|
||||
// Check that the value is a normal propety.
|
||||
const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
|
||||
const int kDetailsOffset =
|
||||
NumberDictionary::kElementsStartOffset + 2 * kPointerSize;
|
||||
ASSERT_EQ(NORMAL, 0);
|
||||
__ Test(FieldOperand(elements, r2, times_pointer_size, kDetailsOffset),
|
||||
Smi::FromInt(PropertyDetails::TypeField::mask()));
|
||||
__ j(not_zero, miss);
|
||||
|
||||
// Get the value at the masked, scaled index.
|
||||
const int kValueOffset = kElementsStartOffset + kPointerSize;
|
||||
const int kValueOffset =
|
||||
NumberDictionary::kElementsStartOffset + kPointerSize;
|
||||
__ movq(r0, FieldOperand(elements, r2, times_pointer_size, kValueOffset));
|
||||
}
|
||||
|
||||
@ -1404,7 +1398,7 @@ void LoadIC::GenerateNormal(MacroAssembler* masm) {
|
||||
|
||||
// Check for non-global object that requires access check.
|
||||
__ testl(FieldOperand(rbx, Map::kBitFieldOffset),
|
||||
Immediate(1 << Map::kIsAccessCheckNeeded));
|
||||
Immediate(1 << Map::kIsAccessCheckNeeded));
|
||||
__ j(not_zero, &miss);
|
||||
|
||||
// Search the dictionary placing the result in rax.
|
||||
|
@ -2644,6 +2644,36 @@ THREADED_TEST(NamedInterceptorPropertyRead) {
|
||||
}
|
||||
|
||||
|
||||
THREADED_TEST(NamedInterceptorDictionaryIC) {
|
||||
v8::HandleScope scope;
|
||||
Local<ObjectTemplate> templ = ObjectTemplate::New();
|
||||
templ->SetNamedPropertyHandler(XPropertyGetter);
|
||||
LocalContext context;
|
||||
// Create an object with a named interceptor.
|
||||
context->Global()->Set(v8_str("interceptor_obj"), templ->NewInstance());
|
||||
Local<Script> script = Script::Compile(v8_str("interceptor_obj.x"));
|
||||
for (int i = 0; i < 10; i++) {
|
||||
Local<Value> result = script->Run();
|
||||
CHECK_EQ(result, v8_str("x"));
|
||||
}
|
||||
// Create a slow case object and a function accessing a property in
|
||||
// that slow case object (with dictionary probing in generated
|
||||
// code). Then force object with a named interceptor into slow-case,
|
||||
// pass it to the function, and check that the interceptor is called
|
||||
// instead of accessing the local property.
|
||||
Local<Value> result =
|
||||
CompileRun("function get_x(o) { return o.x; };"
|
||||
"var obj = { x : 42, y : 0 };"
|
||||
"delete obj.y;"
|
||||
"for (var i = 0; i < 10; i++) get_x(obj);"
|
||||
"interceptor_obj.x = 42;"
|
||||
"interceptor_obj.y = 10;"
|
||||
"delete interceptor_obj.y;"
|
||||
"get_x(interceptor_obj)");
|
||||
CHECK_EQ(result, v8_str("x"));
|
||||
}
|
||||
|
||||
|
||||
static v8::Handle<Value> SetXOnPrototypeGetter(Local<String> property,
|
||||
const AccessorInfo& info) {
|
||||
// Set x on the prototype object and do not handle the get request.
|
||||
|
Loading…
Reference in New Issue
Block a user