Commit 9f375ea8 authored by erik.corry@gmail.com's avatar erik.corry@gmail.com

Fix secondary stub cache and add a test for the stub cache lookups.

Review URL: https://chromiumcodereview.appspot.com/9496010

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10864 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent abffcbdd
...@@ -399,7 +399,7 @@ void CallICBase::GenerateMonomorphicCacheProbe(MacroAssembler* masm, ...@@ -399,7 +399,7 @@ void CallICBase::GenerateMonomorphicCacheProbe(MacroAssembler* masm,
NORMAL, NORMAL,
argc); argc);
Isolate::Current()->stub_cache()->GenerateProbe( Isolate::Current()->stub_cache()->GenerateProbe(
masm, flags, r1, r2, r3, r4, r5); masm, flags, r1, r2, r3, r4, r5, r6);
// If the stub cache probing failed, the receiver might be a value. // If the stub cache probing failed, the receiver might be a value.
// For value objects, we use the map of the prototype objects for // For value objects, we use the map of the prototype objects for
...@@ -438,7 +438,7 @@ void CallICBase::GenerateMonomorphicCacheProbe(MacroAssembler* masm, ...@@ -438,7 +438,7 @@ void CallICBase::GenerateMonomorphicCacheProbe(MacroAssembler* masm,
// Probe the stub cache for the value object. // Probe the stub cache for the value object.
__ bind(&probe); __ bind(&probe);
Isolate::Current()->stub_cache()->GenerateProbe( Isolate::Current()->stub_cache()->GenerateProbe(
masm, flags, r1, r2, r3, r4, r5); masm, flags, r1, r2, r3, r4, r5, r6);
__ bind(&miss); __ bind(&miss);
} }
...@@ -706,7 +706,7 @@ void LoadIC::GenerateMegamorphic(MacroAssembler* masm) { ...@@ -706,7 +706,7 @@ void LoadIC::GenerateMegamorphic(MacroAssembler* masm) {
Code::Flags flags = Code::Flags flags =
Code::ComputeFlags(Code::LOAD_IC, MONOMORPHIC); Code::ComputeFlags(Code::LOAD_IC, MONOMORPHIC);
Isolate::Current()->stub_cache()->GenerateProbe( Isolate::Current()->stub_cache()->GenerateProbe(
masm, flags, r0, r2, r3, r4, r5); masm, flags, r0, r2, r3, r4, r5, r6);
// Cache miss: Jump to runtime. // Cache miss: Jump to runtime.
GenerateMiss(masm); GenerateMiss(masm);
...@@ -1516,7 +1516,7 @@ void StoreIC::GenerateMegamorphic(MacroAssembler* masm, ...@@ -1516,7 +1516,7 @@ void StoreIC::GenerateMegamorphic(MacroAssembler* masm,
Code::ComputeFlags(Code::STORE_IC, MONOMORPHIC, strict_mode); Code::ComputeFlags(Code::STORE_IC, MONOMORPHIC, strict_mode);
Isolate::Current()->stub_cache()->GenerateProbe( Isolate::Current()->stub_cache()->GenerateProbe(
masm, flags, r1, r2, r3, r4, r5); masm, flags, r1, r2, r3, r4, r5, r6);
// Cache miss: Jump to runtime. // Cache miss: Jump to runtime.
GenerateMiss(masm); GenerateMiss(masm);
......
...@@ -43,59 +43,83 @@ static void ProbeTable(Isolate* isolate, ...@@ -43,59 +43,83 @@ static void ProbeTable(Isolate* isolate,
MacroAssembler* masm, MacroAssembler* masm,
Code::Flags flags, Code::Flags flags,
StubCache::Table table, StubCache::Table table,
Register receiver,
Register name, Register name,
// Number of the cache entry, not scaled.
Register offset, Register offset,
int offset_shift_bits,
Register scratch, Register scratch,
Register scratch2) { Register scratch2,
Register offset_scratch) {
ExternalReference key_offset(isolate->stub_cache()->key_reference(table)); ExternalReference key_offset(isolate->stub_cache()->key_reference(table));
ExternalReference value_offset(isolate->stub_cache()->value_reference(table)); ExternalReference value_offset(isolate->stub_cache()->value_reference(table));
ExternalReference map_offset(isolate->stub_cache()->map_reference(table));
uint32_t key_off_addr = reinterpret_cast<uint32_t>(key_offset.address()); uint32_t key_off_addr = reinterpret_cast<uint32_t>(key_offset.address());
uint32_t value_off_addr = reinterpret_cast<uint32_t>(value_offset.address()); uint32_t value_off_addr = reinterpret_cast<uint32_t>(value_offset.address());
uint32_t map_off_addr = reinterpret_cast<uint32_t>(map_offset.address());
// Check the relative positions of the address fields. // Check the relative positions of the address fields.
ASSERT(value_off_addr > key_off_addr); ASSERT(value_off_addr > key_off_addr);
ASSERT((value_off_addr - key_off_addr) % 4 == 0); ASSERT((value_off_addr - key_off_addr) % 4 == 0);
ASSERT((value_off_addr - key_off_addr) < (256 * 4)); ASSERT((value_off_addr - key_off_addr) < (256 * 4));
ASSERT(map_off_addr > key_off_addr);
ASSERT((map_off_addr - key_off_addr) % 4 == 0);
ASSERT((map_off_addr - key_off_addr) < (256 * 4));
Label miss; Label miss;
Register offsets_base_addr = scratch; Register base_addr = scratch;
scratch = no_reg;
// Multiply by 3 because there are 3 fields per entry (name, code, map).
__ add(offset_scratch, offset, Operand(offset, LSL, 1));
// Calculate the base address of the entry.
__ mov(base_addr, Operand(key_offset));
__ add(base_addr, base_addr, Operand(offset_scratch, LSL, kPointerSizeLog2));
// Check that the key in the entry matches the name. // Check that the key in the entry matches the name.
__ mov(offsets_base_addr, Operand(key_offset)); __ ldr(ip, MemOperand(base_addr, 0));
__ ldr(ip, MemOperand(offsets_base_addr, offset, LSL, 1 + offset_shift_bits));
__ cmp(name, ip); __ cmp(name, ip);
__ b(ne, &miss); __ b(ne, &miss);
// Check the map matches.
__ ldr(ip, MemOperand(base_addr, map_off_addr - key_off_addr));
__ ldr(scratch2, FieldMemOperand(receiver, HeapObject::kMapOffset));
__ cmp(ip, scratch2);
__ b(ne, &miss);
// Get the code entry from the cache. // Get the code entry from the cache.
__ add(offsets_base_addr, offsets_base_addr, Register code = scratch2;
Operand(value_off_addr - key_off_addr)); scratch2 = no_reg;
__ ldr(scratch2, __ ldr(code, MemOperand(base_addr, value_off_addr - key_off_addr));
MemOperand(offsets_base_addr, offset, LSL, 1 + offset_shift_bits));
// Check that the flags match what we're looking for. // Check that the flags match what we're looking for.
__ ldr(scratch2, FieldMemOperand(scratch2, Code::kFlagsOffset)); Register flags_reg = base_addr;
base_addr = no_reg;
__ ldr(flags_reg, FieldMemOperand(code, Code::kFlagsOffset));
// It's a nice optimization if this constant is encodable in the bic insn. // It's a nice optimization if this constant is encodable in the bic insn.
uint32_t mask = Code::kFlagsNotUsedInLookup; uint32_t mask = Code::kFlagsNotUsedInLookup;
ASSERT(__ ImmediateFitsAddrMode1Instruction(mask)); ASSERT(__ ImmediateFitsAddrMode1Instruction(mask));
__ bic(scratch2, scratch2, Operand(mask)); __ bic(flags_reg, flags_reg, Operand(mask));
// Using cmn and the negative instead of cmp means we can use movw. // Using cmn and the negative instead of cmp means we can use movw.
if (flags < 0) { if (flags < 0) {
__ cmn(scratch2, Operand(-flags)); __ cmn(flags_reg, Operand(-flags));
} else { } else {
__ cmp(scratch2, Operand(flags)); __ cmp(flags_reg, Operand(flags));
} }
__ b(ne, &miss); __ b(ne, &miss);
// Re-load code entry from cache. #ifdef DEBUG
__ ldr(offset, if (FLAG_test_secondary_stub_cache && table == StubCache::kPrimary) {
MemOperand(offsets_base_addr, offset, LSL, 1 + offset_shift_bits)); __ jmp(&miss);
} else if (FLAG_test_primary_stub_cache && table == StubCache::kSecondary) {
__ jmp(&miss);
}
#endif
// Jump to the first instruction in the code stub. // Jump to the first instruction in the code stub.
__ add(offset, offset, Operand(Code::kHeaderSize - kHeapObjectTag)); __ add(pc, code, Operand(Code::kHeaderSize - kHeapObjectTag));
__ Jump(offset);
// Miss: fall through. // Miss: fall through.
__ bind(&miss); __ bind(&miss);
...@@ -167,13 +191,14 @@ void StubCache::GenerateProbe(MacroAssembler* masm, ...@@ -167,13 +191,14 @@ void StubCache::GenerateProbe(MacroAssembler* masm,
Register name, Register name,
Register scratch, Register scratch,
Register extra, Register extra,
Register extra2) { Register extra2,
Register extra3) {
Isolate* isolate = masm->isolate(); Isolate* isolate = masm->isolate();
Label miss; Label miss;
// Make sure that code is valid. The shifting code relies on the // Make sure that code is valid. The multiplying code relies on the
// entry size being 8. // entry size being 12.
ASSERT(sizeof(Entry) == 8); ASSERT(sizeof(Entry) == 12);
// Make sure the flags does not name a specific type. // Make sure the flags does not name a specific type.
ASSERT(Code::ExtractTypeFromFlags(flags) == 0); ASSERT(Code::ExtractTypeFromFlags(flags) == 0);
...@@ -193,6 +218,11 @@ void StubCache::GenerateProbe(MacroAssembler* masm, ...@@ -193,6 +218,11 @@ void StubCache::GenerateProbe(MacroAssembler* masm,
ASSERT(!scratch.is(no_reg)); ASSERT(!scratch.is(no_reg));
ASSERT(!extra.is(no_reg)); ASSERT(!extra.is(no_reg));
ASSERT(!extra2.is(no_reg)); ASSERT(!extra2.is(no_reg));
ASSERT(!extra3.is(no_reg));
Counters* counters = masm->isolate()->counters();
__ IncrementCounter(counters->megamorphic_stub_cache_probes(), 1,
extra2, extra3);
// Check that the receiver isn't a smi. // Check that the receiver isn't a smi.
__ JumpIfSmi(receiver, &miss); __ JumpIfSmi(receiver, &miss);
...@@ -201,29 +231,32 @@ void StubCache::GenerateProbe(MacroAssembler* masm, ...@@ -201,29 +231,32 @@ void StubCache::GenerateProbe(MacroAssembler* masm,
__ ldr(scratch, FieldMemOperand(name, String::kHashFieldOffset)); __ ldr(scratch, FieldMemOperand(name, String::kHashFieldOffset));
__ ldr(ip, FieldMemOperand(receiver, HeapObject::kMapOffset)); __ ldr(ip, FieldMemOperand(receiver, HeapObject::kMapOffset));
__ add(scratch, scratch, Operand(ip)); __ add(scratch, scratch, Operand(ip));
uint32_t mask = (kPrimaryTableSize - 1) << kHeapObjectTagSize; uint32_t mask = kPrimaryTableSize - 1;
// We shift out the last two bits because they are not part of the hash and
// they are always 01 for maps.
__ mov(scratch, Operand(scratch, LSR, kHeapObjectTagSize));
// Mask down the eor argument to the minimum to keep the immediate // Mask down the eor argument to the minimum to keep the immediate
// ARM-encodable. // ARM-encodable.
__ eor(scratch, scratch, Operand(flags & mask)); __ eor(scratch, scratch, Operand(flags & mask));
// Prefer and_ to ubfx here because ubfx takes 2 cycles. // Prefer and_ to ubfx here because ubfx takes 2 cycles.
__ and_(scratch, scratch, Operand(mask)); __ and_(scratch, scratch, Operand(mask));
__ mov(scratch, Operand(scratch, LSR, 1));
// Probe the primary table. // Probe the primary table.
ProbeTable(isolate, ProbeTable(isolate,
masm, masm,
flags, flags,
kPrimary, kPrimary,
receiver,
name, name,
scratch, scratch,
1,
extra, extra,
extra2); extra2,
extra3);
// Primary miss: Compute hash for secondary probe. // Primary miss: Compute hash for secondary probe.
__ sub(scratch, scratch, Operand(name, LSR, 1)); __ sub(scratch, scratch, Operand(name, LSR, kHeapObjectTagSize));
uint32_t mask2 = (kSecondaryTableSize - 1) << (kHeapObjectTagSize - 1); uint32_t mask2 = (kSecondaryTableSize - 1);
__ add(scratch, scratch, Operand((flags >> 1) & mask2)); __ add(scratch, scratch, Operand((flags >> kHeapObjectTagSize) & mask2));
__ and_(scratch, scratch, Operand(mask2)); __ and_(scratch, scratch, Operand(mask2));
// Probe the secondary table. // Probe the secondary table.
...@@ -231,15 +264,18 @@ void StubCache::GenerateProbe(MacroAssembler* masm, ...@@ -231,15 +264,18 @@ void StubCache::GenerateProbe(MacroAssembler* masm,
masm, masm,
flags, flags,
kSecondary, kSecondary,
receiver,
name, name,
scratch, scratch,
1,
extra, extra,
extra2); extra2,
extra3);
// Cache miss: Fall-through and let caller handle the miss by // Cache miss: Fall-through and let caller handle the miss by
// entering the runtime system. // entering the runtime system.
__ bind(&miss); __ bind(&miss);
__ IncrementCounter(counters->megamorphic_stub_cache_misses(), 1,
extra2, extra3);
} }
......
...@@ -563,6 +563,13 @@ DEFINE_bool(trace_elements_transitions, false, "trace elements transitions") ...@@ -563,6 +563,13 @@ DEFINE_bool(trace_elements_transitions, false, "trace elements transitions")
// code-stubs.cc // code-stubs.cc
DEFINE_bool(print_code_stubs, false, "print code stubs") DEFINE_bool(print_code_stubs, false, "print code stubs")
DEFINE_bool(test_secondary_stub_cache,
false,
"test secondary stub cache by disabling the primary one")
DEFINE_bool(test_primary_stub_cache,
false,
"test primary stub cache by disabling the secondary one")
// codegen-ia32.cc / codegen-arm.cc // codegen-ia32.cc / codegen-arm.cc
DEFINE_bool(print_code, false, "print generated code") DEFINE_bool(print_code, false, "print generated code")
......
...@@ -44,19 +44,30 @@ static void ProbeTable(Isolate* isolate, ...@@ -44,19 +44,30 @@ static void ProbeTable(Isolate* isolate,
Code::Flags flags, Code::Flags flags,
StubCache::Table table, StubCache::Table table,
Register name, Register name,
Register receiver,
// Number of the cache entry pointer-size scaled.
Register offset, Register offset,
Register extra) { Register extra) {
ExternalReference key_offset(isolate->stub_cache()->key_reference(table)); ExternalReference key_offset(isolate->stub_cache()->key_reference(table));
ExternalReference value_offset(isolate->stub_cache()->value_reference(table)); ExternalReference value_offset(isolate->stub_cache()->value_reference(table));
ExternalReference map_offset(isolate->stub_cache()->map_reference(table));
Label miss; Label miss;
// Multiply by 3 because there are 3 fields per entry (name, code, map).
__ lea(offset, Operand(offset, offset, times_2, 0));
if (extra.is_valid()) { if (extra.is_valid()) {
// Get the code entry from the cache. // Get the code entry from the cache.
__ mov(extra, Operand::StaticArray(offset, times_2, value_offset)); __ mov(extra, Operand::StaticArray(offset, times_1, value_offset));
// Check that the key in the entry matches the name. // Check that the key in the entry matches the name.
__ cmp(name, Operand::StaticArray(offset, times_2, key_offset)); __ cmp(name, Operand::StaticArray(offset, times_1, key_offset));
__ j(not_equal, &miss);
// Check the map matches.
__ mov(offset, Operand::StaticArray(offset, times_1, map_offset));
__ cmp(offset, FieldOperand(receiver, HeapObject::kMapOffset));
__ j(not_equal, &miss); __ j(not_equal, &miss);
// Check that the flags match what we're looking for. // Check that the flags match what we're looking for.
...@@ -65,6 +76,14 @@ static void ProbeTable(Isolate* isolate, ...@@ -65,6 +76,14 @@ static void ProbeTable(Isolate* isolate,
__ cmp(offset, flags); __ cmp(offset, flags);
__ j(not_equal, &miss); __ j(not_equal, &miss);
#ifdef DEBUG
if (FLAG_test_secondary_stub_cache && table == StubCache::kPrimary) {
__ jmp(&miss);
} else if (FLAG_test_primary_stub_cache && table == StubCache::kSecondary) {
__ jmp(&miss);
}
#endif
// Jump to the first instruction in the code stub. // Jump to the first instruction in the code stub.
__ add(extra, Immediate(Code::kHeaderSize - kHeapObjectTag)); __ add(extra, Immediate(Code::kHeaderSize - kHeapObjectTag));
__ jmp(extra); __ jmp(extra);
...@@ -75,11 +94,19 @@ static void ProbeTable(Isolate* isolate, ...@@ -75,11 +94,19 @@ static void ProbeTable(Isolate* isolate,
__ push(offset); __ push(offset);
// Check that the key in the entry matches the name. // Check that the key in the entry matches the name.
__ cmp(name, Operand::StaticArray(offset, times_2, key_offset)); __ cmp(name, Operand::StaticArray(offset, times_1, key_offset));
__ j(not_equal, &miss);
// Check the map matches.
__ mov(offset, Operand::StaticArray(offset, times_1, map_offset));
__ cmp(offset, FieldOperand(receiver, HeapObject::kMapOffset));
__ j(not_equal, &miss); __ j(not_equal, &miss);
// Restore offset register.
__ mov(offset, Operand(esp, 0));
// Get the code entry from the cache. // Get the code entry from the cache.
__ mov(offset, Operand::StaticArray(offset, times_2, value_offset)); __ mov(offset, Operand::StaticArray(offset, times_1, value_offset));
// Check that the flags match what we're looking for. // Check that the flags match what we're looking for.
__ mov(offset, FieldOperand(offset, Code::kFlagsOffset)); __ mov(offset, FieldOperand(offset, Code::kFlagsOffset));
...@@ -87,9 +114,17 @@ static void ProbeTable(Isolate* isolate, ...@@ -87,9 +114,17 @@ static void ProbeTable(Isolate* isolate,
__ cmp(offset, flags); __ cmp(offset, flags);
__ j(not_equal, &miss); __ j(not_equal, &miss);
#ifdef DEBUG
if (FLAG_test_secondary_stub_cache && table == StubCache::kPrimary) {
__ jmp(&miss);
} else if (FLAG_test_primary_stub_cache && table == StubCache::kSecondary) {
__ jmp(&miss);
}
#endif
// Restore offset and re-load code entry from cache. // Restore offset and re-load code entry from cache.
__ pop(offset); __ pop(offset);
__ mov(offset, Operand::StaticArray(offset, times_2, value_offset)); __ mov(offset, Operand::StaticArray(offset, times_1, value_offset));
// Jump to the first instruction in the code stub. // Jump to the first instruction in the code stub.
__ add(offset, Immediate(Code::kHeaderSize - kHeapObjectTag)); __ add(offset, Immediate(Code::kHeaderSize - kHeapObjectTag));
...@@ -159,12 +194,13 @@ void StubCache::GenerateProbe(MacroAssembler* masm, ...@@ -159,12 +194,13 @@ void StubCache::GenerateProbe(MacroAssembler* masm,
Register name, Register name,
Register scratch, Register scratch,
Register extra, Register extra,
Register extra2) { Register extra2,
Register extra3) {
Label miss; Label miss;
// Assert that code is valid. The shifting code relies on the entry size // Assert that code is valid. The multiplying code relies on the entry size
// being 8. // being 12.
ASSERT(sizeof(Entry) == 8); ASSERT(sizeof(Entry) == 12);
// Assert the flags do not name a specific type. // Assert the flags do not name a specific type.
ASSERT(Code::ExtractTypeFromFlags(flags) == 0); ASSERT(Code::ExtractTypeFromFlags(flags) == 0);
...@@ -176,37 +212,51 @@ void StubCache::GenerateProbe(MacroAssembler* masm, ...@@ -176,37 +212,51 @@ void StubCache::GenerateProbe(MacroAssembler* masm,
ASSERT(!extra.is(name)); ASSERT(!extra.is(name));
ASSERT(!extra.is(scratch)); ASSERT(!extra.is(scratch));
// Assert scratch and extra registers are valid, and extra2 is unused. // Assert scratch and extra registers are valid, and extra2/3 are unused.
ASSERT(!scratch.is(no_reg)); ASSERT(!scratch.is(no_reg));
ASSERT(extra2.is(no_reg)); ASSERT(extra2.is(no_reg));
ASSERT(extra3.is(no_reg));
Register offset = scratch;
scratch = no_reg;
Counters* counters = masm->isolate()->counters();
__ IncrementCounter(counters->megamorphic_stub_cache_probes(), 1);
// Check that the receiver isn't a smi. // Check that the receiver isn't a smi.
__ JumpIfSmi(receiver, &miss); __ JumpIfSmi(receiver, &miss);
// Get the map of the receiver and compute the hash. // Get the map of the receiver and compute the hash.
__ mov(scratch, FieldOperand(name, String::kHashFieldOffset)); __ mov(offset, FieldOperand(name, String::kHashFieldOffset));
__ add(scratch, FieldOperand(receiver, HeapObject::kMapOffset)); __ add(offset, FieldOperand(receiver, HeapObject::kMapOffset));
__ xor_(scratch, flags); __ xor_(offset, flags);
__ and_(scratch, (kPrimaryTableSize - 1) << kHeapObjectTagSize); // We mask out the last two bits because they are not part of the hash and
// they are always 01 for maps. Also in the two 'and' instructions below.
__ and_(offset, (kPrimaryTableSize - 1) << kHeapObjectTagSize);
// ProbeTable expects the offset to be pointer scaled, which it is, because
// the heap object tag size is 2 and the pointer size log 2 is also 2.
ASSERT(kHeapObjectTagSize == kPointerSizeLog2);
// Probe the primary table. // Probe the primary table.
ProbeTable(isolate(), masm, flags, kPrimary, name, scratch, extra); ProbeTable(isolate(), masm, flags, kPrimary, name, receiver, offset, extra);
// Primary miss: Compute hash for secondary probe. // Primary miss: Compute hash for secondary probe.
__ mov(scratch, FieldOperand(name, String::kHashFieldOffset)); __ mov(offset, FieldOperand(name, String::kHashFieldOffset));
__ add(scratch, FieldOperand(receiver, HeapObject::kMapOffset)); __ add(offset, FieldOperand(receiver, HeapObject::kMapOffset));
__ xor_(scratch, flags); __ xor_(offset, flags);
__ and_(scratch, (kPrimaryTableSize - 1) << kHeapObjectTagSize); __ and_(offset, (kPrimaryTableSize - 1) << kHeapObjectTagSize);
__ sub(scratch, name); __ sub(offset, name);
__ add(scratch, Immediate(flags)); __ add(offset, Immediate(flags));
__ and_(scratch, (kSecondaryTableSize - 1) << kHeapObjectTagSize); __ and_(offset, (kSecondaryTableSize - 1) << kHeapObjectTagSize);
// Probe the secondary table. // Probe the secondary table.
ProbeTable(isolate(), masm, flags, kSecondary, name, scratch, extra); ProbeTable(
isolate(), masm, flags, kSecondary, name, receiver, offset, extra);
// Cache miss: Fall-through and let caller handle the miss by // Cache miss: Fall-through and let caller handle the miss by
// entering the runtime system. // entering the runtime system.
__ bind(&miss); __ bind(&miss);
__ IncrementCounter(counters->megamorphic_stub_cache_misses(), 1);
} }
......
...@@ -273,14 +273,22 @@ void ExternalReferenceTable::PopulateTable(Isolate* isolate) { ...@@ -273,14 +273,22 @@ void ExternalReferenceTable::PopulateTable(Isolate* isolate) {
STUB_CACHE_TABLE, STUB_CACHE_TABLE,
2, 2,
"StubCache::primary_->value"); "StubCache::primary_->value");
Add(stub_cache->key_reference(StubCache::kSecondary).address(), Add(stub_cache->map_reference(StubCache::kPrimary).address(),
STUB_CACHE_TABLE, STUB_CACHE_TABLE,
3, 3,
"StubCache::primary_->map");
Add(stub_cache->key_reference(StubCache::kSecondary).address(),
STUB_CACHE_TABLE,
4,
"StubCache::secondary_->key"); "StubCache::secondary_->key");
Add(stub_cache->value_reference(StubCache::kSecondary).address(), Add(stub_cache->value_reference(StubCache::kSecondary).address(),
STUB_CACHE_TABLE, STUB_CACHE_TABLE,
4, 5,
"StubCache::secondary_->value"); "StubCache::secondary_->value");
Add(stub_cache->map_reference(StubCache::kSecondary).address(),
STUB_CACHE_TABLE,
6,
"StubCache::secondary_->map");
// Runtime entries // Runtime entries
Add(ExternalReference::perform_gc_function(isolate).address(), Add(ExternalReference::perform_gc_function(isolate).address(),
......
...@@ -77,14 +77,15 @@ Code* StubCache::Set(String* name, Map* map, Code* code) { ...@@ -77,14 +77,15 @@ Code* StubCache::Set(String* name, Map* map, Code* code) {
// Compute the primary entry. // Compute the primary entry.
int primary_offset = PrimaryOffset(name, flags, map); int primary_offset = PrimaryOffset(name, flags, map);
Entry* primary = entry(primary_, primary_offset); Entry* primary = entry(primary_, primary_offset);
Code* hit = primary->value; Code* old_code = primary->value;
// If the primary entry has useful data in it, we retire it to the // If the primary entry has useful data in it, we retire it to the
// secondary cache before overwriting it. // secondary cache before overwriting it.
if (hit != isolate_->builtins()->builtin(Builtins::kIllegal)) { if (old_code != isolate_->builtins()->builtin(Builtins::kIllegal)) {
Code::Flags primary_flags = Code::RemoveTypeFromFlags(hit->flags()); Map* old_map = primary->map;
int secondary_offset = Code::Flags old_flags = Code::RemoveTypeFromFlags(old_code->flags());
SecondaryOffset(primary->key, primary_flags, primary_offset); int seed = PrimaryOffset(primary->key, old_flags, old_map);
int secondary_offset = SecondaryOffset(primary->key, old_flags, seed);
Entry* secondary = entry(secondary_, secondary_offset); Entry* secondary = entry(secondary_, secondary_offset);
*secondary = *primary; *secondary = *primary;
} }
...@@ -92,6 +93,8 @@ Code* StubCache::Set(String* name, Map* map, Code* code) { ...@@ -92,6 +93,8 @@ Code* StubCache::Set(String* name, Map* map, Code* code) {
// Update primary cache. // Update primary cache.
primary->key = name; primary->key = name;
primary->value = code; primary->value = code;
primary->map = map;
isolate()->counters()->megamorphic_stub_cache_updates()->Increment();
return code; return code;
} }
......
...@@ -69,6 +69,7 @@ class StubCache { ...@@ -69,6 +69,7 @@ class StubCache {
struct Entry { struct Entry {
String* key; String* key;
Code* value; Code* value;
Map* map;
}; };
void Initialize(); void Initialize();
...@@ -252,7 +253,7 @@ class StubCache { ...@@ -252,7 +253,7 @@ class StubCache {
Handle<Context> global_context); Handle<Context> global_context);
// Generate code for probing the stub cache table. // Generate code for probing the stub cache table.
// Arguments extra and extra2 may be used to pass additional scratch // Arguments extra, extra2 and extra3 may be used to pass additional scratch
// registers. Set to no_reg if not needed. // registers. Set to no_reg if not needed.
void GenerateProbe(MacroAssembler* masm, void GenerateProbe(MacroAssembler* masm,
Code::Flags flags, Code::Flags flags,
...@@ -260,7 +261,8 @@ class StubCache { ...@@ -260,7 +261,8 @@ class StubCache {
Register name, Register name,
Register scratch, Register scratch,
Register extra, Register extra,
Register extra2 = no_reg); Register extra2 = no_reg,
Register extra3 = no_reg);
enum Table { enum Table {
kPrimary, kPrimary,
...@@ -274,6 +276,12 @@ class StubCache { ...@@ -274,6 +276,12 @@ class StubCache {
} }
SCTableReference map_reference(StubCache::Table table) {
return SCTableReference(
reinterpret_cast<Address>(&first_entry(table)->map));
}
SCTableReference value_reference(StubCache::Table table) { SCTableReference value_reference(StubCache::Table table) {
return SCTableReference( return SCTableReference(
reinterpret_cast<Address>(&first_entry(table)->value)); reinterpret_cast<Address>(&first_entry(table)->value));
...@@ -300,7 +308,16 @@ class StubCache { ...@@ -300,7 +308,16 @@ class StubCache {
RelocInfo::Mode mode, RelocInfo::Mode mode,
Code::Kind kind); Code::Kind kind);
// Computes the hashed offsets for primary and secondary caches. // The stub cache has a primary and secondary level. The two levels have
// different hashing algorithms in order to avoid simultaneous collisions
// in both caches. Unlike a probing strategy (quadratic or otherwise) the
// update strategy on updates is fairly clear and simple: Any existing entry
// in the primary cache is moved to the secondary cache, and secondary cache
// entries are overwritten.
// Hash algorithm for the primary table. This algorithm is replicated in
// assembler for every architecture. Returns an index into the table that
// is scaled by 1 << kHeapObjectTagSize.
static int PrimaryOffset(String* name, Code::Flags flags, Map* map) { static int PrimaryOffset(String* name, Code::Flags flags, Map* map) {
// This works well because the heap object tag size and the hash // This works well because the heap object tag size and the hash
// shift are equal. Shifting down the length field to get the // shift are equal. Shifting down the length field to get the
...@@ -324,23 +341,30 @@ class StubCache { ...@@ -324,23 +341,30 @@ class StubCache {
return key & ((kPrimaryTableSize - 1) << kHeapObjectTagSize); return key & ((kPrimaryTableSize - 1) << kHeapObjectTagSize);
} }
// Hash algorithm for the secondary table. This algorithm is replicated in
// assembler for every architecture. Returns an index into the table that
// is scaled by 1 << kHeapObjectTagSize.
static int SecondaryOffset(String* name, Code::Flags flags, int seed) { static int SecondaryOffset(String* name, Code::Flags flags, int seed) {
// Use the seed from the primary cache in the secondary cache. // Use the seed from the primary cache in the secondary cache.
uint32_t string_low32bits = uint32_t string_low32bits =
static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name)); static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name));
uint32_t key = seed - string_low32bits + flags; // We always set the in_loop bit to zero when generating the lookup code
// so do it here too so the hash codes match.
uint32_t iflags =
(static_cast<uint32_t>(flags) & ~Code::kFlagsNotUsedInLookup);
uint32_t key = (seed - string_low32bits) + iflags;
return key & ((kSecondaryTableSize - 1) << kHeapObjectTagSize); return key & ((kSecondaryTableSize - 1) << kHeapObjectTagSize);
} }
// Compute the entry for a given offset in exactly the same way as // Compute the entry for a given offset in exactly the same way as
// we do in generated code. We generate an hash code that already // we do in generated code. We generate an hash code that already
// ends in String::kHashShift 0s. Then we shift it so it is a multiple // ends in String::kHashShift 0s. Then we multiply it so it is a multiple
// of sizeof(Entry). This makes it easier to avoid making mistakes // of sizeof(Entry). This makes it easier to avoid making mistakes
// in the hashed offset computations. // in the hashed offset computations.
static Entry* entry(Entry* table, int offset) { static Entry* entry(Entry* table, int offset) {
const int shift_amount = kPointerSizeLog2 + 1 - String::kHashShift; const int multiplier = sizeof(*table) >> String::kHashShift;
return reinterpret_cast<Entry*>( return reinterpret_cast<Entry*>(
reinterpret_cast<Address>(table) + (offset << shift_amount)); reinterpret_cast<Address>(table) + offset * multiplier);
} }
static const int kPrimaryTableBits = 11; static const int kPrimaryTableBits = 11;
......
...@@ -198,6 +198,9 @@ namespace internal { ...@@ -198,6 +198,9 @@ namespace internal {
SC(constructed_objects_stub, V8.ConstructedObjectsStub) \ SC(constructed_objects_stub, V8.ConstructedObjectsStub) \
SC(negative_lookups, V8.NegativeLookups) \ SC(negative_lookups, V8.NegativeLookups) \
SC(negative_lookups_miss, V8.NegativeLookupsMiss) \ SC(negative_lookups_miss, V8.NegativeLookupsMiss) \
SC(megamorphic_stub_cache_probes, V8.MegamorphicStubCacheProbes) \
SC(megamorphic_stub_cache_misses, V8.MegamorphicStubCacheMisses) \
SC(megamorphic_stub_cache_updates, V8.MegamorphicStubCacheUpdates) \
SC(array_function_runtime, V8.ArrayFunctionRuntime) \ SC(array_function_runtime, V8.ArrayFunctionRuntime) \
SC(array_function_native, V8.ArrayFunctionNative) \ SC(array_function_native, V8.ArrayFunctionNative) \
SC(for_in, V8.ForIn) \ SC(for_in, V8.ForIn) \
......
...@@ -43,32 +43,61 @@ static void ProbeTable(Isolate* isolate, ...@@ -43,32 +43,61 @@ static void ProbeTable(Isolate* isolate,
MacroAssembler* masm, MacroAssembler* masm,
Code::Flags flags, Code::Flags flags,
StubCache::Table table, StubCache::Table table,
Register receiver,
Register name, Register name,
// The offset is scaled by 4, based on
// kHeapObjectTagSize, which is two bits
Register offset) { Register offset) {
ASSERT_EQ(8, kPointerSize); // We need to scale up the pointer by 2 because the offset is scaled by less
ASSERT_EQ(16, sizeof(StubCache::Entry)); // than the pointer size.
ASSERT(kPointerSizeLog2 == kHeapObjectTagSize + 1);
ScaleFactor scale_factor = times_2;
ASSERT_EQ(24, sizeof(StubCache::Entry));
// The offset register holds the entry offset times four (due to masking // The offset register holds the entry offset times four (due to masking
// and shifting optimizations). // and shifting optimizations).
ExternalReference key_offset(isolate->stub_cache()->key_reference(table)); ExternalReference key_offset(isolate->stub_cache()->key_reference(table));
ExternalReference value_offset(isolate->stub_cache()->value_reference(table));
Label miss; Label miss;
// Multiply by 3 because there are 3 fields per entry (name, code, map).
__ lea(offset, Operand(offset, offset, times_2, 0));
__ LoadAddress(kScratchRegister, key_offset); __ LoadAddress(kScratchRegister, key_offset);
// Check that the key in the entry matches the name. // Check that the key in the entry matches the name.
// Multiply entry offset by 16 to get the entry address. Since the // Multiply entry offset by 16 to get the entry address. Since the
// offset register already holds the entry offset times four, multiply // offset register already holds the entry offset times four, multiply
// by a further four. // by a further four.
__ cmpl(name, Operand(kScratchRegister, offset, times_4, 0)); __ cmpl(name, Operand(kScratchRegister, offset, scale_factor, 0));
__ j(not_equal, &miss); __ j(not_equal, &miss);
// Get the map entry from the cache.
// Use key_offset + kPointerSize * 2, rather than loading map_offset.
__ movq(kScratchRegister,
Operand(kScratchRegister, offset, scale_factor, kPointerSize * 2));
__ cmpq(kScratchRegister, FieldOperand(receiver, HeapObject::kMapOffset));
__ j(not_equal, &miss);
// Get the code entry from the cache. // Get the code entry from the cache.
// Use key_offset + kPointerSize, rather than loading value_offset. __ LoadAddress(kScratchRegister, value_offset);
__ movq(kScratchRegister, __ movq(kScratchRegister,
Operand(kScratchRegister, offset, times_4, kPointerSize)); Operand(kScratchRegister, offset, scale_factor, 0));
// Check that the flags match what we're looking for. // Check that the flags match what we're looking for.
__ movl(offset, FieldOperand(kScratchRegister, Code::kFlagsOffset)); __ movl(offset, FieldOperand(kScratchRegister, Code::kFlagsOffset));
__ and_(offset, Immediate(~Code::kFlagsNotUsedInLookup)); __ and_(offset, Immediate(~Code::kFlagsNotUsedInLookup));
__ cmpl(offset, Immediate(flags)); __ cmpl(offset, Immediate(flags));
__ j(not_equal, &miss); __ j(not_equal, &miss);
#ifdef DEBUG
if (FLAG_test_secondary_stub_cache && table == StubCache::kPrimary) {
__ jmp(&miss);
} else if (FLAG_test_primary_stub_cache && table == StubCache::kSecondary) {
__ jmp(&miss);
}
#endif
// Jump to the first instruction in the code stub. // Jump to the first instruction in the code stub.
__ addq(kScratchRegister, Immediate(Code::kHeaderSize - kHeapObjectTag)); __ addq(kScratchRegister, Immediate(Code::kHeaderSize - kHeapObjectTag));
__ jmp(kScratchRegister); __ jmp(kScratchRegister);
...@@ -134,14 +163,16 @@ void StubCache::GenerateProbe(MacroAssembler* masm, ...@@ -134,14 +163,16 @@ void StubCache::GenerateProbe(MacroAssembler* masm,
Register name, Register name,
Register scratch, Register scratch,
Register extra, Register extra,
Register extra2) { Register extra2,
Register extra3) {
Isolate* isolate = masm->isolate(); Isolate* isolate = masm->isolate();
Label miss; Label miss;
USE(extra); // The register extra is not used on the X64 platform. USE(extra); // The register extra is not used on the X64 platform.
USE(extra2); // The register extra2 is not used on the X64 platform. USE(extra2); // The register extra2 is not used on the X64 platform.
// Make sure that code is valid. The shifting code relies on the USE(extra3); // The register extra2 is not used on the X64 platform.
// entry size being 16. // Make sure that code is valid. The multiplying code relies on the
ASSERT(sizeof(Entry) == 16); // entry size being 24.
ASSERT(sizeof(Entry) == 24);
// Make sure the flags do not name a specific type. // Make sure the flags do not name a specific type.
ASSERT(Code::ExtractTypeFromFlags(flags) == 0); ASSERT(Code::ExtractTypeFromFlags(flags) == 0);
...@@ -153,6 +184,10 @@ void StubCache::GenerateProbe(MacroAssembler* masm, ...@@ -153,6 +184,10 @@ void StubCache::GenerateProbe(MacroAssembler* masm,
// Check scratch register is valid, extra and extra2 are unused. // Check scratch register is valid, extra and extra2 are unused.
ASSERT(!scratch.is(no_reg)); ASSERT(!scratch.is(no_reg));
ASSERT(extra2.is(no_reg)); ASSERT(extra2.is(no_reg));
ASSERT(extra3.is(no_reg));
Counters* counters = masm->isolate()->counters();
__ IncrementCounter(counters->megamorphic_stub_cache_probes(), 1);
// Check that the receiver isn't a smi. // Check that the receiver isn't a smi.
__ JumpIfSmi(receiver, &miss); __ JumpIfSmi(receiver, &miss);
...@@ -162,10 +197,12 @@ void StubCache::GenerateProbe(MacroAssembler* masm, ...@@ -162,10 +197,12 @@ void StubCache::GenerateProbe(MacroAssembler* masm,
// Use only the low 32 bits of the map pointer. // Use only the low 32 bits of the map pointer.
__ addl(scratch, FieldOperand(receiver, HeapObject::kMapOffset)); __ addl(scratch, FieldOperand(receiver, HeapObject::kMapOffset));
__ xor_(scratch, Immediate(flags)); __ xor_(scratch, Immediate(flags));
// We mask out the last two bits because they are not part of the hash and
// they are always 01 for maps. Also in the two 'and' instructions below.
__ and_(scratch, Immediate((kPrimaryTableSize - 1) << kHeapObjectTagSize)); __ and_(scratch, Immediate((kPrimaryTableSize - 1) << kHeapObjectTagSize));
// Probe the primary table. // Probe the primary table.
ProbeTable(isolate, masm, flags, kPrimary, name, scratch); ProbeTable(isolate, masm, flags, kPrimary, receiver, name, scratch);
// Primary miss: Compute hash for secondary probe. // Primary miss: Compute hash for secondary probe.
__ movl(scratch, FieldOperand(name, String::kHashFieldOffset)); __ movl(scratch, FieldOperand(name, String::kHashFieldOffset));
...@@ -177,11 +214,12 @@ void StubCache::GenerateProbe(MacroAssembler* masm, ...@@ -177,11 +214,12 @@ void StubCache::GenerateProbe(MacroAssembler* masm,
__ and_(scratch, Immediate((kSecondaryTableSize - 1) << kHeapObjectTagSize)); __ and_(scratch, Immediate((kSecondaryTableSize - 1) << kHeapObjectTagSize));
// Probe the secondary table. // Probe the secondary table.
ProbeTable(isolate, masm, flags, kSecondary, name, scratch); ProbeTable(isolate, masm, flags, kSecondary, receiver, name, scratch);
// Cache miss: Fall-through and let caller handle the miss by // Cache miss: Fall-through and let caller handle the miss by
// entering the runtime system. // entering the runtime system.
__ bind(&miss); __ bind(&miss);
__ IncrementCounter(counters->megamorphic_stub_cache_misses(), 1);
} }
......
...@@ -16102,3 +16102,72 @@ TEST(CallCompletedCallbackTwoExceptions) { ...@@ -16102,3 +16102,72 @@ TEST(CallCompletedCallbackTwoExceptions) {
v8::V8::AddCallCompletedCallback(CallCompletedCallbackException); v8::V8::AddCallCompletedCallback(CallCompletedCallbackException);
CompileRun("throw 'first exception';"); CompileRun("throw 'first exception';");
} }
static int probes_counter = 0;
static int misses_counter = 0;
static int updates_counter = 0;
static int* LookupCounter(const char* name) {
if (strcmp(name, "c:V8.MegamorphicStubCacheProbes") == 0) {
return &probes_counter;
} else if (strcmp(name, "c:V8.MegamorphicStubCacheMisses") == 0) {
return &misses_counter;
} else if (strcmp(name, "c:V8.MegamorphicStubCacheUpdates") == 0) {
return &updates_counter;
}
return NULL;
}
static const char* kMegamorphicTestProgram =
"function ClassA() { };"
"function ClassB() { };"
"ClassA.prototype.foo = function() { };"
"ClassB.prototype.foo = function() { };"
"function fooify(obj) { obj.foo(); };"
"var a = new ClassA();"
"var b = new ClassB();"
"for (var i = 0; i < 10000; i++) {"
" fooify(a);"
" fooify(b);"
"}";
static void StubCacheHelper(bool primary) {
V8::SetCounterFunction(LookupCounter);
USE(kMegamorphicTestProgram);
#ifdef DEBUG
i::FLAG_native_code_counters = true;
if (primary) {
i::FLAG_test_primary_stub_cache = true;
} else {
i::FLAG_test_secondary_stub_cache = true;
}
i::FLAG_crankshaft = false;
v8::HandleScope scope;
LocalContext env;
int initial_probes = probes_counter;
int initial_misses = misses_counter;
int initial_updates = updates_counter;
CompileRun(kMegamorphicTestProgram);
int probes = probes_counter - initial_probes;
int misses = misses_counter - initial_misses;
int updates = updates_counter - initial_updates;
CHECK_LT(updates, 10);
CHECK_LT(misses, 10);
CHECK_GE(probes, 10000);
#endif
}
TEST(SecondaryStubCache) {
StubCacheHelper(true);
}
TEST(PrimaryStubCache) {
StubCacheHelper(false);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment