static const int kJSObjectHeaderSize = 3 * kApiPointerSize;
static const int kFixedArrayHeaderSize = 2 * kApiPointerSize;
static const int kContextHeaderSize = 2 * kApiPointerSize;
- static const int kContextEmbedderDataIndex = 87;
+ static const int kContextEmbedderDataIndex = 86;
static const int kFullStringRepresentationMask = 0x07;
static const int kStringEncodingMask = 0x4;
static const int kExternalTwoByteRepresentationTag = 0x02;
V(kIndexIsTooLarge, "Index is too large") \
V(kInlinedRuntimeFunctionFastOneByteArrayJoin, \
"Inlined runtime function: FastOneByteArrayJoin") \
- V(kInlinedRuntimeFunctionGetFromCache, \
- "Inlined runtime function: GetFromCache") \
V(kInliningBailedOut, "Inlining bailed out") \
V(kInputGPRIsExpectedToHaveUpper32Cleared, \
"Input GPR is expected to have upper32 cleared") \
bool InstallExtraNatives();
void InstallBuiltinFunctionIds();
void InstallExperimentalBuiltinFunctionIds();
- void InstallJSFunctionResultCaches();
void InitializeNormalizedMapCaches();
enum ExtensionTraversalState {
#undef INSTALL_BUILTIN_ID
-// Do not forget to update macros.py with named constant
-// of cache id.
-#define JSFUNCTION_RESULT_CACHE_LIST(F) \
- F(16, native_context()->regexp_function())
-
-
-static FixedArray* CreateCache(int size, Handle<JSFunction> factory_function) {
- Factory* factory = factory_function->GetIsolate()->factory();
- // Caches are supposed to live for a long time, allocate in old space.
- int array_size = JSFunctionResultCache::kEntriesIndex + 2 * size;
- // Cannot use cast as object is not fully initialized yet.
- JSFunctionResultCache* cache = reinterpret_cast<JSFunctionResultCache*>(
- *factory->NewFixedArrayWithHoles(array_size, TENURED));
- cache->set(JSFunctionResultCache::kFactoryIndex, *factory_function);
- cache->MakeZeroSize();
- return cache;
-}
-
-
-void Genesis::InstallJSFunctionResultCaches() {
- const int kNumberOfCaches = 0 +
-#define F(size, func) + 1
- JSFUNCTION_RESULT_CACHE_LIST(F)
-#undef F
- ;
-
- Handle<FixedArray> caches =
- factory()->NewFixedArray(kNumberOfCaches, TENURED);
-
- int index = 0;
-
-#define F(size, func) do { \
- FixedArray* cache = CreateCache((size), Handle<JSFunction>(func)); \
- caches->set(index++, cache); \
- } while (false)
-
- JSFUNCTION_RESULT_CACHE_LIST(F);
-
-#undef F
-
- native_context()->set_jsfunction_result_caches(*caches);
-}
-
-
void Genesis::InitializeNormalizedMapCaches() {
Handle<NormalizedMapCache> cache = NormalizedMapCache::New(isolate());
native_context()->set_normalized_map_cache(*cache);
CreateNewGlobals(global_proxy_template, global_proxy);
HookUpGlobalProxy(global_object, global_proxy);
InitializeGlobal(global_object, empty_function, context_type);
- InstallJSFunctionResultCaches();
InitializeNormalizedMapCaches();
if (!InstallNatives(context_type)) return;
V(GET_STACK_TRACE_LINE_INDEX, JSFunction, get_stack_trace_line_fun) \
V(CONFIGURE_GLOBAL_INDEX, JSFunction, configure_global_fun) \
V(FUNCTION_CACHE_INDEX, ObjectHashTable, function_cache) \
- V(JSFUNCTION_RESULT_CACHES_INDEX, FixedArray, jsfunction_result_caches) \
V(NORMALIZED_MAP_CACHE_INDEX, Object, normalized_map_cache) \
V(RUNTIME_CONTEXT_INDEX, Context, runtime_context) \
V(CALL_AS_FUNCTION_DELEGATE_INDEX, JSFunction, call_as_function_delegate) \
GET_STACK_TRACE_LINE_INDEX,
CONFIGURE_GLOBAL_INDEX,
FUNCTION_CACHE_INDEX,
- JSFUNCTION_RESULT_CACHES_INDEX,
NORMALIZED_MAP_CACHE_INDEX,
RUNTIME_CONTEXT_INDEX,
CALL_AS_FUNCTION_DELEGATE_INDEX,
}
-void FullCodeGenerator::EmitGetFromCache(CallRuntime* expr) {
- ZoneList<Expression*>* args = expr->arguments();
- DCHECK_EQ(2, args->length());
- DCHECK_NOT_NULL(args->at(0)->AsLiteral());
- int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->value()))->value();
-
- Handle<FixedArray> jsfunction_result_caches(
- isolate()->native_context()->jsfunction_result_caches());
- if (jsfunction_result_caches->length() <= cache_id) {
- __ Abort(kAttemptToUseUndefinedCache);
- __ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
- context()->Plug(r0);
- return;
- }
-
- VisitForAccumulatorValue(args->at(1));
-
- Register key = r0;
- Register cache = r1;
- __ ldr(cache, ContextOperand(cp, Context::GLOBAL_OBJECT_INDEX));
- __ ldr(cache, FieldMemOperand(cache, GlobalObject::kNativeContextOffset));
- __ ldr(cache, ContextOperand(cache, Context::JSFUNCTION_RESULT_CACHES_INDEX));
- __ ldr(cache,
- FieldMemOperand(cache, FixedArray::OffsetOfElementAt(cache_id)));
-
-
- Label done, not_found;
- __ ldr(r2, FieldMemOperand(cache, JSFunctionResultCache::kFingerOffset));
- // r2 now holds finger offset as a smi.
- __ add(r3, cache, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
- // r3 now points to the start of fixed array elements.
- __ ldr(r2, MemOperand::PointerAddressFromSmiKey(r3, r2, PreIndex));
- // Note side effect of PreIndex: r3 now points to the key of the pair.
- __ cmp(key, r2);
- __ b(ne, ¬_found);
-
- __ ldr(r0, MemOperand(r3, kPointerSize));
- __ b(&done);
-
- __ bind(¬_found);
- // Call runtime to perform the lookup.
- __ Push(cache, key);
- __ CallRuntime(Runtime::kGetFromCacheRT, 2);
-
- __ bind(&done);
- context()->Plug(r0);
-}
-
-
void FullCodeGenerator::EmitHasCachedArrayIndex(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
VisitForAccumulatorValue(args->at(0));
}
-void FullCodeGenerator::EmitGetFromCache(CallRuntime* expr) {
- ZoneList<Expression*>* args = expr->arguments();
- DCHECK_EQ(2, args->length());
- DCHECK_NOT_NULL(args->at(0)->AsLiteral());
- int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->value()))->value();
-
- Handle<FixedArray> jsfunction_result_caches(
- isolate()->native_context()->jsfunction_result_caches());
- if (jsfunction_result_caches->length() <= cache_id) {
- __ Abort(kAttemptToUseUndefinedCache);
- __ LoadRoot(x0, Heap::kUndefinedValueRootIndex);
- context()->Plug(x0);
- return;
- }
-
- VisitForAccumulatorValue(args->at(1));
-
- Register key = x0;
- Register cache = x1;
- __ Ldr(cache, GlobalObjectMemOperand());
- __ Ldr(cache, FieldMemOperand(cache, GlobalObject::kNativeContextOffset));
- __ Ldr(cache, ContextMemOperand(cache,
- Context::JSFUNCTION_RESULT_CACHES_INDEX));
- __ Ldr(cache,
- FieldMemOperand(cache, FixedArray::OffsetOfElementAt(cache_id)));
-
- Label done;
- __ Ldrsw(x2, UntagSmiFieldMemOperand(cache,
- JSFunctionResultCache::kFingerOffset));
- __ Add(x3, cache, FixedArray::kHeaderSize - kHeapObjectTag);
- __ Add(x3, x3, Operand(x2, LSL, kPointerSizeLog2));
-
- // Load the key and data from the cache.
- __ Ldp(x2, x3, MemOperand(x3));
-
- __ Cmp(key, x2);
- __ CmovX(x0, x3, eq);
- __ B(eq, &done);
-
- // Call runtime to perform the lookup.
- __ Push(cache, key);
- __ CallRuntime(Runtime::kGetFromCacheRT, 2);
-
- __ Bind(&done);
- context()->Plug(x0);
-}
-
-
void FullCodeGenerator::EmitHasCachedArrayIndex(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
VisitForAccumulatorValue(args->at(0));
F(StringCompare) \
F(RegExpExec) \
F(RegExpConstructResult) \
- F(GetFromCache) \
F(NumberToString) \
F(ToObject) \
F(DebugIsActive)
}
-void FullCodeGenerator::EmitGetFromCache(CallRuntime* expr) {
- ZoneList<Expression*>* args = expr->arguments();
- DCHECK_EQ(2, args->length());
-
- DCHECK_NOT_NULL(args->at(0)->AsLiteral());
- int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->value()))->value();
-
- Handle<FixedArray> jsfunction_result_caches(
- isolate()->native_context()->jsfunction_result_caches());
- if (jsfunction_result_caches->length() <= cache_id) {
- __ Abort(kAttemptToUseUndefinedCache);
- __ mov(eax, isolate()->factory()->undefined_value());
- context()->Plug(eax);
- return;
- }
-
- VisitForAccumulatorValue(args->at(1));
-
- Register key = eax;
- Register cache = ebx;
- Register tmp = ecx;
- __ mov(cache, ContextOperand(esi, Context::GLOBAL_OBJECT_INDEX));
- __ mov(cache,
- FieldOperand(cache, GlobalObject::kNativeContextOffset));
- __ mov(cache, ContextOperand(cache, Context::JSFUNCTION_RESULT_CACHES_INDEX));
- __ mov(cache,
- FieldOperand(cache, FixedArray::OffsetOfElementAt(cache_id)));
-
- Label done, not_found;
- STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
- __ mov(tmp, FieldOperand(cache, JSFunctionResultCache::kFingerOffset));
- // tmp now holds finger offset as a smi.
- __ cmp(key, FixedArrayElementOperand(cache, tmp));
- __ j(not_equal, ¬_found);
-
- __ mov(eax, FixedArrayElementOperand(cache, tmp, 1));
- __ jmp(&done);
-
- __ bind(¬_found);
- // Call runtime to perform the lookup.
- __ push(cache);
- __ push(key);
- __ CallRuntime(Runtime::kGetFromCacheRT, 2);
-
- __ bind(&done);
- context()->Plug(eax);
-}
-
-
void FullCodeGenerator::EmitHasCachedArrayIndex(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
DCHECK(args->length() == 1);
}
-void FullCodeGenerator::EmitGetFromCache(CallRuntime* expr) {
- ZoneList<Expression*>* args = expr->arguments();
- DCHECK_EQ(2, args->length());
-
- DCHECK_NOT_NULL(args->at(0)->AsLiteral());
- int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->value()))->value();
-
- Handle<FixedArray> jsfunction_result_caches(
- isolate()->native_context()->jsfunction_result_caches());
- if (jsfunction_result_caches->length() <= cache_id) {
- __ Abort(kAttemptToUseUndefinedCache);
- __ LoadRoot(v0, Heap::kUndefinedValueRootIndex);
- context()->Plug(v0);
- return;
- }
-
- VisitForAccumulatorValue(args->at(1));
-
- Register key = v0;
- Register cache = a1;
- __ lw(cache, ContextOperand(cp, Context::GLOBAL_OBJECT_INDEX));
- __ lw(cache, FieldMemOperand(cache, GlobalObject::kNativeContextOffset));
- __ lw(cache,
- ContextOperand(
- cache, Context::JSFUNCTION_RESULT_CACHES_INDEX));
- __ lw(cache,
- FieldMemOperand(cache, FixedArray::OffsetOfElementAt(cache_id)));
-
-
- Label done, not_found;
- STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
- __ lw(a2, FieldMemOperand(cache, JSFunctionResultCache::kFingerOffset));
- // a2 now holds finger offset as a smi.
- __ Addu(a3, cache, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
- // a3 now points to the start of fixed array elements.
- __ sll(at, a2, kPointerSizeLog2 - kSmiTagSize);
- __ addu(a3, a3, at);
- // a3 now points to key of indexed element of cache.
- __ lw(a2, MemOperand(a3));
- __ Branch(¬_found, ne, key, Operand(a2));
-
- __ lw(v0, MemOperand(a3, kPointerSize));
- __ Branch(&done);
-
- __ bind(¬_found);
- // Call runtime to perform the lookup.
- __ Push(cache, key);
- __ CallRuntime(Runtime::kGetFromCacheRT, 2);
-
- __ bind(&done);
- context()->Plug(v0);
-}
-
-
void FullCodeGenerator::EmitHasCachedArrayIndex(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
VisitForAccumulatorValue(args->at(0));
}
-void FullCodeGenerator::EmitGetFromCache(CallRuntime* expr) {
- ZoneList<Expression*>* args = expr->arguments();
- DCHECK_EQ(2, args->length());
-
- DCHECK_NOT_NULL(args->at(0)->AsLiteral());
- int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->value()))->value();
-
- Handle<FixedArray> jsfunction_result_caches(
- isolate()->native_context()->jsfunction_result_caches());
- if (jsfunction_result_caches->length() <= cache_id) {
- __ Abort(kAttemptToUseUndefinedCache);
- __ LoadRoot(v0, Heap::kUndefinedValueRootIndex);
- context()->Plug(v0);
- return;
- }
-
- VisitForAccumulatorValue(args->at(1));
-
- Register key = v0;
- Register cache = a1;
- __ ld(cache, ContextOperand(cp, Context::GLOBAL_OBJECT_INDEX));
- __ ld(cache, FieldMemOperand(cache, GlobalObject::kNativeContextOffset));
- __ ld(cache,
- ContextOperand(
- cache, Context::JSFUNCTION_RESULT_CACHES_INDEX));
- __ ld(cache,
- FieldMemOperand(cache, FixedArray::OffsetOfElementAt(cache_id)));
-
-
- Label done, not_found;
- STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
- __ ld(a2, FieldMemOperand(cache, JSFunctionResultCache::kFingerOffset));
- // a2 now holds finger offset as a smi.
- __ Daddu(a3, cache, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
- // a3 now points to the start of fixed array elements.
- __ SmiScale(at, a2, kPointerSizeLog2);
- __ daddu(a3, a3, at);
- // a3 now points to key of indexed element of cache.
- __ ld(a2, MemOperand(a3));
- __ Branch(¬_found, ne, key, Operand(a2));
-
- __ ld(v0, MemOperand(a3, kPointerSize));
- __ Branch(&done);
-
- __ bind(¬_found);
- // Call runtime to perform the lookup.
- __ Push(cache, key);
- __ CallRuntime(Runtime::kGetFromCacheRT, 2);
-
- __ bind(&done);
- context()->Plug(v0);
-}
-
-
void FullCodeGenerator::EmitHasCachedArrayIndex(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
VisitForAccumulatorValue(args->at(0));
}
-void FullCodeGenerator::EmitGetFromCache(CallRuntime* expr) {
- ZoneList<Expression*>* args = expr->arguments();
- DCHECK_EQ(2, args->length());
- DCHECK_NOT_NULL(args->at(0)->AsLiteral());
- int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->value()))->value();
-
- Handle<FixedArray> jsfunction_result_caches(
- isolate()->native_context()->jsfunction_result_caches());
- if (jsfunction_result_caches->length() <= cache_id) {
- __ Abort(kAttemptToUseUndefinedCache);
- __ LoadRoot(r3, Heap::kUndefinedValueRootIndex);
- context()->Plug(r3);
- return;
- }
-
- VisitForAccumulatorValue(args->at(1));
-
- Register key = r3;
- Register cache = r4;
- __ LoadP(cache, ContextOperand(cp, Context::GLOBAL_OBJECT_INDEX));
- __ LoadP(cache, FieldMemOperand(cache, GlobalObject::kNativeContextOffset));
- __ LoadP(cache,
- ContextOperand(cache, Context::JSFUNCTION_RESULT_CACHES_INDEX));
- __ LoadP(cache,
- FieldMemOperand(cache, FixedArray::OffsetOfElementAt(cache_id)), r0);
-
- Label done, not_found;
- __ LoadP(r5, FieldMemOperand(cache, JSFunctionResultCache::kFingerOffset));
- // r5 now holds finger offset as a smi.
- __ addi(r6, cache, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
- // r6 now points to the start of fixed array elements.
- __ SmiToPtrArrayOffset(r5, r5);
- __ LoadPUX(r5, MemOperand(r6, r5));
- // r6 now points to the key of the pair.
- __ cmp(key, r5);
- __ bne(¬_found);
-
- __ LoadP(r3, MemOperand(r6, kPointerSize));
- __ b(&done);
-
- __ bind(¬_found);
- // Call runtime to perform the lookup.
- __ Push(cache, key);
- __ CallRuntime(Runtime::kGetFromCacheRT, 2);
-
- __ bind(&done);
- context()->Plug(r3);
-}
-
-
void FullCodeGenerator::EmitHasCachedArrayIndex(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
VisitForAccumulatorValue(args->at(0));
}
-void FullCodeGenerator::EmitGetFromCache(CallRuntime* expr) {
- ZoneList<Expression*>* args = expr->arguments();
- DCHECK_EQ(2, args->length());
-
- DCHECK_NOT_NULL(args->at(0)->AsLiteral());
- int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->value()))->value();
-
- Handle<FixedArray> jsfunction_result_caches(
- isolate()->native_context()->jsfunction_result_caches());
- if (jsfunction_result_caches->length() <= cache_id) {
- __ Abort(kAttemptToUseUndefinedCache);
- __ LoadRoot(rax, Heap::kUndefinedValueRootIndex);
- context()->Plug(rax);
- return;
- }
-
- VisitForAccumulatorValue(args->at(1));
-
- Register key = rax;
- Register cache = rbx;
- Register tmp = rcx;
- __ movp(cache, ContextOperand(rsi, Context::GLOBAL_OBJECT_INDEX));
- __ movp(cache,
- FieldOperand(cache, GlobalObject::kNativeContextOffset));
- __ movp(cache,
- ContextOperand(cache, Context::JSFUNCTION_RESULT_CACHES_INDEX));
- __ movp(cache,
- FieldOperand(cache, FixedArray::OffsetOfElementAt(cache_id)));
-
- Label done, not_found;
- STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
- __ movp(tmp, FieldOperand(cache, JSFunctionResultCache::kFingerOffset));
- // tmp now holds finger offset as a smi.
- SmiIndex index =
- __ SmiToIndex(kScratchRegister, tmp, kPointerSizeLog2);
- __ cmpp(key, FieldOperand(cache,
- index.reg,
- index.scale,
- FixedArray::kHeaderSize));
- __ j(not_equal, ¬_found, Label::kNear);
- __ movp(rax, FieldOperand(cache,
- index.reg,
- index.scale,
- FixedArray::kHeaderSize + kPointerSize));
- __ jmp(&done, Label::kNear);
-
- __ bind(¬_found);
- // Call runtime to perform the lookup.
- __ Push(cache);
- __ Push(key);
- __ CallRuntime(Runtime::kGetFromCacheRT, 2);
-
- __ bind(&done);
- context()->Plug(rax);
-}
-
-
void FullCodeGenerator::EmitHasCachedArrayIndex(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
DCHECK(args->length() == 1);
}
-void FullCodeGenerator::EmitGetFromCache(CallRuntime* expr) {
- ZoneList<Expression*>* args = expr->arguments();
- DCHECK_EQ(2, args->length());
-
- DCHECK_NOT_NULL(args->at(0)->AsLiteral());
- int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->value()))->value();
-
- Handle<FixedArray> jsfunction_result_caches(
- isolate()->native_context()->jsfunction_result_caches());
- if (jsfunction_result_caches->length() <= cache_id) {
- __ Abort(kAttemptToUseUndefinedCache);
- __ mov(eax, isolate()->factory()->undefined_value());
- context()->Plug(eax);
- return;
- }
-
- VisitForAccumulatorValue(args->at(1));
-
- Register key = eax;
- Register cache = ebx;
- Register tmp = ecx;
- __ mov(cache, ContextOperand(esi, Context::GLOBAL_OBJECT_INDEX));
- __ mov(cache,
- FieldOperand(cache, GlobalObject::kNativeContextOffset));
- __ mov(cache, ContextOperand(cache, Context::JSFUNCTION_RESULT_CACHES_INDEX));
- __ mov(cache,
- FieldOperand(cache, FixedArray::OffsetOfElementAt(cache_id)));
-
- Label done, not_found;
- STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
- __ mov(tmp, FieldOperand(cache, JSFunctionResultCache::kFingerOffset));
- // tmp now holds finger offset as a smi.
- __ cmp(key, FixedArrayElementOperand(cache, tmp));
- __ j(not_equal, ¬_found);
-
- __ mov(eax, FixedArrayElementOperand(cache, tmp, 1));
- __ jmp(&done);
-
- __ bind(¬_found);
- // Call runtime to perform the lookup.
- __ push(cache);
- __ push(key);
- __ CallRuntime(Runtime::kGetFromCacheRT, 2);
-
- __ bind(&done);
- context()->Plug(eax);
-}
-
-
void FullCodeGenerator::EmitHasCachedArrayIndex(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
DCHECK(args->length() == 1);
EXTRACT_CONTEXT_FIELD(EXTENSION_INDEX, Object, extension);
EXTRACT_CONTEXT_FIELD(GLOBAL_OBJECT_INDEX, GlobalObject, global);
if (context->IsNativeContext()) {
- TagObject(context->jsfunction_result_caches(),
- "(context func. result caches)");
TagObject(context->normalized_map_cache(), "(context norm. map cache)");
TagObject(context->runtime_context(), "(runtime context)");
TagObject(context->embedder_data(), "(context data)");
void Heap::GarbageCollectionPrologue() {
{
AllowHeapAllocation for_the_first_part_of_prologue;
- ClearJSFunctionResultCaches();
gc_count_++;
unflattened_strings_length_ = 0;
}
-void Heap::ClearJSFunctionResultCaches() {
- if (isolate_->bootstrapper()->IsActive()) return;
-
- Object* context = native_contexts_list();
- while (!context->IsUndefined()) {
- // Get the caches for this context. GC can happen when the context
- // is not fully initialized, so the caches can be undefined.
- Object* caches_or_undefined =
- Context::cast(context)->get(Context::JSFUNCTION_RESULT_CACHES_INDEX);
- if (!caches_or_undefined->IsUndefined()) {
- FixedArray* caches = FixedArray::cast(caches_or_undefined);
- // Clear the caches:
- int length = caches->length();
- for (int i = 0; i < length; i++) {
- JSFunctionResultCache::cast(caches->get(i))->Clear();
- }
- }
- // Get the next context:
- context = Context::cast(context)->get(Context::NEXT_CONTEXT_LINK);
- }
-}
-
-
void Heap::ClearNormalizedMapCaches() {
if (isolate_->bootstrapper()->IsActive() &&
!incremental_marking()->IsMarking()) {
// scavenge operation.
inline bool ShouldBePromoted(Address old_address, int object_size);
- void ClearJSFunctionResultCaches();
-
void ClearNormalizedMapCaches();
GCTracer* tracer() { return &tracer_; }
}
-// Support for fast native caches.
-void HOptimizedGraphBuilder::GenerateGetFromCache(CallRuntime* call) {
- return Bailout(kInlinedRuntimeFunctionGetFromCache);
-}
-
-
// Fast support for number to string.
void HOptimizedGraphBuilder::GenerateNumberToString(CallRuntime* call) {
DCHECK_EQ(1, call->arguments()->length());
F(StringCompare) \
F(RegExpExec) \
F(RegExpConstructResult) \
- F(GetFromCache) \
F(NumberToString) \
F(DebugIsActive) \
F(Likely) \
}
-void JSFunctionResultCache::JSFunctionResultCacheVerify() {
- JSFunction::cast(get(kFactoryIndex))->ObjectVerify();
-
- int size = Smi::cast(get(kCacheSizeIndex))->value();
- CHECK(kEntriesIndex <= size);
- CHECK(size <= length());
- CHECK_EQ(0, size % kEntrySize);
-
- int finger = Smi::cast(get(kFingerIndex))->value();
- CHECK(kEntriesIndex <= finger);
- CHECK((finger < size) || (finger == kEntriesIndex && finger == size));
- CHECK_EQ(0, finger % kEntrySize);
-
- if (FLAG_enable_slow_asserts) {
- for (int i = kEntriesIndex; i < size; i++) {
- CHECK(!get(i)->IsTheHole());
- get(i)->ObjectVerify();
- }
- for (int i = size; i < length(); i++) {
- CHECK(get(i)->IsTheHole());
- get(i)->ObjectVerify();
- }
- }
-}
-
-
void NormalizedMapCache::NormalizedMapCacheVerify() {
FixedArray::cast(this)->FixedArrayVerify();
if (FLAG_enable_slow_asserts) {
}
-bool Object::IsJSFunctionResultCache() const {
- if (!IsFixedArray()) return false;
- const FixedArray* self = FixedArray::cast(this);
- int length = self->length();
- if (length < JSFunctionResultCache::kEntriesIndex) return false;
- if ((length - JSFunctionResultCache::kEntriesIndex)
- % JSFunctionResultCache::kEntrySize != 0) {
- return false;
- }
-#ifdef VERIFY_HEAP
- if (FLAG_verify_heap) {
- // TODO(svenpanne) We use const_cast here and below to break our dependency
- // cycle between the predicates and the verifiers. This can be removed when
- // the verifiers are const-correct, too.
- reinterpret_cast<JSFunctionResultCache*>(const_cast<Object*>(this))->
- JSFunctionResultCacheVerify();
- }
-#endif
- return true;
-}
-
-
bool Object::IsNormalizedMapCache() const {
return NormalizedMapCache::IsNormalizedMapCache(this);
}
CAST_ACCESSOR(JSDate)
CAST_ACCESSOR(JSFunction)
CAST_ACCESSOR(JSFunctionProxy)
-CAST_ACCESSOR(JSFunctionResultCache)
CAST_ACCESSOR(JSGeneratorObject)
CAST_ACCESSOR(JSGlobalObject)
CAST_ACCESSOR(JSGlobalProxy)
}
-void JSFunctionResultCache::MakeZeroSize() {
- set_finger_index(kEntriesIndex);
- set_size(kEntriesIndex);
-}
-
-
-void JSFunctionResultCache::Clear() {
- int cache_size = size();
- Object** entries_start = RawFieldOfElementAt(kEntriesIndex);
- MemsetPointer(entries_start,
- GetHeap()->the_hole_value(),
- cache_size - kEntriesIndex);
- MakeZeroSize();
-}
-
-
-int JSFunctionResultCache::size() {
- return Smi::cast(get(kCacheSizeIndex))->value();
-}
-
-
-void JSFunctionResultCache::set_size(int size) {
- set(kCacheSizeIndex, Smi::FromInt(size));
-}
-
-
-int JSFunctionResultCache::finger_index() {
- return Smi::cast(get(kFingerIndex))->value();
-}
-
-
-void JSFunctionResultCache::set_finger_index(int finger_index) {
- set(kFingerIndex, Smi::FromInt(finger_index));
-}
-
-
byte ByteArray::get(int index) {
DCHECK(index >= 0 && index < this->length());
return READ_BYTE_FIELD(this, kHeaderSize + index * kCharSize);
// - OrderedHashMap
// - Context
// - TypeFeedbackVector
-// - JSFunctionResultCache
// - ScopeInfo
// - TransitionArray
// - ScriptContextTable
V(HashTable) \
V(Dictionary) \
V(StringTable) \
- V(JSFunctionResultCache) \
V(NormalizedMapCache) \
V(CompilationCacheTable) \
V(CodeCacheHashTable) \
};
-// JSFunctionResultCache caches results of some JSFunction invocation.
-// It is a fixed array with fixed structure:
-// [0]: factory function
-// [1]: finger index
-// [2]: current cache size
-// [3]: dummy field.
-// The rest of array are key/value pairs.
-class JSFunctionResultCache : public FixedArray {
- public:
- static const int kFactoryIndex = 0;
- static const int kFingerIndex = kFactoryIndex + 1;
- static const int kCacheSizeIndex = kFingerIndex + 1;
- static const int kDummyIndex = kCacheSizeIndex + 1;
- static const int kEntriesIndex = kDummyIndex + 1;
-
- static const int kEntrySize = 2; // key + value
-
- static const int kFactoryOffset = kHeaderSize;
- static const int kFingerOffset = kFactoryOffset + kPointerSize;
- static const int kCacheSizeOffset = kFingerOffset + kPointerSize;
-
- inline void MakeZeroSize();
- inline void Clear();
-
- inline int size();
- inline void set_size(int size);
- inline int finger_index();
- inline void set_finger_index(int finger_index);
-
- DECLARE_CAST(JSFunctionResultCache)
-
- DECLARE_VERIFIER(JSFunctionResultCache)
-};
-
-
// ScopeInfo represents information about different scopes of a source
// program and the allocation of the scope's variables. Scope information
// is stored in a compressed form in ScopeInfo objects and is used
}
-RUNTIME_FUNCTION(Runtime_GetFromCacheRT) {
- SealHandleScope shs(isolate);
- // This is only called from codegen, so checks might be more lax.
- CONVERT_ARG_CHECKED(JSFunctionResultCache, cache, 0);
- CONVERT_ARG_CHECKED(Object, key, 1);
-
- {
- DisallowHeapAllocation no_alloc;
-
- int finger_index = cache->finger_index();
- Object* o = cache->get(finger_index);
- if (o == key) {
- // The fastest case: hit the same place again.
- return cache->get(finger_index + 1);
- }
-
- for (int i = finger_index - 2; i >= JSFunctionResultCache::kEntriesIndex;
- i -= 2) {
- o = cache->get(i);
- if (o == key) {
- cache->set_finger_index(i);
- return cache->get(i + 1);
- }
- }
-
- int size = cache->size();
- DCHECK(size <= cache->length());
-
- for (int i = size - 2; i > finger_index; i -= 2) {
- o = cache->get(i);
- if (o == key) {
- cache->set_finger_index(i);
- return cache->get(i + 1);
- }
- }
- }
-
- // There is no value in the cache. Invoke the function and cache result.
- HandleScope scope(isolate);
-
- Handle<JSFunctionResultCache> cache_handle(cache);
- Handle<Object> key_handle(key, isolate);
- Handle<Object> value;
- {
- Handle<JSFunction> factory(JSFunction::cast(
- cache_handle->get(JSFunctionResultCache::kFactoryIndex)));
- // TODO(antonm): consider passing a receiver when constructing a cache.
- Handle<JSObject> receiver(isolate->global_proxy());
- // This handle is nor shared, nor used later, so it's safe.
- Handle<Object> argv[] = {key_handle};
- ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
- isolate, value,
- Execution::Call(isolate, factory, receiver, arraysize(argv), argv));
- }
-
-#ifdef VERIFY_HEAP
- if (FLAG_verify_heap) {
- cache_handle->JSFunctionResultCacheVerify();
- }
-#endif
-
- // Function invocation may have cleared the cache. Reread all the data.
- int finger_index = cache_handle->finger_index();
- int size = cache_handle->size();
-
- // If we have spare room, put new data into it, otherwise evict post finger
- // entry which is likely to be the least recently used.
- int index = -1;
- if (size < cache_handle->length()) {
- cache_handle->set_size(size + JSFunctionResultCache::kEntrySize);
- index = size;
- } else {
- index = finger_index + JSFunctionResultCache::kEntrySize;
- if (index == cache_handle->length()) {
- index = JSFunctionResultCache::kEntriesIndex;
- }
- }
-
- DCHECK(index % 2 == 0);
- DCHECK(index >= JSFunctionResultCache::kEntriesIndex);
- DCHECK(index < cache_handle->length());
-
- cache_handle->set(index, *key_handle);
- cache_handle->set(index + 1, *value);
- cache_handle->set_finger_index(index);
-
-#ifdef VERIFY_HEAP
- if (FLAG_verify_heap) {
- cache_handle->JSFunctionResultCacheVerify();
- }
-#endif
-
- return *value;
-}
-
-
RUNTIME_FUNCTION(Runtime_MessageGetStartPosition) {
SealHandleScope shs(isolate);
DCHECK(args.length() == 1);
}
-RUNTIME_FUNCTION(Runtime_GetFromCache) {
- HandleScope scope(isolate);
- DCHECK(args.length() == 2);
- CONVERT_SMI_ARG_CHECKED(id, 0);
- args[0] = isolate->native_context()->jsfunction_result_caches()->get(id);
- return __RT_impl_Runtime_GetFromCacheRT(args, isolate);
-}
-
-
RUNTIME_FUNCTION(Runtime_IncrementStatsCounter) {
SealHandleScope shs(isolate);
DCHECK(args.length() == 1);
F(AllocateInTargetSpace, 2, 1) \
F(CollectStackTrace, 2, 1) \
F(RenderCallSite, 0, 1) \
- F(GetFromCacheRT, 2, 1) \
F(MessageGetStartPosition, 1, 1) \
F(MessageGetScript, 1, 1) \
F(FormatMessageString, 4, 1) \
F(CallSiteIsEvalRT, 3, 1) \
F(CallSiteIsConstructorRT, 3, 1) \
F(IS_VAR, 1, 1) \
- F(GetFromCache, 2, 1) \
F(IncrementStatsCounter, 1, 1) \
F(Likely, 1, 1) \
F(Unlikely, 1, 1) \
CHECK_OBJECT_COERCIBLE(this, "String.prototype.search");
var regexp;
- if (IS_STRING(re)) {
- regexp = %_GetFromCache(STRING_TO_REGEXP_CACHE_ID, re);
- } else if (IS_REGEXP(re)) {
+ if (IS_REGEXP(re)) {
regexp = re;
} else {
regexp = new GlobalRegExp(re);
}
callback_();
if (initialize_) {
- EmptyMessageQueues(isolate_);
+ if (v8::Locker::IsActive()) {
+ v8::Locker locker(isolate_);
+ EmptyMessageQueues(isolate_);
+ } else {
+ EmptyMessageQueues(isolate_);
+ }
isolate_->Exit();
}
}
THREADED_TEST(GCFromWeakCallbacks) {
v8::Isolate* isolate = CcTest::isolate();
+ v8::Locker locker(CcTest::isolate());
v8::HandleScope scope(isolate);
v8::Handle<Context> context = Context::New(isolate);
Context::Scope context_scope(context);
THREADED_TEST(NoGlobalHandlesOrphaningDueToWeakCallback) {
+ v8::Locker locker(CcTest::isolate());
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
}
-THREADED_TEST(AddToJSFunctionResultCache) {
- i::FLAG_stress_compaction = false;
- i::FLAG_allow_natives_syntax = true;
- v8::HandleScope scope(CcTest::isolate());
-
- LocalContext context;
-
- const char* code =
- "(function() {"
- " var key0 = 'a';"
- " var key1 = 'b';"
- " var r0 = %_GetFromCache(0, key0);"
- " var r1 = %_GetFromCache(0, key1);"
- " var r0_ = %_GetFromCache(0, key0);"
- " if (r0 !== r0_)"
- " return 'Different results for ' + key0 + ': ' + r0 + ' vs. ' + r0_;"
- " var r1_ = %_GetFromCache(0, key1);"
- " if (r1 !== r1_)"
- " return 'Different results for ' + key1 + ': ' + r1 + ' vs. ' + r1_;"
- " return 'PASSED';"
- "})()";
- CcTest::heap()->ClearJSFunctionResultCaches();
- ExpectString(code, "PASSED");
-}
-
-
-THREADED_TEST(FillJSFunctionResultCache) {
- i::FLAG_allow_natives_syntax = true;
- LocalContext context;
- v8::HandleScope scope(context->GetIsolate());
-
- const char* code =
- "(function() {"
- " var k = 'a';"
- " var r = %_GetFromCache(0, k);"
- " for (var i = 0; i < 16; i++) {"
- " %_GetFromCache(0, 'a' + i);"
- " };"
- " if (r === %_GetFromCache(0, k))"
- " return 'FAILED: k0CacheSize is too small';"
- " return 'PASSED';"
- "})()";
- CcTest::heap()->ClearJSFunctionResultCaches();
- ExpectString(code, "PASSED");
-}
-
-
-THREADED_TEST(RoundRobinGetFromCache) {
- i::FLAG_allow_natives_syntax = true;
- LocalContext context;
- v8::HandleScope scope(context->GetIsolate());
-
- const char* code =
- "(function() {"
- " var keys = [];"
- " for (var i = 0; i < 16; i++) keys.push(i);"
- " var values = [];"
- " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);"
- " for (var i = 0; i < 16; i++) {"
- " var v = %_GetFromCache(0, keys[i]);"
- " if (v.toString() !== values[i].toString())"
- " return 'Wrong value for ' + "
- " keys[i] + ': ' + v + ' vs. ' + values[i];"
- " };"
- " return 'PASSED';"
- "})()";
- CcTest::heap()->ClearJSFunctionResultCaches();
- ExpectString(code, "PASSED");
-}
-
-
-THREADED_TEST(ReverseGetFromCache) {
- i::FLAG_allow_natives_syntax = true;
- LocalContext context;
- v8::HandleScope scope(context->GetIsolate());
-
- const char* code =
- "(function() {"
- " var keys = [];"
- " for (var i = 0; i < 16; i++) keys.push(i);"
- " var values = [];"
- " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);"
- " for (var i = 15; i >= 16; i--) {"
- " var v = %_GetFromCache(0, keys[i]);"
- " if (v !== values[i])"
- " return 'Wrong value for ' + "
- " keys[i] + ': ' + v + ' vs. ' + values[i];"
- " };"
- " return 'PASSED';"
- "})()";
- CcTest::heap()->ClearJSFunctionResultCaches();
- ExpectString(code, "PASSED");
-}
-
-
-THREADED_TEST(TestEviction) {
- i::FLAG_allow_natives_syntax = true;
- LocalContext context;
- v8::HandleScope scope(context->GetIsolate());
-
- const char* code =
- "(function() {"
- " for (var i = 0; i < 2*16; i++) {"
- " %_GetFromCache(0, 'a' + i);"
- " };"
- " return 'PASSED';"
- "})()";
- CcTest::heap()->ClearJSFunctionResultCaches();
- ExpectString(code, "PASSED");
-}
-
-
THREADED_TEST(TwoByteStringInOneByteCons) {
// See Chromium issue 47824.
LocalContext context;
#include "src/isolate.h"
-enum Turn { FILL_CACHE, CLEAN_CACHE, SECOND_TIME_FILL_CACHE, CACHE_DONE };
-
-static Turn turn = FILL_CACHE;
-
-
-class ThreadA : public v8::base::Thread {
- public:
- ThreadA() : Thread(Options("ThreadA")) {}
- void Run() {
- v8::Isolate* isolate = CcTest::isolate();
- v8::Locker locker(isolate);
- v8::Isolate::Scope isolate_scope(isolate);
- v8::HandleScope scope(isolate);
- v8::Handle<v8::Context> context = v8::Context::New(isolate);
- v8::Context::Scope context_scope(context);
-
- CHECK_EQ(FILL_CACHE, turn);
-
- // Fill String.search cache.
- v8::Handle<v8::Script> script = v8::Script::Compile(
- v8::String::NewFromUtf8(
- isolate,
- "for (var i = 0; i < 3; i++) {"
- " var result = \"a\".search(\"a\");"
- " if (result != 0) throw \"result: \" + result + \" @\" + i;"
- "};"
- "true"));
- CHECK(script->Run()->IsTrue());
-
- turn = CLEAN_CACHE;
- do {
- {
- v8::Unlocker unlocker(CcTest::isolate());
- }
- } while (turn != SECOND_TIME_FILL_CACHE);
-
- // Rerun the script.
- CHECK(script->Run()->IsTrue());
-
- turn = CACHE_DONE;
- }
-};
-
-
-class ThreadB : public v8::base::Thread {
- public:
- ThreadB() : Thread(Options("ThreadB")) {}
- void Run() {
- do {
- {
- v8::Isolate* isolate = CcTest::isolate();
- v8::Locker locker(isolate);
- v8::Isolate::Scope isolate_scope(isolate);
- if (turn == CLEAN_CACHE) {
- v8::HandleScope scope(isolate);
- v8::Handle<v8::Context> context = v8::Context::New(isolate);
- v8::Context::Scope context_scope(context);
-
- // Clear the caches by forcing major GC.
- CcTest::heap()->CollectAllGarbage();
- turn = SECOND_TIME_FILL_CACHE;
- break;
- }
- }
- } while (true);
- }
-};
-
-
-TEST(JSFunctionResultCachesInTwoThreads) {
- ThreadA threadA;
- ThreadB threadB;
-
- threadA.Start();
- threadB.Start();
-
- threadA.Join();
- threadB.Join();
-
- CHECK_EQ(CACHE_DONE, turn);
-}
-
class ThreadIdValidationThread : public v8::base::Thread {
public:
ThreadIdValidationThread(v8::base::Thread* thread_to_start,