Index: src/ia32/codegen-ia32.cc |
diff --git a/src/ia32/codegen-ia32.cc b/src/ia32/codegen-ia32.cc |
index 60969829b6bbd47e218d3b7867f8e3ccadc15e47..915bf57ccb7fa9fe4cd534eef6c2bf957925f3c1 100644 |
--- a/src/ia32/codegen-ia32.cc |
+++ b/src/ia32/codegen-ia32.cc |
@@ -6580,14 +6580,120 @@ class DeferredSearchCache: public DeferredCode { |
virtual void Generate(); |
private: |
- Register dst_, cache_, key_; |
+ Register dst_; // on invocation Smi index of finger, on exit |
+ // holds value being looked up. |
+ Register cache_; // instance of JSFunctionResultCache. |
+ Register key_; // key being looked up. |
}; |
+// Return a position of the element at |index_as_smi| + |additional_offset| |
+// in FixedArray pointer to which is held in |array|. |index_as_smi| is Smi. |
+static Operand ArrayElement(Register array, |
+ Register index_as_smi, |
+ int additional_offset = 0) { |
+ int offset = FixedArray::kHeaderSize + additional_offset * kPointerSize; |
+ return FieldOperand(array, index_as_smi, times_half_pointer_size, offset); |
+} |
+ |
+ |
void DeferredSearchCache::Generate() { |
- __ push(cache_); |
+ Label first_loop, search_further, second_loop, cache_miss; |
+ |
+ // Smi-tagging is equivalent to multiplying by 2. |
+ STATIC_ASSERT(kSmiTag == 0); |
+ STATIC_ASSERT(kSmiTagSize == 1); |
+ |
+ Smi* kEntrySizeSmi = Smi::FromInt(JSFunctionResultCache::kEntrySize); |
+ Smi* kEntriesIndexSmi = Smi::FromInt(JSFunctionResultCache::kEntriesIndex); |
+ |
+ // Check the cache from finger to start of the cache. |
+ __ bind(&first_loop); |
+ __ sub(Operand(dst_), Immediate(kEntrySizeSmi)); |
+ __ cmp(Operand(dst_), Immediate(kEntriesIndexSmi)); |
+ __ j(less, &search_further); |
+ |
+ __ cmp(key_, ArrayElement(cache_, dst_)); |
+ __ j(not_equal, &first_loop); |
+ |
+ __ mov(FieldOperand(cache_, JSFunctionResultCache::kFingerOffset), dst_); |
+ __ mov(dst_, ArrayElement(cache_, dst_, 1)); |
+ __ jmp(exit_label()); |
+ |
+ __ bind(&search_further); |
+ |
+ // Check the cache from end of cache up to finger. |
+ __ mov(dst_, FieldOperand(cache_, JSFunctionResultCache::kCacheSizeOffset)); |
+ |
+ __ bind(&second_loop); |
+ __ sub(Operand(dst_), Immediate(kEntrySizeSmi)); |
+ // Consider prefetching into some reg. |
+ __ cmp(dst_, FieldOperand(cache_, JSFunctionResultCache::kFingerOffset)); |
+ __ j(less_equal, &cache_miss); |
+ |
+ __ cmp(key_, ArrayElement(cache_, dst_)); |
+ __ j(not_equal, &second_loop); |
+ |
+ __ mov(FieldOperand(cache_, JSFunctionResultCache::kFingerOffset), dst_); |
+ __ mov(dst_, ArrayElement(cache_, dst_, 1)); |
+ __ jmp(exit_label()); |
+ |
+ __ 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(key_); |
- __ CallRuntime(Runtime::kGetFromCache, 2); |
+ // On ia32 function must be in edi. |
+ __ mov(edi, FieldOperand(cache_, JSFunctionResultCache::kFactoryOffset)); |
+ ParameterCount expected(1); |
+ __ InvokeFunction(edi, expected, CALL_FUNCTION); |
+ |
+ // Find a place to put new cached value into. |
+ Label add_new_entry, update_cache; |
+ __ mov(ecx, Operand(esp, kPointerSize)); // restore the cache |
+ // Possible optimization: cache size is constant for the given cache |
+ // so technically we could use a constant here. However, if we have |
+ // cache miss this optimization would hardly matter much. |
+ |
+ // Check if we could add new entry to cache. |
+ __ mov(ebx, FieldOperand(ecx, FixedArray::kLengthOffset)); |
+ __ SmiTag(ebx); |
+ __ cmp(ebx, FieldOperand(ecx, JSFunctionResultCache::kCacheSizeOffset)); |
+ __ j(greater, &add_new_entry); |
+ |
+ // Check if we could evict entry after finger. |
+ __ mov(edx, FieldOperand(ecx, JSFunctionResultCache::kFingerOffset)); |
+ __ add(Operand(edx), Immediate(kEntrySizeSmi)); |
+ __ cmp(ebx, Operand(edx)); |
+ __ j(greater, &update_cache); |
+ |
+ // Need to wrap over the cache. |
+ __ mov(edx, Immediate(kEntriesIndexSmi)); |
+ __ jmp(&update_cache); |
+ |
+ __ bind(&add_new_entry); |
+ __ mov(edx, FieldOperand(ecx, JSFunctionResultCache::kCacheSizeOffset)); |
+ __ lea(ebx, Operand(edx, JSFunctionResultCache::kEntrySize << 1)); |
+ __ mov(FieldOperand(ecx, JSFunctionResultCache::kCacheSizeOffset), ebx); |
+ |
+ // Update the cache itself. |
+ // edx holds the index. |
+ __ bind(&update_cache); |
+ __ pop(ebx); // restore the key |
+ __ mov(FieldOperand(ecx, JSFunctionResultCache::kFingerOffset), edx); |
+ // Store key. |
+ __ mov(ArrayElement(ecx, edx), ebx); |
+ __ RecordWrite(ecx, 0, ebx, edx); |
+ |
+ // Store value. |
+ __ pop(ecx); // restore the cache. |
+ __ mov(edx, FieldOperand(ecx, JSFunctionResultCache::kFingerOffset)); |
+ __ add(Operand(edx), Immediate(Smi::FromInt(1))); |
+ __ mov(ebx, eax); |
+ __ mov(ArrayElement(ecx, edx), ebx); |
+ __ RecordWrite(ecx, 0, ebx, edx); |
+ |
if (!dst_.is(eax)) { |
__ mov(dst_, eax); |
} |
@@ -6629,21 +6735,14 @@ void CodeGenerator::GenerateGetFromCache(ZoneList<Expression*>* args) { |
cache.reg(), |
key.reg()); |
- const int kFingerOffset = |
- FixedArray::OffsetOfElementAt(JSFunctionResultCache::kFingerIndex); |
// tmp.reg() now holds finger offset as a smi. |
ASSERT(kSmiTag == 0 && kSmiTagSize == 1); |
- __ mov(tmp.reg(), FieldOperand(cache.reg(), kFingerOffset)); |
- __ cmp(key.reg(), FieldOperand(cache.reg(), |
- tmp.reg(), // as smi |
- times_half_pointer_size, |
- FixedArray::kHeaderSize)); |
+ __ mov(tmp.reg(), FieldOperand(cache.reg(), |
+ JSFunctionResultCache::kFingerOffset)); |
+ __ cmp(key.reg(), ArrayElement(cache.reg(), tmp.reg())); |
deferred->Branch(not_equal); |
- __ mov(tmp.reg(), FieldOperand(cache.reg(), |
- tmp.reg(), // as smi |
- times_half_pointer_size, |
- kPointerSize + FixedArray::kHeaderSize)); |
+ __ mov(tmp.reg(), ArrayElement(cache.reg(), tmp.reg(), 1)); |
deferred->BindExit(); |
frame_->Push(&tmp); |