From e05c04e5fa9af5717f6608e332e8bf536516d757 Mon Sep 17 00:00:00 2001 From: "kmillikin@chromium.org" Date: Tue, 25 Oct 2011 09:24:49 +0000 Subject: [PATCH] Handlify CallStubCompiler::CompileCallField. This function relies on a number of helpers for checking prototypes and probing dictionaries. It is not possible to wrap these helpers to retry after allocation failure in a safe way---the assembler has no way to undo what it has already assembled. These functions have all been duplicated with handle and raw versions. The raw versions will eventually be removed completely. R=ulan@chromium.org,vegorov@chromium.org BUG= TEST= Review URL: http://codereview.chromium.org/8332003 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@9769 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/code-stubs-arm.cc | 77 +++++++- src/arm/code-stubs-arm.h | 12 +- src/arm/stub-cache-arm.cc | 359 +++++++++++++++++++++++++++------- src/ia32/code-stubs-ia32.cc | 62 +++++- src/ia32/code-stubs-ia32.h | 11 +- src/ia32/stub-cache-ia32.cc | 375 ++++++++++++++++++++++++++++-------- src/objects.cc | 10 + src/objects.h | 5 + src/stub-cache.cc | 51 +++-- src/stub-cache.h | 58 ++++-- src/x64/code-stubs-x64.cc | 63 +++++- src/x64/code-stubs-x64.h | 11 +- src/x64/stub-cache-x64.cc | 375 +++++++++++++++++++++++++++++------- 13 files changed, 1212 insertions(+), 257 deletions(-) diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc index 355f4e1ce..412ba00fc 100644 --- a/src/arm/code-stubs-arm.cc +++ b/src/arm/code-stubs-arm.cc @@ -6677,7 +6677,82 @@ void DirectCEntryStub::GenerateCall(MacroAssembler* masm, } -MaybeObject* StringDictionaryLookupStub::GenerateNegativeLookup( +void StringDictionaryLookupStub::GenerateNegativeLookup(MacroAssembler* masm, + Label* miss, + Label* done, + Register receiver, + Register properties, + Handle name, + Register scratch0) { + // If names of slots in range from 1 to kProbes - 1 for the hash value are + // not equal to the name and kProbes-th slot is not used (its name is the + // undefined value), it guarantees the hash table doesn't contain the + // property. It's true even if some slots represent deleted properties + // (their names are the null value). + for (int i = 0; i < kInlinedProbes; i++) { + // scratch0 points to properties hash. + // Compute the masked index: (hash + i + i * i) & mask. + Register index = scratch0; + // Capacity is smi 2^n. + __ ldr(index, FieldMemOperand(properties, kCapacityOffset)); + __ sub(index, index, Operand(1)); + __ and_(index, index, Operand( + Smi::FromInt(name->Hash() + StringDictionary::GetProbeOffset(i)))); + + // Scale the index by multiplying by the entry size. + ASSERT(StringDictionary::kEntrySize == 3); + __ add(index, index, Operand(index, LSL, 1)); // index *= 3. + + Register entity_name = scratch0; + // Having undefined at this place means the name is not contained. + ASSERT_EQ(kSmiTagSize, 1); + Register tmp = properties; + __ add(tmp, properties, Operand(index, LSL, 1)); + __ ldr(entity_name, FieldMemOperand(tmp, kElementsStartOffset)); + + ASSERT(!tmp.is(entity_name)); + __ LoadRoot(tmp, Heap::kUndefinedValueRootIndex); + __ cmp(entity_name, tmp); + __ b(eq, done); + + if (i != kInlinedProbes - 1) { + // Stop if found the property. + __ cmp(entity_name, Operand(Handle(name))); + __ b(eq, miss); + + // Check if the entry name is not a symbol. + __ ldr(entity_name, FieldMemOperand(entity_name, HeapObject::kMapOffset)); + __ ldrb(entity_name, + FieldMemOperand(entity_name, Map::kInstanceTypeOffset)); + __ tst(entity_name, Operand(kIsSymbolMask)); + __ b(eq, miss); + + // Restore the properties. + __ ldr(properties, + FieldMemOperand(receiver, JSObject::kPropertiesOffset)); + } + } + + const int spill_mask = + (lr.bit() | r6.bit() | r5.bit() | r4.bit() | r3.bit() | + r2.bit() | r1.bit() | r0.bit()); + + __ stm(db_w, sp, spill_mask); + __ ldr(r0, FieldMemOperand(receiver, JSObject::kPropertiesOffset)); + __ mov(r1, Operand(Handle(name))); + StringDictionaryLookupStub stub(NEGATIVE_LOOKUP); + __ CallStub(&stub); + __ tst(r0, Operand(r0)); + __ ldm(ia_w, sp, spill_mask); + + __ b(eq, done); + __ b(ne, miss); +} + + +// TODO(kmillikin): Eliminate this function when the stub cache is fully +// handlified. +MaybeObject* StringDictionaryLookupStub::TryGenerateNegativeLookup( MacroAssembler* masm, Label* miss, Label* done, diff --git a/src/arm/code-stubs-arm.h b/src/arm/code-stubs-arm.h index 3ba75bab1..647fc8d15 100644 --- a/src/arm/code-stubs-arm.h +++ b/src/arm/code-stubs-arm.h @@ -799,7 +799,17 @@ class StringDictionaryLookupStub: public CodeStub { void Generate(MacroAssembler* masm); - MUST_USE_RESULT static MaybeObject* GenerateNegativeLookup( + static void GenerateNegativeLookup(MacroAssembler* masm, + Label* miss, + Label* done, + Register receiver, + Register properties, + Handle name, + Register scratch0); + + // TODO(kmillikin): Eliminate this function when the stub cache is fully + // handlified. + MUST_USE_RESULT static MaybeObject* TryGenerateNegativeLookup( MacroAssembler* masm, Label* miss, Label* done, diff --git a/src/arm/stub-cache-arm.cc b/src/arm/stub-cache-arm.cc index ce9eefada..545c06c1d 100644 --- a/src/arm/stub-cache-arm.cc +++ b/src/arm/stub-cache-arm.cc @@ -95,7 +95,63 @@ static void ProbeTable(Isolate* isolate, // must always call a backup property check that is complete. // This function is safe to call if the receiver has fast properties. // Name must be a symbol and receiver must be a heap object. -MUST_USE_RESULT static MaybeObject* GenerateDictionaryNegativeLookup( +static void GenerateDictionaryNegativeLookup(MacroAssembler* masm, + Label* miss_label, + Register receiver, + Handle name, + Register scratch0, + Register scratch1) { + ASSERT(name->IsSymbol()); + Counters* counters = masm->isolate()->counters(); + __ IncrementCounter(counters->negative_lookups(), 1, scratch0, scratch1); + __ IncrementCounter(counters->negative_lookups_miss(), 1, scratch0, scratch1); + + Label done; + + const int kInterceptorOrAccessCheckNeededMask = + (1 << Map::kHasNamedInterceptor) | (1 << Map::kIsAccessCheckNeeded); + + // Bail out if the receiver has a named interceptor or requires access checks. + Register map = scratch1; + __ ldr(map, FieldMemOperand(receiver, HeapObject::kMapOffset)); + __ ldrb(scratch0, FieldMemOperand(map, Map::kBitFieldOffset)); + __ tst(scratch0, Operand(kInterceptorOrAccessCheckNeededMask)); + __ b(ne, miss_label); + + // Check that receiver is a JSObject. + __ ldrb(scratch0, FieldMemOperand(map, Map::kInstanceTypeOffset)); + __ cmp(scratch0, Operand(FIRST_SPEC_OBJECT_TYPE)); + __ b(lt, miss_label); + + // Load properties array. + Register properties = scratch0; + __ ldr(properties, FieldMemOperand(receiver, JSObject::kPropertiesOffset)); + // Check that the properties array is a dictionary. + __ ldr(map, FieldMemOperand(properties, HeapObject::kMapOffset)); + Register tmp = properties; + __ LoadRoot(tmp, Heap::kHashTableMapRootIndex); + __ cmp(map, tmp); + __ b(ne, miss_label); + + // Restore the temporarily used register. + __ ldr(properties, FieldMemOperand(receiver, JSObject::kPropertiesOffset)); + + + StringDictionaryLookupStub::GenerateNegativeLookup(masm, + miss_label, + &done, + receiver, + properties, + name, + scratch1); + __ bind(&done); + __ DecrementCounter(counters->negative_lookups_miss(), 1, scratch0, scratch1); +} + + +// TODO(kmillikin): Eliminate this function when the stub cache is fully +// handlified. +MUST_USE_RESULT static MaybeObject* TryGenerateDictionaryNegativeLookup( MacroAssembler* masm, Label* miss_label, Register receiver, @@ -138,7 +194,7 @@ MUST_USE_RESULT static MaybeObject* GenerateDictionaryNegativeLookup( __ ldr(properties, FieldMemOperand(receiver, JSObject::kPropertiesOffset)); - MaybeObject* result = StringDictionaryLookupStub::GenerateNegativeLookup( + MaybeObject* result = StringDictionaryLookupStub::TryGenerateNegativeLookup( masm, miss_label, &done, @@ -259,8 +315,10 @@ void StubCompiler::GenerateDirectLoadGlobalFunctionPrototype( // are loaded directly otherwise the property is loaded from the properties // fixed array. void StubCompiler::GenerateFastPropertyLoad(MacroAssembler* masm, - Register dst, Register src, - JSObject* holder, int index) { + Register dst, + Register src, + Handle holder, + int index) { // Adjust for the number of properties stored in the holder. index -= holder->map()->inobject_properties(); if (index < 0) { @@ -480,7 +538,7 @@ void StubCompiler::GenerateLoadMiss(MacroAssembler* masm, Code::Kind kind) { static void GenerateCallFunction(MacroAssembler* masm, - Object* object, + Handle object, const ParameterCount& arguments, Label* miss, Code::ExtraICState extra_ic_state) { @@ -868,7 +926,26 @@ class CallInterceptorCompiler BASE_EMBEDDED { // Generate code to check that a global property cell is empty. Create // the property cell at compilation time if no cell exists for the // property. -MUST_USE_RESULT static MaybeObject* GenerateCheckPropertyCell( +static void GenerateCheckPropertyCell(MacroAssembler* masm, + Handle global, + Handle name, + Register scratch, + Label* miss) { + Handle cell = + GlobalObject::EnsurePropertyCell(global, name); + ASSERT(cell->value()->IsTheHole()); + __ mov(scratch, Operand(cell)); + __ ldr(scratch, + FieldMemOperand(scratch, JSGlobalPropertyCell::kValueOffset)); + __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); + __ cmp(scratch, ip); + __ b(ne, miss); +} + + +// TODO(kmillikin): Eliminate this function when the stub cache is fully +// handlified. +MUST_USE_RESULT static MaybeObject* TryGenerateCheckPropertyCell( MacroAssembler* masm, GlobalObject* global, String* name, @@ -889,9 +966,32 @@ MUST_USE_RESULT static MaybeObject* GenerateCheckPropertyCell( return cell; } + // Calls GenerateCheckPropertyCell for each global object in the prototype chain // from object to (but not including) holder. -MUST_USE_RESULT static MaybeObject* GenerateCheckPropertyCells( +static void GenerateCheckPropertyCells(MacroAssembler* masm, + Handle object, + Handle holder, + Handle name, + Register scratch, + Label* miss) { + Handle current = object; + while (!current.is_identical_to(holder)) { + if (current->IsGlobalObject()) { + GenerateCheckPropertyCell(masm, + Handle::cast(current), + name, + scratch, + miss); + } + current = Handle(JSObject::cast(current->GetPrototype())); + } +} + + +// TODO(kmillikin): Eliminate this function when the stub cache is fully +// handlified. +MUST_USE_RESULT static MaybeObject* TryGenerateCheckPropertyCells( MacroAssembler* masm, JSObject* object, JSObject* holder, @@ -902,7 +1002,7 @@ MUST_USE_RESULT static MaybeObject* GenerateCheckPropertyCells( while (current != holder) { if (current->IsGlobalObject()) { // Returns a cell or a failure. - MaybeObject* result = GenerateCheckPropertyCell( + MaybeObject* result = TryGenerateCheckPropertyCell( masm, GlobalObject::cast(current), name, @@ -1027,6 +1127,112 @@ static void GenerateUInt2Double(MacroAssembler* masm, #define __ ACCESS_MASM(masm()) +Register StubCompiler::CheckPrototypes(Handle object, + Register object_reg, + Handle holder, + Register holder_reg, + Register scratch1, + Register scratch2, + Handle name, + int save_at_depth, + Label* miss) { + // Make sure there's no overlap between holder and object registers. + ASSERT(!scratch1.is(object_reg) && !scratch1.is(holder_reg)); + ASSERT(!scratch2.is(object_reg) && !scratch2.is(holder_reg) + && !scratch2.is(scratch1)); + + // Keep track of the current object in register reg. + Register reg = object_reg; + int depth = 0; + + if (save_at_depth == depth) { + __ str(reg, MemOperand(sp)); + } + + // Check the maps in the prototype chain. + // Traverse the prototype chain from the object and do map checks. + Handle current = object; + while (!current.is_identical_to(holder)) { + ++depth; + + // Only global objects and objects that do not require access + // checks are allowed in stubs. + ASSERT(current->IsJSGlobalProxy() || !current->IsAccessCheckNeeded()); + + Handle prototype(JSObject::cast(current->GetPrototype())); + if (!current->HasFastProperties() && + !current->IsJSGlobalObject() && + !current->IsJSGlobalProxy()) { + if (!name->IsSymbol()) { + name = factory()->LookupSymbol(name); + } + ASSERT(current->property_dictionary()->FindEntry(*name) == + StringDictionary::kNotFound); + + GenerateDictionaryNegativeLookup(masm(), miss, reg, name, + scratch1, scratch2); + + __ ldr(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset)); + reg = holder_reg; // From now on the object will be in holder_reg. + __ ldr(reg, FieldMemOperand(scratch1, Map::kPrototypeOffset)); + } else { + Handle current_map(current->map()); + __ ldr(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset)); + __ cmp(scratch1, Operand(current_map)); + // Branch on the result of the map check. + __ b(ne, miss); + // Check access rights to the global object. This has to happen after + // the map check so that we know that the object is actually a global + // object. + if (current->IsJSGlobalProxy()) { + __ CheckAccessGlobalProxy(reg, scratch2, miss); + } + reg = holder_reg; // From now on the object will be in holder_reg. + + if (heap()->InNewSpace(*prototype)) { + // The prototype is in new space; we cannot store a reference to it + // in the code. Load it from the map. + __ ldr(reg, FieldMemOperand(scratch1, Map::kPrototypeOffset)); + } else { + // The prototype is in old space; load it directly. + __ mov(reg, Operand(prototype)); + } + } + + if (save_at_depth == depth) { + __ str(reg, MemOperand(sp)); + } + + // Go to the next object in the prototype chain. + current = prototype; + } + + // Log the check depth. + LOG(masm()->isolate(), IntEvent("check-maps-depth", depth + 1)); + + // Check the holder map. + __ ldr(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset)); + __ cmp(scratch1, Operand(Handle(current->map()))); + __ b(ne, miss); + + // Perform security check for access to the global object. + ASSERT(holder->IsJSGlobalProxy() || !holder->IsAccessCheckNeeded()); + if (holder->IsJSGlobalProxy()) { + __ CheckAccessGlobalProxy(reg, scratch1, miss); + } + + // If we've skipped any global objects, it's not enough to verify that + // their maps haven't changed. We also need to check that the property + // cell for the property is still empty. + GenerateCheckPropertyCells(masm(), object, holder, name, scratch1, miss); + + // Return the register containing the holder. + return reg; +} + + +// TODO(kmillikin): Eliminate this function when the stub cache is fully +// handlified. Register StubCompiler::CheckPrototypes(JSObject* object, Register object_reg, JSObject* holder, @@ -1076,12 +1282,13 @@ Register StubCompiler::CheckPrototypes(JSObject* object, ASSERT(current->property_dictionary()->FindEntry(name) == StringDictionary::kNotFound); - MaybeObject* negative_lookup = GenerateDictionaryNegativeLookup(masm(), - miss, - reg, - name, - scratch1, - scratch2); + MaybeObject* negative_lookup = + TryGenerateDictionaryNegativeLookup(masm(), + miss, + reg, + name, + scratch1, + scratch2); if (negative_lookup->IsFailure()) { set_failure(Failure::cast(negative_lookup)); return reg; @@ -1150,17 +1357,17 @@ Register StubCompiler::CheckPrototypes(JSObject* object, ASSERT(holder->IsJSGlobalProxy() || !holder->IsAccessCheckNeeded()); if (holder->IsJSGlobalProxy()) { __ CheckAccessGlobalProxy(reg, scratch1, miss); - }; + } // If we've skipped any global objects, it's not enough to verify // that their maps haven't changed. We also need to check that the // property cell for the property is still empty. - MaybeObject* result = GenerateCheckPropertyCells(masm(), - object, - holder, - name, - scratch1, - miss); + MaybeObject* result = TryGenerateCheckPropertyCells(masm(), + object, + holder, + name, + scratch1, + miss); if (result->IsFailure()) set_failure(Failure::cast(result)); // Return the register containing the holder. @@ -1184,7 +1391,7 @@ void StubCompiler::GenerateLoadField(JSObject* object, Register reg = CheckPrototypes(object, receiver, holder, scratch1, scratch2, scratch3, name, miss); - GenerateFastPropertyLoad(masm(), r0, reg, holder, index); + GenerateFastPropertyLoad(masm(), r0, reg, Handle(holder), index); __ Ret(); } @@ -1365,7 +1572,8 @@ void StubCompiler::GenerateLoadInterceptor(JSObject* object, // We found FIELD property in prototype chain of interceptor's holder. // Retrieve a field from field's holder. GenerateFastPropertyLoad(masm(), r0, holder_reg, - lookup->holder(), lookup->GetFieldIndex()); + Handle(lookup->holder()), + lookup->GetFieldIndex()); __ Ret(); } else { // We found CALLBACKS property in prototype chain of interceptor's @@ -1416,9 +1624,9 @@ void StubCompiler::GenerateLoadInterceptor(JSObject* object, } -void CallStubCompiler::GenerateNameCheck(String* name, Label* miss) { +void CallStubCompiler::GenerateNameCheck(Handle name, Label* miss) { if (kind_ == Code::KEYED_CALL_IC) { - __ cmp(r2, Operand(Handle(name))); + __ cmp(r2, Operand(name)); __ b(ne, miss); } } @@ -1478,7 +1686,18 @@ void CallStubCompiler::GenerateLoadFunctionFromCell(JSGlobalPropertyCell* cell, } -MaybeObject* CallStubCompiler::GenerateMissBranch() { +void CallStubCompiler::GenerateMissBranch() { + Handle code = + isolate()->stub_cache()->ComputeCallMiss(arguments().immediate(), + kind_, + extra_state_); + __ Jump(code, RelocInfo::CODE_TARGET); +} + + +// TODO(kmillikin): Eliminate this function when the stub cache is fully +// handlified. +MaybeObject* CallStubCompiler::TryGenerateMissBranch() { MaybeObject* maybe_obj = isolate()->stub_cache()->TryComputeCallMiss(arguments().immediate(), kind_, @@ -1490,10 +1709,10 @@ MaybeObject* CallStubCompiler::GenerateMissBranch() { } -MaybeObject* CallStubCompiler::CompileCallField(JSObject* object, - JSObject* holder, +Handle CallStubCompiler::CompileCallField(Handle object, + Handle holder, int index, - String* name) { + Handle name) { // ----------- S t a t e ------------- // -- r2 : name // -- lr : return address @@ -1517,8 +1736,7 @@ MaybeObject* CallStubCompiler::CompileCallField(JSObject* object, // Handle call cache miss. __ bind(&miss); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(FIELD, name); @@ -1543,7 +1761,7 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, Label miss; - GenerateNameCheck(name, &miss); + GenerateNameCheck(Handle(name), &miss); Register receiver = r1; @@ -1709,11 +1927,11 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, // Handle call cache miss. __ bind(&miss); - MaybeObject* maybe_result = GenerateMissBranch(); + MaybeObject* maybe_result = TryGenerateMissBranch(); if (maybe_result->IsFailure()) return maybe_result; // Return the generated code. - return GetCode(function); + return TryGetCode(function); } @@ -1738,7 +1956,7 @@ MaybeObject* CallStubCompiler::CompileArrayPopCall(Object* object, Register receiver = r1; Register elements = r3; - GenerateNameCheck(name, &miss); + GenerateNameCheck(Handle(name), &miss); // Get the receiver from the stack const int argc = arguments().immediate(); @@ -1798,11 +2016,11 @@ MaybeObject* CallStubCompiler::CompileArrayPopCall(Object* object, // Handle call cache miss. __ bind(&miss); - MaybeObject* maybe_result = GenerateMissBranch(); + MaybeObject* maybe_result = TryGenerateMissBranch(); if (maybe_result->IsFailure()) return maybe_result; // Return the generated code. - return GetCode(function); + return TryGetCode(function); } @@ -1836,7 +2054,7 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( index_out_of_range_label = &miss; } - GenerateNameCheck(name, &name_miss); + GenerateNameCheck(Handle(name), &name_miss); // Check that the maps starting from the prototype haven't changed. GenerateDirectLoadGlobalFunctionPrototype(masm(), @@ -1884,11 +2102,11 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( // Restore function name in r2. __ Move(r2, Handle(name)); __ bind(&name_miss); - MaybeObject* maybe_result = GenerateMissBranch(); + MaybeObject* maybe_result = TryGenerateMissBranch(); if (maybe_result->IsFailure()) return maybe_result; // Return the generated code. - return GetCode(function); + return TryGetCode(function); } @@ -1922,7 +2140,7 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall( index_out_of_range_label = &miss; } - GenerateNameCheck(name, &name_miss); + GenerateNameCheck(Handle(name), &name_miss); // Check that the maps starting from the prototype haven't changed. GenerateDirectLoadGlobalFunctionPrototype(masm(), @@ -1972,11 +2190,11 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall( // Restore function name in r2. __ Move(r2, Handle(name)); __ bind(&name_miss); - MaybeObject* maybe_result = GenerateMissBranch(); + MaybeObject* maybe_result = TryGenerateMissBranch(); if (maybe_result->IsFailure()) return maybe_result; // Return the generated code. - return GetCode(function); + return TryGetCode(function); } @@ -2001,7 +2219,7 @@ MaybeObject* CallStubCompiler::CompileStringFromCharCodeCall( if (!object->IsJSObject() || argc != 1) return heap()->undefined_value(); Label miss; - GenerateNameCheck(name, &miss); + GenerateNameCheck(Handle(name), &miss); if (cell == NULL) { __ ldr(r1, MemOperand(sp, 1 * kPointerSize)); @@ -2044,11 +2262,11 @@ MaybeObject* CallStubCompiler::CompileStringFromCharCodeCall( __ bind(&miss); // r2: function name. - MaybeObject* maybe_result = GenerateMissBranch(); + MaybeObject* maybe_result = TryGenerateMissBranch(); if (maybe_result->IsFailure()) return maybe_result; // Return the generated code. - return (cell == NULL) ? GetCode(function) : GetCode(NORMAL, name); + return (cell == NULL) ? TryGetCode(function) : TryGetCode(NORMAL, name); } @@ -2078,7 +2296,7 @@ MaybeObject* CallStubCompiler::CompileMathFloorCall(Object* object, if (!object->IsJSObject() || argc != 1) return heap()->undefined_value(); Label miss, slow; - GenerateNameCheck(name, &miss); + GenerateNameCheck(Handle(name), &miss); if (cell == NULL) { __ ldr(r1, MemOperand(sp, 1 * kPointerSize)); @@ -2192,11 +2410,11 @@ MaybeObject* CallStubCompiler::CompileMathFloorCall(Object* object, __ bind(&miss); // r2: function name. - MaybeObject* maybe_result = GenerateMissBranch(); + MaybeObject* maybe_result = TryGenerateMissBranch(); if (maybe_result->IsFailure()) return maybe_result; // Return the generated code. - return (cell == NULL) ? GetCode(function) : GetCode(NORMAL, name); + return (cell == NULL) ? TryGetCode(function) : TryGetCode(NORMAL, name); } @@ -2220,7 +2438,7 @@ MaybeObject* CallStubCompiler::CompileMathAbsCall(Object* object, if (!object->IsJSObject() || argc != 1) return heap()->undefined_value(); Label miss; - GenerateNameCheck(name, &miss); + GenerateNameCheck(Handle(name), &miss); if (cell == NULL) { __ ldr(r1, MemOperand(sp, 1 * kPointerSize)); @@ -2293,11 +2511,11 @@ MaybeObject* CallStubCompiler::CompileMathAbsCall(Object* object, __ bind(&miss); // r2: function name. - MaybeObject* maybe_result = GenerateMissBranch(); + MaybeObject* maybe_result = TryGenerateMissBranch(); if (maybe_result->IsFailure()) return maybe_result; // Return the generated code. - return (cell == NULL) ? GetCode(function) : GetCode(NORMAL, name); + return (cell == NULL) ? TryGetCode(function) : TryGetCode(NORMAL, name); } @@ -2322,7 +2540,7 @@ MaybeObject* CallStubCompiler::CompileFastApiCall( Label miss, miss_before_stack_reserved; - GenerateNameCheck(name, &miss_before_stack_reserved); + GenerateNameCheck(Handle(name), &miss_before_stack_reserved); // Get the receiver from the stack. const int argc = arguments().immediate(); @@ -2347,11 +2565,11 @@ MaybeObject* CallStubCompiler::CompileFastApiCall( FreeSpaceForFastApiCall(masm()); __ bind(&miss_before_stack_reserved); - MaybeObject* maybe_result = GenerateMissBranch(); + MaybeObject* maybe_result = TryGenerateMissBranch(); if (maybe_result->IsFailure()) return maybe_result; // Return the generated code. - return GetCode(function); + return TryGetCode(function); } @@ -2375,7 +2593,7 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object, Label miss; - GenerateNameCheck(name, &miss); + GenerateNameCheck(Handle(name), &miss); // Get the receiver from the stack const int argc = arguments().immediate(); @@ -2481,11 +2699,11 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object, // Handle call cache miss. __ bind(&miss); - MaybeObject* maybe_result = GenerateMissBranch(); + MaybeObject* maybe_result = TryGenerateMissBranch(); if (maybe_result->IsFailure()) return maybe_result; // Return the generated code. - return GetCode(function); + return TryGetCode(function); } @@ -2499,7 +2717,7 @@ MaybeObject* CallStubCompiler::CompileCallInterceptor(JSObject* object, Label miss; - GenerateNameCheck(name, &miss); + GenerateNameCheck(Handle(name), &miss); // Get the number of arguments. const int argc = arguments().immediate(); @@ -2530,15 +2748,16 @@ MaybeObject* CallStubCompiler::CompileCallInterceptor(JSObject* object, // Restore receiver. __ ldr(r0, MemOperand(sp, argc * kPointerSize)); - GenerateCallFunction(masm(), object, arguments(), &miss, extra_state_); + GenerateCallFunction(masm(), Handle(object), arguments(), &miss, + extra_state_); // Handle call cache miss. __ bind(&miss); - MaybeObject* maybe_result = GenerateMissBranch(); + MaybeObject* maybe_result = TryGenerateMissBranch(); if (maybe_result->IsFailure()) return maybe_result; // Return the generated code. - return GetCode(INTERCEPTOR, name); + return TryGetCode(INTERCEPTOR, name); } @@ -2563,7 +2782,7 @@ MaybeObject* CallStubCompiler::CompileCallGlobal(JSObject* object, Label miss; - GenerateNameCheck(name, &miss); + GenerateNameCheck(Handle(name), &miss); // Get the number of arguments. const int argc = arguments().immediate(); @@ -2600,11 +2819,11 @@ MaybeObject* CallStubCompiler::CompileCallGlobal(JSObject* object, // Handle call cache miss. __ bind(&miss); __ IncrementCounter(counters->call_global_inline_miss(), 1, r1, r3); - MaybeObject* maybe_result = GenerateMissBranch(); + MaybeObject* maybe_result = TryGenerateMissBranch(); if (maybe_result->IsFailure()) return maybe_result; // Return the generated code. - return GetCode(NORMAL, name); + return TryGetCode(NORMAL, name); } @@ -2802,11 +3021,11 @@ MaybeObject* LoadStubCompiler::CompileLoadNonexistent(String* name, // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last->IsGlobalObject()) { - MaybeObject* cell = GenerateCheckPropertyCell(masm(), - GlobalObject::cast(last), - name, - r1, - &miss); + MaybeObject* cell = TryGenerateCheckPropertyCell(masm(), + GlobalObject::cast(last), + name, + r1, + &miss); if (cell->IsFailure()) { miss.Unuse(); return cell; diff --git a/src/ia32/code-stubs-ia32.cc b/src/ia32/code-stubs-ia32.cc index 7b6eb8faf..d7d1d9c3c 100644 --- a/src/ia32/code-stubs-ia32.cc +++ b/src/ia32/code-stubs-ia32.cc @@ -6541,7 +6541,67 @@ void ICCompareStub::GenerateMiss(MacroAssembler* masm) { // must always call a backup property check that is complete. // This function is safe to call if the receiver has fast properties. // Name must be a symbol and receiver must be a heap object. -MaybeObject* StringDictionaryLookupStub::GenerateNegativeLookup( +void StringDictionaryLookupStub::GenerateNegativeLookup(MacroAssembler* masm, + Label* miss, + Label* done, + Register properties, + Handle name, + Register r0) { + ASSERT(name->IsSymbol()); + + // If names of slots in range from 1 to kProbes - 1 for the hash value are + // not equal to the name and kProbes-th slot is not used (its name is the + // undefined value), it guarantees the hash table doesn't contain the + // property. It's true even if some slots represent deleted properties + // (their names are the null value). + for (int i = 0; i < kInlinedProbes; i++) { + // Compute the masked index: (hash + i + i * i) & mask. + Register index = r0; + // Capacity is smi 2^n. + __ mov(index, FieldOperand(properties, kCapacityOffset)); + __ dec(index); + __ and_(index, + Immediate(Smi::FromInt(name->Hash() + + StringDictionary::GetProbeOffset(i)))); + + // Scale the index by multiplying by the entry size. + ASSERT(StringDictionary::kEntrySize == 3); + __ lea(index, Operand(index, index, times_2, 0)); // index *= 3. + Register entity_name = r0; + // Having undefined at this place means the name is not contained. + ASSERT_EQ(kSmiTagSize, 1); + __ mov(entity_name, Operand(properties, index, times_half_pointer_size, + kElementsStartOffset - kHeapObjectTag)); + __ cmp(entity_name, masm->isolate()->factory()->undefined_value()); + __ j(equal, done); + + // Stop if found the property. + __ cmp(entity_name, Handle(name)); + __ j(equal, miss); + + // Check if the entry name is not a symbol. + __ mov(entity_name, FieldOperand(entity_name, HeapObject::kMapOffset)); + __ test_b(FieldOperand(entity_name, Map::kInstanceTypeOffset), + kIsSymbolMask); + __ j(zero, miss); + } + + StringDictionaryLookupStub stub(properties, + r0, + r0, + StringDictionaryLookupStub::NEGATIVE_LOOKUP); + __ push(Immediate(Handle(name))); + __ push(Immediate(name->Hash())); + __ CallStub(&stub); + __ test(r0, r0); + __ j(not_zero, miss); + __ jmp(done); +} + + +// TODO(kmillikin): Eliminate this function when the stub cache is fully +// handlified. +MaybeObject* StringDictionaryLookupStub::TryGenerateNegativeLookup( MacroAssembler* masm, Label* miss, Label* done, diff --git a/src/ia32/code-stubs-ia32.h b/src/ia32/code-stubs-ia32.h index 2a7d316f4..8775344a5 100644 --- a/src/ia32/code-stubs-ia32.h +++ b/src/ia32/code-stubs-ia32.h @@ -421,7 +421,16 @@ class StringDictionaryLookupStub: public CodeStub { void Generate(MacroAssembler* masm); - MUST_USE_RESULT static MaybeObject* GenerateNegativeLookup( + static void GenerateNegativeLookup(MacroAssembler* masm, + Label* miss, + Label* done, + Register properties, + Handle name, + Register r0); + + // TODO(kmillikin): Eliminate this function when the stub cache is fully + // handlified. + MUST_USE_RESULT static MaybeObject* TryGenerateNegativeLookup( MacroAssembler* masm, Label* miss, Label* done, diff --git a/src/ia32/stub-cache-ia32.cc b/src/ia32/stub-cache-ia32.cc index 3c5f6323e..25f35ce51 100644 --- a/src/ia32/stub-cache-ia32.cc +++ b/src/ia32/stub-cache-ia32.cc @@ -107,12 +107,60 @@ static void ProbeTable(Isolate* isolate, // must always call a backup property check that is complete. // This function is safe to call if the receiver has fast properties. // Name must be a symbol and receiver must be a heap object. -static MaybeObject* GenerateDictionaryNegativeLookup(MacroAssembler* masm, - Label* miss_label, - Register receiver, - String* name, - Register r0, - Register r1) { +static void GenerateDictionaryNegativeLookup(MacroAssembler* masm, + Label* miss_label, + Register receiver, + Handle name, + Register r0, + Register r1) { + ASSERT(name->IsSymbol()); + Counters* counters = masm->isolate()->counters(); + __ IncrementCounter(counters->negative_lookups(), 1); + __ IncrementCounter(counters->negative_lookups_miss(), 1); + + __ mov(r0, FieldOperand(receiver, HeapObject::kMapOffset)); + + const int kInterceptorOrAccessCheckNeededMask = + (1 << Map::kHasNamedInterceptor) | (1 << Map::kIsAccessCheckNeeded); + + // Bail out if the receiver has a named interceptor or requires access checks. + __ test_b(FieldOperand(r0, Map::kBitFieldOffset), + kInterceptorOrAccessCheckNeededMask); + __ j(not_zero, miss_label); + + // Check that receiver is a JSObject. + __ CmpInstanceType(r0, FIRST_SPEC_OBJECT_TYPE); + __ j(below, miss_label); + + // Load properties array. + Register properties = r0; + __ mov(properties, FieldOperand(receiver, JSObject::kPropertiesOffset)); + + // Check that the properties array is a dictionary. + __ cmp(FieldOperand(properties, HeapObject::kMapOffset), + Immediate(masm->isolate()->factory()->hash_table_map())); + __ j(not_equal, miss_label); + + Label done; + StringDictionaryLookupStub::GenerateNegativeLookup(masm, + miss_label, + &done, + properties, + name, + r1); + __ bind(&done); + __ DecrementCounter(counters->negative_lookups_miss(), 1); +} + + +// TODO(kmillikin): Eliminate this function when the stub cache is fully +// handlified. +static MaybeObject* TryGenerateDictionaryNegativeLookup(MacroAssembler* masm, + Label* miss_label, + Register receiver, + String* name, + Register r0, + Register r1) { ASSERT(name->IsSymbol()); Counters* counters = masm->isolate()->counters(); __ IncrementCounter(counters->negative_lookups(), 1); @@ -143,12 +191,12 @@ static MaybeObject* GenerateDictionaryNegativeLookup(MacroAssembler* masm, Label done; MaybeObject* result = - StringDictionaryLookupStub::GenerateNegativeLookup(masm, - miss_label, - &done, - properties, - name, - r1); + StringDictionaryLookupStub::TryGenerateNegativeLookup(masm, + miss_label, + &done, + properties, + name, + r1); if (result->IsFailure()) return result; __ bind(&done); @@ -325,8 +373,10 @@ void StubCompiler::GenerateLoadFunctionPrototype(MacroAssembler* masm, // are loaded directly otherwise the property is loaded from the properties // fixed array. void StubCompiler::GenerateFastPropertyLoad(MacroAssembler* masm, - Register dst, Register src, - JSObject* holder, int index) { + Register dst, + Register src, + Handle holder, + int index) { // Adjust for the number of properties stored in the holder. index -= holder->map()->inobject_properties(); if (index < 0) { @@ -818,7 +868,29 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm, // Generate code to check that a global property cell is empty. Create // the property cell at compilation time if no cell exists for the // property. -MUST_USE_RESULT static MaybeObject* GenerateCheckPropertyCell( +static void GenerateCheckPropertyCell(MacroAssembler* masm, + Handle global, + Handle name, + Register scratch, + Label* miss) { + Handle cell = + GlobalObject::EnsurePropertyCell(global, name); + ASSERT(cell->value()->IsTheHole()); + Handle the_hole = masm->isolate()->factory()->the_hole_value(); + if (Serializer::enabled()) { + __ mov(scratch, Immediate(cell)); + __ cmp(FieldOperand(scratch, JSGlobalPropertyCell::kValueOffset), + Immediate(the_hole)); + } else { + __ cmp(Operand::Cell(cell), Immediate(the_hole)); + } + __ j(not_equal, miss); +} + + +// TODO(kmillikin): Eliminate this function when the stub cache is fully +// handlified. +MUST_USE_RESULT static MaybeObject* TryGenerateCheckPropertyCell( MacroAssembler* masm, GlobalObject* global, String* name, @@ -845,7 +917,29 @@ MUST_USE_RESULT static MaybeObject* GenerateCheckPropertyCell( // Calls GenerateCheckPropertyCell for each global object in the prototype chain // from object to (but not including) holder. -MUST_USE_RESULT static MaybeObject* GenerateCheckPropertyCells( +static void GenerateCheckPropertyCells(MacroAssembler* masm, + Handle object, + Handle holder, + Handle name, + Register scratch, + Label* miss) { + Handle current = object; + while (!current.is_identical_to(holder)) { + if (current->IsGlobalObject()) { + GenerateCheckPropertyCell(masm, + Handle::cast(current), + name, + scratch, + miss); + } + current = Handle(JSObject::cast(current->GetPrototype())); + } +} + + +// TODO(kmillikin): Eliminate this function when the stub cache is fully +// handlified. +MUST_USE_RESULT static MaybeObject* TryGenerateCheckPropertyCells( MacroAssembler* masm, JSObject* object, JSObject* holder, @@ -856,7 +950,7 @@ MUST_USE_RESULT static MaybeObject* GenerateCheckPropertyCells( while (current != holder) { if (current->IsGlobalObject()) { // Returns a cell or a failure. - MaybeObject* result = GenerateCheckPropertyCell( + MaybeObject* result = TryGenerateCheckPropertyCell( masm, GlobalObject::cast(current), name, @@ -875,6 +969,120 @@ MUST_USE_RESULT static MaybeObject* GenerateCheckPropertyCells( #define __ ACCESS_MASM(masm()) +Register StubCompiler::CheckPrototypes(Handle object, + Register object_reg, + Handle holder, + Register holder_reg, + Register scratch1, + Register scratch2, + Handle name, + int save_at_depth, + Label* miss) { + // Make sure there's no overlap between holder and object registers. + ASSERT(!scratch1.is(object_reg) && !scratch1.is(holder_reg)); + ASSERT(!scratch2.is(object_reg) && !scratch2.is(holder_reg) + && !scratch2.is(scratch1)); + + // Keep track of the current object in register reg. + Register reg = object_reg; + Handle current = object; + int depth = 0; + + if (save_at_depth == depth) { + __ mov(Operand(esp, kPointerSize), reg); + } + + // Traverse the prototype chain and check the maps in the prototype chain for + // fast and global objects or do negative lookup for normal objects. + while (!current.is_identical_to(holder)) { + ++depth; + + // Only global objects and objects that do not require access + // checks are allowed in stubs. + ASSERT(current->IsJSGlobalProxy() || !current->IsAccessCheckNeeded()); + + Handle prototype(JSObject::cast(current->GetPrototype())); + if (!current->HasFastProperties() && + !current->IsJSGlobalObject() && + !current->IsJSGlobalProxy()) { + if (!name->IsSymbol()) { + name = factory()->LookupSymbol(name); + } + ASSERT(current->property_dictionary()->FindEntry(*name) == + StringDictionary::kNotFound); + + GenerateDictionaryNegativeLookup(masm(), miss, reg, name, + scratch1, scratch2); + + __ mov(scratch1, FieldOperand(reg, HeapObject::kMapOffset)); + reg = holder_reg; // From now on the object will be in holder_reg. + __ mov(reg, FieldOperand(scratch1, Map::kPrototypeOffset)); + } else { + bool in_new_space = heap()->InNewSpace(*prototype); + Handle current_map(current->map()); + if (in_new_space) { + // Save the map in scratch1 for later. + __ mov(scratch1, FieldOperand(reg, HeapObject::kMapOffset)); + __ cmp(scratch1, Immediate(current_map)); + } else { + __ cmp(FieldOperand(reg, HeapObject::kMapOffset), + Immediate(current_map)); + } + // Branch on the result of the map check. + __ j(not_equal, miss); + // Check access rights to the global object. This has to happen after + // the map check so that we know that the object is actually a global + // object. + if (current->IsJSGlobalProxy()) { + __ CheckAccessGlobalProxy(reg, scratch2, miss); + } + reg = holder_reg; // From now on the object will be in holder_reg. + + if (in_new_space) { + // The prototype is in new space; we cannot store a reference to it + // in the code. Load it from the map. + __ mov(reg, FieldOperand(scratch1, Map::kPrototypeOffset)); + } else { + // The prototype is in old space; load it directly. + __ mov(reg, prototype); + } + } + + if (save_at_depth == depth) { + __ mov(Operand(esp, kPointerSize), reg); + } + + // Go to the next object in the prototype chain. + current = prototype; + } + ASSERT(current.is_identical_to(holder)); + + // Log the check depth. + LOG(isolate(), IntEvent("check-maps-depth", depth + 1)); + + // Check the holder map. + __ cmp(FieldOperand(reg, HeapObject::kMapOffset), + Immediate(Handle(holder->map()))); + __ j(not_equal, miss); + + // Perform security check for access to the global object. + ASSERT(holder->IsJSGlobalProxy() || !holder->IsAccessCheckNeeded()); + if (holder->IsJSGlobalProxy()) { + __ CheckAccessGlobalProxy(reg, scratch1, miss); + } + + // If we've skipped any global objects, it's not enough to verify that + // their maps haven't changed. We also need to check that the property + // cell for the property is still empty. + GenerateCheckPropertyCells(masm(), object, holder, name, scratch1, miss); + + // Return the register containing the holder. + return reg; +} + + +// TODO(kmillikin): Eliminate this function when the stub cache is fully +// handlified. Register StubCompiler::CheckPrototypes(JSObject* object, Register object_reg, JSObject* holder, @@ -924,12 +1132,9 @@ Register StubCompiler::CheckPrototypes(JSObject* object, ASSERT(current->property_dictionary()->FindEntry(name) == StringDictionary::kNotFound); - MaybeObject* negative_lookup = GenerateDictionaryNegativeLookup(masm(), - miss, - reg, - name, - scratch1, - scratch2); + MaybeObject* negative_lookup = + TryGenerateDictionaryNegativeLookup(masm(), miss, reg, name, + scratch1, scratch2); if (negative_lookup->IsFailure()) { set_failure(Failure::cast(negative_lookup)); return reg; @@ -996,17 +1201,17 @@ Register StubCompiler::CheckPrototypes(JSObject* object, ASSERT(holder->IsJSGlobalProxy() || !holder->IsAccessCheckNeeded()); if (holder->IsJSGlobalProxy()) { __ CheckAccessGlobalProxy(reg, scratch1, miss); - }; + } // If we've skipped any global objects, it's not enough to verify // that their maps haven't changed. We also need to check that the // property cell for the property is still empty. - MaybeObject* result = GenerateCheckPropertyCells(masm(), - object, - holder, - name, - scratch1, - miss); + MaybeObject* result = TryGenerateCheckPropertyCells(masm(), + object, + holder, + name, + scratch1, + miss); if (result->IsFailure()) set_failure(Failure::cast(result)); // Return the register containing the holder. @@ -1032,7 +1237,7 @@ void StubCompiler::GenerateLoadField(JSObject* object, scratch1, scratch2, scratch3, name, miss); // Get the value from the properties. - GenerateFastPropertyLoad(masm(), eax, reg, holder, index); + GenerateFastPropertyLoad(masm(), eax, reg, Handle(holder), index); __ ret(0); } @@ -1221,7 +1426,8 @@ void StubCompiler::GenerateLoadInterceptor(JSObject* object, // We found FIELD property in prototype chain of interceptor's holder. // Retrieve a field from field's holder. GenerateFastPropertyLoad(masm(), eax, holder_reg, - lookup->holder(), lookup->GetFieldIndex()); + Handle(lookup->holder()), + lookup->GetFieldIndex()); __ ret(0); } else { // We found CALLBACKS property in prototype chain of interceptor's @@ -1268,9 +1474,9 @@ void StubCompiler::GenerateLoadInterceptor(JSObject* object, } -void CallStubCompiler::GenerateNameCheck(String* name, Label* miss) { +void CallStubCompiler::GenerateNameCheck(Handle name, Label* miss) { if (kind_ == Code::KEYED_CALL_IC) { - __ cmp(ecx, Immediate(Handle(name))); + __ cmp(ecx, Immediate(name)); __ j(not_equal, miss); } } @@ -1333,7 +1539,18 @@ void CallStubCompiler::GenerateLoadFunctionFromCell(JSGlobalPropertyCell* cell, } -MaybeObject* CallStubCompiler::GenerateMissBranch() { +void CallStubCompiler::GenerateMissBranch() { + Handle code = + isolate()->stub_cache()->ComputeCallMiss(arguments().immediate(), + kind_, + extra_state_); + __ jmp(code, RelocInfo::CODE_TARGET); +} + + +// TODO(kmillikin): Eliminate this function when the stub cache is fully +// handlified. +MaybeObject* CallStubCompiler::TryGenerateMissBranch() { MaybeObject* maybe_obj = isolate()->stub_cache()->TryComputeCallMiss(arguments().immediate(), kind_, @@ -1345,11 +1562,10 @@ MaybeObject* CallStubCompiler::GenerateMissBranch() { } -MUST_USE_RESULT MaybeObject* CallStubCompiler::CompileCallField( - JSObject* object, - JSObject* holder, - int index, - String* name) { +Handle CallStubCompiler::CompileCallField(Handle object, + Handle holder, + int index, + Handle name) { // ----------- S t a t e ------------- // -- ecx : name // -- esp[0] : return address @@ -1395,8 +1611,7 @@ MUST_USE_RESULT MaybeObject* CallStubCompiler::CompileCallField( // Handle call cache miss. __ bind(&miss); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(FIELD, name); @@ -1423,7 +1638,7 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, Label miss; - GenerateNameCheck(name, &miss); + GenerateNameCheck(Handle(name), &miss); // Get the receiver from the stack. const int argc = arguments().immediate(); @@ -1580,11 +1795,11 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, } __ bind(&miss); - MaybeObject* maybe_result = GenerateMissBranch(); + MaybeObject* maybe_result = TryGenerateMissBranch(); if (maybe_result->IsFailure()) return maybe_result; // Return the generated code. - return GetCode(function); + return TryGetCode(function); } @@ -1608,7 +1823,7 @@ MaybeObject* CallStubCompiler::CompileArrayPopCall(Object* object, Label miss, return_undefined, call_builtin; - GenerateNameCheck(name, &miss); + GenerateNameCheck(Handle(name), &miss); // Get the receiver from the stack. const int argc = arguments().immediate(); @@ -1663,11 +1878,11 @@ MaybeObject* CallStubCompiler::CompileArrayPopCall(Object* object, 1); __ bind(&miss); - MaybeObject* maybe_result = GenerateMissBranch(); + MaybeObject* maybe_result = TryGenerateMissBranch(); if (maybe_result->IsFailure()) return maybe_result; // Return the generated code. - return GetCode(function); + return TryGetCode(function); } @@ -1703,7 +1918,7 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( index_out_of_range_label = &miss; } - GenerateNameCheck(name, &name_miss); + GenerateNameCheck(Handle(name), &name_miss); // Check that the maps starting from the prototype haven't changed. GenerateDirectLoadGlobalFunctionPrototype(masm(), @@ -1749,11 +1964,11 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( // Restore function name in ecx. __ Set(ecx, Immediate(Handle(name))); __ bind(&name_miss); - MaybeObject* maybe_result = GenerateMissBranch(); + MaybeObject* maybe_result = TryGenerateMissBranch(); if (maybe_result->IsFailure()) return maybe_result; // Return the generated code. - return GetCode(function); + return TryGetCode(function); } @@ -1789,7 +2004,7 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall( index_out_of_range_label = &miss; } - GenerateNameCheck(name, &name_miss); + GenerateNameCheck(Handle(name), &name_miss); // Check that the maps starting from the prototype haven't changed. GenerateDirectLoadGlobalFunctionPrototype(masm(), @@ -1837,11 +2052,11 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall( // Restore function name in ecx. __ Set(ecx, Immediate(Handle(name))); __ bind(&name_miss); - MaybeObject* maybe_result = GenerateMissBranch(); + MaybeObject* maybe_result = TryGenerateMissBranch(); if (maybe_result->IsFailure()) return maybe_result; // Return the generated code. - return GetCode(function); + return TryGetCode(function); } @@ -1868,7 +2083,7 @@ MaybeObject* CallStubCompiler::CompileStringFromCharCodeCall( } Label miss; - GenerateNameCheck(name, &miss); + GenerateNameCheck(Handle(name), &miss); if (cell == NULL) { __ mov(edx, Operand(esp, 2 * kPointerSize)); @@ -1914,11 +2129,11 @@ MaybeObject* CallStubCompiler::CompileStringFromCharCodeCall( __ bind(&miss); // ecx: function name. - MaybeObject* maybe_result = GenerateMissBranch(); + MaybeObject* maybe_result = TryGenerateMissBranch(); if (maybe_result->IsFailure()) return maybe_result; // Return the generated code. - return (cell == NULL) ? GetCode(function) : GetCode(NORMAL, name); + return (cell == NULL) ? TryGetCode(function) : TryGetCode(NORMAL, name); } @@ -1950,7 +2165,7 @@ MaybeObject* CallStubCompiler::CompileMathFloorCall(Object* object, } Label miss; - GenerateNameCheck(name, &miss); + GenerateNameCheck(Handle(name), &miss); if (cell == NULL) { __ mov(edx, Operand(esp, 2 * kPointerSize)); @@ -2043,11 +2258,11 @@ MaybeObject* CallStubCompiler::CompileMathFloorCall(Object* object, __ bind(&miss); // ecx: function name. - MaybeObject* maybe_result = GenerateMissBranch(); + MaybeObject* maybe_result = TryGenerateMissBranch(); if (maybe_result->IsFailure()) return maybe_result; // Return the generated code. - return (cell == NULL) ? GetCode(function) : GetCode(NORMAL, name); + return (cell == NULL) ? TryGetCode(function) : TryGetCode(NORMAL, name); } @@ -2073,7 +2288,7 @@ MaybeObject* CallStubCompiler::CompileMathAbsCall(Object* object, } Label miss; - GenerateNameCheck(name, &miss); + GenerateNameCheck(Handle(name), &miss); if (cell == NULL) { __ mov(edx, Operand(esp, 2 * kPointerSize)); @@ -2147,11 +2362,11 @@ MaybeObject* CallStubCompiler::CompileMathAbsCall(Object* object, __ bind(&miss); // ecx: function name. - MaybeObject* maybe_result = GenerateMissBranch(); + MaybeObject* maybe_result = TryGenerateMissBranch(); if (maybe_result->IsFailure()) return maybe_result; // Return the generated code. - return (cell == NULL) ? GetCode(function) : GetCode(NORMAL, name); + return (cell == NULL) ? TryGetCode(function) : TryGetCode(NORMAL, name); } @@ -2174,7 +2389,7 @@ MaybeObject* CallStubCompiler::CompileFastApiCall( Label miss, miss_before_stack_reserved; - GenerateNameCheck(name, &miss_before_stack_reserved); + GenerateNameCheck(Handle(name), &miss_before_stack_reserved); // Get the receiver from the stack. const int argc = arguments().immediate(); @@ -2208,11 +2423,11 @@ MaybeObject* CallStubCompiler::CompileFastApiCall( __ add(esp, Immediate(kFastApiCallArguments * kPointerSize)); __ bind(&miss_before_stack_reserved); - MaybeObject* maybe_result = GenerateMissBranch(); + MaybeObject* maybe_result = TryGenerateMissBranch(); if (maybe_result->IsFailure()) return maybe_result; // Return the generated code. - return GetCode(function); + return TryGetCode(function); } @@ -2241,7 +2456,7 @@ MaybeObject* CallStubCompiler::CompileCallConstant( Label miss; - GenerateNameCheck(name, &miss); + GenerateNameCheck(Handle(name), &miss); // Get the receiver from the stack. const int argc = arguments().immediate(); @@ -2345,11 +2560,11 @@ MaybeObject* CallStubCompiler::CompileCallConstant( // Handle call cache miss. __ bind(&miss); - MaybeObject* maybe_result = GenerateMissBranch(); + MaybeObject* maybe_result = TryGenerateMissBranch(); if (maybe_result->IsFailure()) return maybe_result; // Return the generated code. - return GetCode(function); + return TryGetCode(function); } @@ -2365,7 +2580,7 @@ MaybeObject* CallStubCompiler::CompileCallInterceptor(JSObject* object, // ----------------------------------- Label miss; - GenerateNameCheck(name, &miss); + GenerateNameCheck(Handle(name), &miss); // Get the number of arguments. const int argc = arguments().immediate(); @@ -2414,11 +2629,11 @@ MaybeObject* CallStubCompiler::CompileCallInterceptor(JSObject* object, // Handle load cache miss. __ bind(&miss); - MaybeObject* maybe_result = GenerateMissBranch(); + MaybeObject* maybe_result = TryGenerateMissBranch(); if (maybe_result->IsFailure()) return maybe_result; // Return the generated code. - return GetCode(INTERCEPTOR, name); + return TryGetCode(INTERCEPTOR, name); } @@ -2447,7 +2662,7 @@ MaybeObject* CallStubCompiler::CompileCallGlobal( Label miss; - GenerateNameCheck(name, &miss); + GenerateNameCheck(Handle(name), &miss); // Get the number of arguments. const int argc = arguments().immediate(); @@ -2482,11 +2697,11 @@ MaybeObject* CallStubCompiler::CompileCallGlobal( // Handle call cache miss. __ bind(&miss); __ IncrementCounter(counters->call_global_inline_miss(), 1); - MaybeObject* maybe_result = GenerateMissBranch(); + MaybeObject* maybe_result = TryGenerateMissBranch(); if (maybe_result->IsFailure()) return maybe_result; // Return the generated code. - return GetCode(NORMAL, name); + return TryGetCode(NORMAL, name); } @@ -2804,11 +3019,11 @@ MaybeObject* LoadStubCompiler::CompileLoadNonexistent(String* name, // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last->IsGlobalObject()) { - MaybeObject* cell = GenerateCheckPropertyCell(masm(), - GlobalObject::cast(last), - name, - edx, - &miss); + MaybeObject* cell = TryGenerateCheckPropertyCell(masm(), + GlobalObject::cast(last), + name, + edx, + &miss); if (cell->IsFailure()) { miss.Unuse(); return cell; diff --git a/src/objects.cc b/src/objects.cc index 5bfead79f..fe7bca540 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -11472,6 +11472,16 @@ JSGlobalPropertyCell* GlobalObject::GetPropertyCell(LookupResult* result) { } +Handle GlobalObject::EnsurePropertyCell( + Handle global, + Handle name) { + Isolate* isolate = global->GetIsolate(); + CALL_HEAP_FUNCTION(isolate, + global->EnsurePropertyCell(*name), + JSGlobalPropertyCell); +} + + MaybeObject* GlobalObject::EnsurePropertyCell(String* name) { ASSERT(!HasFastProperties()); int entry = property_dictionary()->FindEntry(name); diff --git a/src/objects.h b/src/objects.h index 40c40660d..335bdc818 100644 --- a/src/objects.h +++ b/src/objects.h @@ -5395,6 +5395,11 @@ class GlobalObject: public JSObject { } // Ensure that the global object has a cell for the given property name. + static Handle EnsurePropertyCell( + Handle global, + Handle name); + // TODO(kmillikin): This function can be eliminated once the stub cache is + // full handlified (and the static helper can be written directly). MUST_USE_RESULT MaybeObject* EnsurePropertyCell(String* name); // Casting. diff --git a/src/stub-cache.cc b/src/stub-cache.cc index 0c30a1d88..e5c8482ec 100644 --- a/src/stub-cache.cc +++ b/src/stub-cache.cc @@ -835,17 +835,6 @@ Handle StubCache::ComputeCallConstant(int argc, } -Handle CallStubCompiler::CompileCallField(Handle object, - Handle holder, - int index, - Handle name) { - CALL_HEAP_FUNCTION( - isolate(), - (set_failure(NULL), CompileCallField(*object, *holder, index, *name)), - Code); -} - - Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, @@ -1634,6 +1623,14 @@ Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, } +Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, + Handle name) { + return (FLAG_print_code_stubs && !name.is_null()) + ? GetCodeWithFlags(flags, *name->ToCString()) + : GetCodeWithFlags(flags, reinterpret_cast(NULL)); +} + + MaybeObject* StubCompiler::TryGetCodeWithFlags(Code::Flags flags, const char* name) { // Check for allocation failures during stub compilation. @@ -1654,7 +1651,7 @@ MaybeObject* StubCompiler::TryGetCodeWithFlags(Code::Flags flags, MaybeObject* StubCompiler::TryGetCodeWithFlags(Code::Flags flags, String* name) { - if (FLAG_print_code_stubs && (name != NULL)) { + if (FLAG_print_code_stubs && name != NULL) { return TryGetCodeWithFlags(flags, *name->ToCString()); } return TryGetCodeWithFlags(flags, reinterpret_cast(NULL)); @@ -1814,7 +1811,29 @@ MaybeObject* CallStubCompiler::CompileCustomCall(Object* object, } -MaybeObject* CallStubCompiler::GetCode(PropertyType type, String* name) { +Handle CallStubCompiler::GetCode(PropertyType type, Handle name) { + int argc = arguments_.immediate(); + Code::Flags flags = Code::ComputeMonomorphicFlags(kind_, + type, + extra_state_, + cache_holder_, + argc); + return GetCodeWithFlags(flags, name); +} + + +Handle CallStubCompiler::GetCode(Handle function) { + Handle function_name; + if (function->shared()->name()->IsString()) { + function_name = Handle(String::cast(function->shared()->name())); + } + return GetCode(CONSTANT_FUNCTION, function_name); +} + + +// TODO(kmillikin): Eliminate this function when the stub cache is fully +// handlified. +MaybeObject* CallStubCompiler::TryGetCode(PropertyType type, String* name) { int argc = arguments_.immediate(); Code::Flags flags = Code::ComputeMonomorphicFlags(kind_, type, @@ -1825,12 +1844,14 @@ MaybeObject* CallStubCompiler::GetCode(PropertyType type, String* name) { } -MaybeObject* CallStubCompiler::GetCode(JSFunction* function) { +// TODO(kmillikin): Eliminate this function when the stub cache is fully +// handlified. +MaybeObject* CallStubCompiler::TryGetCode(JSFunction* function) { String* function_name = NULL; if (function->shared()->name()->IsString()) { function_name = String::cast(function->shared()->name()); } - return GetCode(CONSTANT_FUNCTION, function_name); + return TryGetCode(CONSTANT_FUNCTION, function_name); } diff --git a/src/stub-cache.h b/src/stub-cache.h index 89ee9d76d..c1cd77a1d 100644 --- a/src/stub-cache.h +++ b/src/stub-cache.h @@ -416,8 +416,10 @@ class StubCompiler BASE_EMBEDDED { Label* miss); static void GenerateFastPropertyLoad(MacroAssembler* masm, - Register dst, Register src, - JSObject* holder, int index); + Register dst, + Register src, + Handle holder, + int index); static void GenerateLoadArrayLength(MacroAssembler* masm, Register receiver, @@ -466,7 +468,30 @@ class StubCompiler BASE_EMBEDDED { // The function can optionally (when save_at_depth != // kInvalidProtoDepth) save the object at the given depth by moving // it to [esp + kPointerSize]. + Register CheckPrototypes(Handle object, + Register object_reg, + Handle holder, + Register holder_reg, + Register scratch1, + Register scratch2, + Handle name, + Label* miss) { + return CheckPrototypes(object, object_reg, holder, holder_reg, scratch1, + scratch2, name, kInvalidProtoDepth, miss); + } + Register CheckPrototypes(Handle object, + Register object_reg, + Handle holder, + Register holder_reg, + Register scratch1, + Register scratch2, + Handle name, + int save_at_depth, + Label* miss); + + // TODO(kmillikin): Eliminate this function when the stub cache is fully + // handlified. Register CheckPrototypes(JSObject* object, Register object_reg, JSObject* holder, @@ -479,6 +504,8 @@ class StubCompiler BASE_EMBEDDED { scratch2, name, kInvalidProtoDepth, miss); } + // TODO(kmillikin): Eliminate this function when the stub cache is fully + // handlified. Register CheckPrototypes(JSObject* object, Register object_reg, JSObject* holder, @@ -491,6 +518,7 @@ class StubCompiler BASE_EMBEDDED { protected: Handle GetCodeWithFlags(Code::Flags flags, const char* name); + Handle GetCodeWithFlags(Code::Flags flags, Handle name); MUST_USE_RESULT MaybeObject* TryGetCodeWithFlags(Code::Flags flags, const char* name); @@ -826,11 +854,6 @@ class CallStubCompiler: public StubCompiler { int index, Handle name); - MUST_USE_RESULT MaybeObject* CompileCallField(JSObject* object, - JSObject* holder, - int index, - String* name); - Handle CompileCallConstant(Handle object, Handle holder, Handle function, @@ -899,13 +922,15 @@ class CallStubCompiler: public StubCompiler { const ParameterCount& arguments() { return arguments_; } - MUST_USE_RESULT MaybeObject* GetCode(PropertyType type, String* name); + Handle GetCode(PropertyType type, Handle name); + Handle GetCode(Handle function); - // Convenience function. Calls GetCode above passing - // CONSTANT_FUNCTION type and the name of the given function. - MUST_USE_RESULT MaybeObject* GetCode(JSFunction* function); + // TODO(kmillikin): Eliminate these functions when the stub cache is fully + // handlified. + MUST_USE_RESULT MaybeObject* TryGetCode(PropertyType type, String* name); + MUST_USE_RESULT MaybeObject* TryGetCode(JSFunction* function); - void GenerateNameCheck(String* name, Label* miss); + void GenerateNameCheck(Handle name, Label* miss); void GenerateGlobalReceiverCheck(JSObject* object, JSObject* holder, @@ -918,9 +943,12 @@ class CallStubCompiler: public StubCompiler { JSFunction* function, Label* miss); - // Generates a jump to CallIC miss stub. Returns Failure if the jump cannot - // be generated. - MUST_USE_RESULT MaybeObject* GenerateMissBranch(); + // Generates a jump to CallIC miss stub. + void GenerateMissBranch(); + + // TODO(kmillikin): Eliminate this function when the stub cache is fully + // handlified. + MUST_USE_RESULT MaybeObject* TryGenerateMissBranch(); }; diff --git a/src/x64/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc index cfe323eca..3dfebeec4 100644 --- a/src/x64/code-stubs-x64.cc +++ b/src/x64/code-stubs-x64.cc @@ -5461,7 +5461,68 @@ void ICCompareStub::GenerateMiss(MacroAssembler* masm) { } -MaybeObject* StringDictionaryLookupStub::GenerateNegativeLookup( +void StringDictionaryLookupStub::GenerateNegativeLookup(MacroAssembler* masm, + Label* miss, + Label* done, + Register properties, + Handle name, + Register r0) { + // If names of slots in range from 1 to kProbes - 1 for the hash value are + // not equal to the name and kProbes-th slot is not used (its name is the + // undefined value), it guarantees the hash table doesn't contain the + // property. It's true even if some slots represent deleted properties + // (their names are the null value). + for (int i = 0; i < kInlinedProbes; i++) { + // r0 points to properties hash. + // Compute the masked index: (hash + i + i * i) & mask. + Register index = r0; + // Capacity is smi 2^n. + __ SmiToInteger32(index, FieldOperand(properties, kCapacityOffset)); + __ decl(index); + __ and_(index, + Immediate(name->Hash() + StringDictionary::GetProbeOffset(i))); + + // Scale the index by multiplying by the entry size. + ASSERT(StringDictionary::kEntrySize == 3); + __ lea(index, Operand(index, index, times_2, 0)); // index *= 3. + + Register entity_name = r0; + // Having undefined at this place means the name is not contained. + ASSERT_EQ(kSmiTagSize, 1); + __ movq(entity_name, Operand(properties, + index, + times_pointer_size, + kElementsStartOffset - kHeapObjectTag)); + __ Cmp(entity_name, masm->isolate()->factory()->undefined_value()); + __ j(equal, done); + + // Stop if found the property. + __ Cmp(entity_name, Handle(name)); + __ j(equal, miss); + + // Check if the entry name is not a symbol. + __ movq(entity_name, FieldOperand(entity_name, HeapObject::kMapOffset)); + __ testb(FieldOperand(entity_name, Map::kInstanceTypeOffset), + Immediate(kIsSymbolMask)); + __ j(zero, miss); + } + + StringDictionaryLookupStub stub(properties, + r0, + r0, + StringDictionaryLookupStub::NEGATIVE_LOOKUP); + __ Push(Handle(name)); + __ push(Immediate(name->Hash())); + __ CallStub(&stub); + __ testq(r0, r0); + __ j(not_zero, miss); + __ jmp(done); +} + + +// TODO(kmillikin): Eliminate this function when the stub cache is fully +// handlified. +MaybeObject* StringDictionaryLookupStub::TryGenerateNegativeLookup( MacroAssembler* masm, Label* miss, Label* done, diff --git a/src/x64/code-stubs-x64.h b/src/x64/code-stubs-x64.h index 698ba403c..ffa3f4d20 100644 --- a/src/x64/code-stubs-x64.h +++ b/src/x64/code-stubs-x64.h @@ -423,7 +423,16 @@ class StringDictionaryLookupStub: public CodeStub { void Generate(MacroAssembler* masm); - MUST_USE_RESULT static MaybeObject* GenerateNegativeLookup( + static void GenerateNegativeLookup(MacroAssembler* masm, + Label* miss, + Label* done, + Register properties, + Handle name, + Register r0); + + // TODO(kmillikin): Eliminate this function when the stub cache is fully + // handlified. + MUST_USE_RESULT static MaybeObject* TryGenerateNegativeLookup( MacroAssembler* masm, Label* miss, Label* done, diff --git a/src/x64/stub-cache-x64.cc b/src/x64/stub-cache-x64.cc index 9eea3477a..b0f9847fa 100644 --- a/src/x64/stub-cache-x64.cc +++ b/src/x64/stub-cache-x64.cc @@ -82,7 +82,55 @@ static void ProbeTable(Isolate* isolate, // must always call a backup property check that is complete. // This function is safe to call if the receiver has fast properties. // Name must be a symbol and receiver must be a heap object. -MUST_USE_RESULT static MaybeObject* GenerateDictionaryNegativeLookup( +static void GenerateDictionaryNegativeLookup(MacroAssembler* masm, + Label* miss_label, + Register receiver, + Handle name, + Register r0, + Register r1) { + ASSERT(name->IsSymbol()); + Counters* counters = masm->isolate()->counters(); + __ IncrementCounter(counters->negative_lookups(), 1); + __ IncrementCounter(counters->negative_lookups_miss(), 1); + + __ movq(r0, FieldOperand(receiver, HeapObject::kMapOffset)); + + const int kInterceptorOrAccessCheckNeededMask = + (1 << Map::kHasNamedInterceptor) | (1 << Map::kIsAccessCheckNeeded); + + // Bail out if the receiver has a named interceptor or requires access checks. + __ testb(FieldOperand(r0, Map::kBitFieldOffset), + Immediate(kInterceptorOrAccessCheckNeededMask)); + __ j(not_zero, miss_label); + + // Check that receiver is a JSObject. + __ CmpInstanceType(r0, FIRST_SPEC_OBJECT_TYPE); + __ j(below, miss_label); + + // Load properties array. + Register properties = r0; + __ movq(properties, FieldOperand(receiver, JSObject::kPropertiesOffset)); + + // Check that the properties array is a dictionary. + __ CompareRoot(FieldOperand(properties, HeapObject::kMapOffset), + Heap::kHashTableMapRootIndex); + __ j(not_equal, miss_label); + + Label done; + StringDictionaryLookupStub::GenerateNegativeLookup(masm, + miss_label, + &done, + properties, + name, + r1); + __ bind(&done); + __ DecrementCounter(counters->negative_lookups_miss(), 1); +} + + +// TODO(kmillikin): Eliminate this function when the stub cache is fully +// handlified. +MUST_USE_RESULT static MaybeObject* TryGenerateDictionaryNegativeLookup( MacroAssembler* masm, Label* miss_label, Register receiver, @@ -118,7 +166,7 @@ MUST_USE_RESULT static MaybeObject* GenerateDictionaryNegativeLookup( __ j(not_equal, miss_label); Label done; - MaybeObject* result = StringDictionaryLookupStub::GenerateNegativeLookup( + MaybeObject* result = StringDictionaryLookupStub::TryGenerateNegativeLookup( masm, miss_label, &done, @@ -312,8 +360,10 @@ void StubCompiler::GenerateLoadFunctionPrototype(MacroAssembler* masm, // are loaded directly otherwise the property is loaded from the properties // fixed array. void StubCompiler::GenerateFastPropertyLoad(MacroAssembler* masm, - Register dst, Register src, - JSObject* holder, int index) { + Register dst, + Register src, + Handle holder, + int index) { // Adjust for the number of properties stored in the holder. index -= holder->map()->inobject_properties(); if (index < 0) { @@ -808,7 +858,24 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm, // Generate code to check that a global property cell is empty. Create // the property cell at compilation time if no cell exists for the // property. -MUST_USE_RESULT static MaybeObject* GenerateCheckPropertyCell( +static void GenerateCheckPropertyCell(MacroAssembler* masm, + Handle global, + Handle name, + Register scratch, + Label* miss) { + Handle cell = + GlobalObject::EnsurePropertyCell(global, name); + ASSERT(cell->value()->IsTheHole()); + __ Move(scratch, cell); + __ Cmp(FieldOperand(scratch, JSGlobalPropertyCell::kValueOffset), + masm->isolate()->factory()->the_hole_value()); + __ j(not_equal, miss); +} + + +// TODO(kmillikin): Eliminate this function when the stub cache is fully +// handlified. +MUST_USE_RESULT static MaybeObject* TryGenerateCheckPropertyCell( MacroAssembler* masm, GlobalObject* global, String* name, @@ -828,10 +895,172 @@ MUST_USE_RESULT static MaybeObject* GenerateCheckPropertyCell( } +// Calls GenerateCheckPropertyCell for each global object in the prototype chain +// from object to (but not including) holder. +static void GenerateCheckPropertyCells(MacroAssembler* masm, + Handle object, + Handle holder, + Handle name, + Register scratch, + Label* miss) { + Handle current = object; + while (!current.is_identical_to(holder)) { + if (current->IsGlobalObject()) { + GenerateCheckPropertyCell(masm, + Handle::cast(current), + name, + scratch, + miss); + } + current = Handle(JSObject::cast(current->GetPrototype())); + } +} + + +// TODO(kmillikin): Eliminate this function when the stub cache is fully +// handlified. +MUST_USE_RESULT static MaybeObject* TryGenerateCheckPropertyCells( + MacroAssembler* masm, + JSObject* object, + JSObject* holder, + String* name, + Register scratch, + Label* miss) { + JSObject* current = object; + while (current != holder) { + if (current->IsGlobalObject()) { + // Returns a cell or a failure. + MaybeObject* result = TryGenerateCheckPropertyCell( + masm, + GlobalObject::cast(current), + name, + scratch, + miss); + if (result->IsFailure()) return result; + } + ASSERT(current->IsJSObject()); + current = JSObject::cast(current->GetPrototype()); + } + return NULL; +} + + #undef __ #define __ ACCESS_MASM((masm())) +Register StubCompiler::CheckPrototypes(Handle object, + Register object_reg, + Handle holder, + Register holder_reg, + Register scratch1, + Register scratch2, + Handle name, + int save_at_depth, + Label* miss) { + // Make sure there's no overlap between holder and object registers. + ASSERT(!scratch1.is(object_reg) && !scratch1.is(holder_reg)); + ASSERT(!scratch2.is(object_reg) && !scratch2.is(holder_reg) + && !scratch2.is(scratch1)); + + // Keep track of the current object in register reg. On the first + // iteration, reg is an alias for object_reg, on later iterations, + // it is an alias for holder_reg. + Register reg = object_reg; + int depth = 0; + + if (save_at_depth == depth) { + __ movq(Operand(rsp, kPointerSize), object_reg); + } + + // Check the maps in the prototype chain. + // Traverse the prototype chain from the object and do map checks. + Handle current = object; + while (!current.is_identical_to(holder)) { + ++depth; + + // Only global objects and objects that do not require access + // checks are allowed in stubs. + ASSERT(current->IsJSGlobalProxy() || !current->IsAccessCheckNeeded()); + + Handle prototype(JSObject::cast(current->GetPrototype())); + if (!current->HasFastProperties() && + !current->IsJSGlobalObject() && + !current->IsJSGlobalProxy()) { + if (!name->IsSymbol()) { + name = factory()->LookupSymbol(name); + } + ASSERT(current->property_dictionary()->FindEntry(*name) == + StringDictionary::kNotFound); + + GenerateDictionaryNegativeLookup(masm(), miss, reg, name, + scratch1, scratch2); + + __ movq(scratch1, FieldOperand(reg, HeapObject::kMapOffset)); + reg = holder_reg; // From now on the object will be in holder_reg. + __ movq(reg, FieldOperand(scratch1, Map::kPrototypeOffset)); + } else { + bool in_new_space = heap()->InNewSpace(*prototype); + Handle current_map(current->map()); + if (in_new_space) { + // Save the map in scratch1 for later. + __ movq(scratch1, FieldOperand(reg, HeapObject::kMapOffset)); + __ Cmp(scratch1, current_map); + } else { + __ Cmp(FieldOperand(reg, HeapObject::kMapOffset), current_map); + } + // Branch on the result of the map check. + __ j(not_equal, miss); + // Check access rights to the global object. This has to happen after + // the map check so that we know that the object is actually a global + // object. + if (current->IsJSGlobalProxy()) { + __ CheckAccessGlobalProxy(reg, scratch2, miss); + } + reg = holder_reg; // From now on the object will be in holder_reg. + + if (in_new_space) { + // The prototype is in new space; we cannot store a reference to it + // in the code. Load it from the map. + __ movq(reg, FieldOperand(scratch1, Map::kPrototypeOffset)); + } else { + // The prototype is in old space; load it directly. + __ Move(reg, prototype); + } + } + + if (save_at_depth == depth) { + __ movq(Operand(rsp, kPointerSize), reg); + } + + // Go to the next object in the prototype chain. + current = prototype; + } + ASSERT(current.is_identical_to(holder)); + + // Log the check depth. + LOG(isolate(), IntEvent("check-maps-depth", depth + 1)); + + // Check the holder map. + __ Cmp(FieldOperand(reg, HeapObject::kMapOffset), Handle(holder->map())); + __ j(not_equal, miss); + + // Perform security check for access to the global object. + ASSERT(current->IsJSGlobalProxy() || !current->IsAccessCheckNeeded()); + if (current->IsJSGlobalProxy()) { + __ CheckAccessGlobalProxy(reg, scratch1, miss); + } + + // If we've skipped any global objects, it's not enough to verify that + // their maps haven't changed. We also need to check that the property + // cell for the property is still empty. + GenerateCheckPropertyCells(masm(), object, holder, name, scratch1, miss); + + // Return the register containing the holder. + return reg; +} + + Register StubCompiler::CheckPrototypes(JSObject* object, Register object_reg, JSObject* holder, @@ -882,12 +1111,13 @@ Register StubCompiler::CheckPrototypes(JSObject* object, ASSERT(current->property_dictionary()->FindEntry(name) == StringDictionary::kNotFound); - MaybeObject* negative_lookup = GenerateDictionaryNegativeLookup(masm(), - miss, - reg, - name, - scratch1, - scratch2); + MaybeObject* negative_lookup = + TryGenerateDictionaryNegativeLookup(masm(), + miss, + reg, + name, + scratch1, + scratch2); if (negative_lookup->IsFailure()) { set_failure(Failure::cast(negative_lookup)); return reg; @@ -960,21 +1190,13 @@ Register StubCompiler::CheckPrototypes(JSObject* object, // If we've skipped any global objects, it's not enough to verify // that their maps haven't changed. We also need to check that the // property cell for the property is still empty. - current = object; - while (current != holder) { - if (current->IsGlobalObject()) { - MaybeObject* cell = GenerateCheckPropertyCell(masm(), - GlobalObject::cast(current), - name, - scratch1, - miss); - if (cell->IsFailure()) { - set_failure(Failure::cast(cell)); - return reg; - } - } - current = JSObject::cast(current->GetPrototype()); - } + MaybeObject* result = TryGenerateCheckPropertyCells(masm(), + object, + holder, + name, + scratch1, + miss); + if (result->IsFailure()) set_failure(Failure::cast(result)); // Return the register containing the holder. return reg; @@ -999,7 +1221,7 @@ void StubCompiler::GenerateLoadField(JSObject* object, scratch1, scratch2, scratch3, name, miss); // Get the value from the properties. - GenerateFastPropertyLoad(masm(), rax, reg, holder, index); + GenerateFastPropertyLoad(masm(), rax, reg, Handle(holder), index); __ ret(0); } @@ -1198,7 +1420,8 @@ void StubCompiler::GenerateLoadInterceptor(JSObject* object, // We found FIELD property in prototype chain of interceptor's holder. // Retrieve a field from field's holder. GenerateFastPropertyLoad(masm(), rax, holder_reg, - lookup->holder(), lookup->GetFieldIndex()); + Handle(lookup->holder()), + lookup->GetFieldIndex()); __ ret(0); } else { // We found CALLBACKS property in prototype chain of interceptor's @@ -1244,9 +1467,9 @@ void StubCompiler::GenerateLoadInterceptor(JSObject* object, } -void CallStubCompiler::GenerateNameCheck(String* name, Label* miss) { +void CallStubCompiler::GenerateNameCheck(Handle name, Label* miss) { if (kind_ == Code::KEYED_CALL_IC) { - __ Cmp(rcx, Handle(name)); + __ Cmp(rcx, name); __ j(not_equal, miss); } } @@ -1305,7 +1528,18 @@ void CallStubCompiler::GenerateLoadFunctionFromCell(JSGlobalPropertyCell* cell, } -MaybeObject* CallStubCompiler::GenerateMissBranch() { +void CallStubCompiler::GenerateMissBranch() { + Handle code = + isolate()->stub_cache()->ComputeCallMiss(arguments().immediate(), + kind_, + extra_state_); + __ Jump(code, RelocInfo::CODE_TARGET); +} + + +// TODO(kmillikin): Eliminate this function when the stub cache is fully +// handlified. +MaybeObject* CallStubCompiler::TryGenerateMissBranch() { MaybeObject* maybe_obj = isolate()->stub_cache()->TryComputeCallMiss(arguments().immediate(), kind_, @@ -1317,10 +1551,10 @@ MaybeObject* CallStubCompiler::GenerateMissBranch() { } -MaybeObject* CallStubCompiler::CompileCallField(JSObject* object, - JSObject* holder, +Handle CallStubCompiler::CompileCallField(Handle object, + Handle holder, int index, - String* name) { + Handle name) { // ----------- S t a t e ------------- // rcx : function name // rsp[0] : return address @@ -1368,8 +1602,7 @@ MaybeObject* CallStubCompiler::CompileCallField(JSObject* object, // Handle call cache miss. __ bind(&miss); - MaybeObject* maybe_result = GenerateMissBranch(); - if (maybe_result->IsFailure()) return maybe_result; + GenerateMissBranch(); // Return the generated code. return GetCode(FIELD, name); @@ -1394,7 +1627,7 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, Label miss; - GenerateNameCheck(name, &miss); + GenerateNameCheck(Handle(name), &miss); // Get the receiver from the stack. const int argc = arguments().immediate(); @@ -1553,11 +1786,11 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, } __ bind(&miss); - MaybeObject* maybe_result = GenerateMissBranch(); + MaybeObject* maybe_result = TryGenerateMissBranch(); if (maybe_result->IsFailure()) return maybe_result; // Return the generated code. - return GetCode(function); + return TryGetCode(function); } @@ -1579,7 +1812,7 @@ MaybeObject* CallStubCompiler::CompileArrayPopCall(Object* object, Label miss, return_undefined, call_builtin; - GenerateNameCheck(name, &miss); + GenerateNameCheck(Handle(name), &miss); // Get the receiver from the stack. const int argc = arguments().immediate(); @@ -1636,11 +1869,11 @@ MaybeObject* CallStubCompiler::CompileArrayPopCall(Object* object, 1); __ bind(&miss); - MaybeObject* maybe_result = GenerateMissBranch(); + MaybeObject* maybe_result = TryGenerateMissBranch(); if (maybe_result->IsFailure()) return maybe_result; // Return the generated code. - return GetCode(function); + return TryGetCode(function); } @@ -1674,7 +1907,7 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( index_out_of_range_label = &miss; } - GenerateNameCheck(name, &name_miss); + GenerateNameCheck(Handle(name), &name_miss); // Check that the maps starting from the prototype haven't changed. GenerateDirectLoadGlobalFunctionPrototype(masm(), @@ -1720,11 +1953,11 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( // Restore function name in rcx. __ Move(rcx, Handle(name)); __ bind(&name_miss); - MaybeObject* maybe_result = GenerateMissBranch(); + MaybeObject* maybe_result = TryGenerateMissBranch(); if (maybe_result->IsFailure()) return maybe_result; // Return the generated code. - return GetCode(function); + return TryGetCode(function); } @@ -1758,7 +1991,7 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall( index_out_of_range_label = &miss; } - GenerateNameCheck(name, &name_miss); + GenerateNameCheck(Handle(name), &name_miss); // Check that the maps starting from the prototype haven't changed. GenerateDirectLoadGlobalFunctionPrototype(masm(), @@ -1806,11 +2039,11 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall( // Restore function name in rcx. __ Move(rcx, Handle(name)); __ bind(&name_miss); - MaybeObject* maybe_result = GenerateMissBranch(); + MaybeObject* maybe_result = TryGenerateMissBranch(); if (maybe_result->IsFailure()) return maybe_result; // Return the generated code. - return GetCode(function); + return TryGetCode(function); } @@ -1835,7 +2068,7 @@ MaybeObject* CallStubCompiler::CompileStringFromCharCodeCall( if (!object->IsJSObject() || argc != 1) return heap()->undefined_value(); Label miss; - GenerateNameCheck(name, &miss); + GenerateNameCheck(Handle(name), &miss); if (cell == NULL) { __ movq(rdx, Operand(rsp, 2 * kPointerSize)); @@ -1879,11 +2112,11 @@ MaybeObject* CallStubCompiler::CompileStringFromCharCodeCall( __ bind(&miss); // rcx: function name. - MaybeObject* maybe_result = GenerateMissBranch(); + MaybeObject* maybe_result = TryGenerateMissBranch(); if (maybe_result->IsFailure()) return maybe_result; // Return the generated code. - return (cell == NULL) ? GetCode(function) : GetCode(NORMAL, name); + return (cell == NULL) ? TryGetCode(function) : TryGetCode(NORMAL, name); } @@ -1917,7 +2150,7 @@ MaybeObject* CallStubCompiler::CompileMathAbsCall(Object* object, if (!object->IsJSObject() || argc != 1) return heap()->undefined_value(); Label miss; - GenerateNameCheck(name, &miss); + GenerateNameCheck(Handle(name), &miss); if (cell == NULL) { __ movq(rdx, Operand(rsp, 2 * kPointerSize)); @@ -1996,11 +2229,11 @@ MaybeObject* CallStubCompiler::CompileMathAbsCall(Object* object, __ bind(&miss); // rcx: function name. - MaybeObject* maybe_result = GenerateMissBranch(); + MaybeObject* maybe_result = TryGenerateMissBranch(); if (maybe_result->IsFailure()) return maybe_result; // Return the generated code. - return (cell == NULL) ? GetCode(function) : GetCode(NORMAL, name); + return (cell == NULL) ? TryGetCode(function) : TryGetCode(NORMAL, name); } @@ -2023,7 +2256,7 @@ MaybeObject* CallStubCompiler::CompileFastApiCall( Label miss, miss_before_stack_reserved; - GenerateNameCheck(name, &miss_before_stack_reserved); + GenerateNameCheck(Handle(name), &miss_before_stack_reserved); // Get the receiver from the stack. const int argc = arguments().immediate(); @@ -2055,11 +2288,11 @@ MaybeObject* CallStubCompiler::CompileFastApiCall( __ addq(rsp, Immediate(kFastApiCallArguments * kPointerSize)); __ bind(&miss_before_stack_reserved); - MaybeObject* maybe_result = GenerateMissBranch(); + MaybeObject* maybe_result = TryGenerateMissBranch(); if (maybe_result->IsFailure()) return maybe_result; // Return the generated code. - return GetCode(function); + return TryGetCode(function); } @@ -2089,7 +2322,7 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object, Label miss; - GenerateNameCheck(name, &miss); + GenerateNameCheck(Handle(name), &miss); // Get the receiver from the stack. const int argc = arguments().immediate(); @@ -2194,11 +2427,11 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object, // Handle call cache miss. __ bind(&miss); - MaybeObject* maybe_result = GenerateMissBranch(); + MaybeObject* maybe_result = TryGenerateMissBranch(); if (maybe_result->IsFailure()) return maybe_result; // Return the generated code. - return GetCode(function); + return TryGetCode(function); } @@ -2216,7 +2449,7 @@ MaybeObject* CallStubCompiler::CompileCallInterceptor(JSObject* object, // ----------------------------------- Label miss; - GenerateNameCheck(name, &miss); + GenerateNameCheck(Handle(name), &miss); // Get the number of arguments. const int argc = arguments().immediate(); @@ -2265,11 +2498,11 @@ MaybeObject* CallStubCompiler::CompileCallInterceptor(JSObject* object, // Handle load cache miss. __ bind(&miss); - MaybeObject* maybe_result = GenerateMissBranch(); + MaybeObject* maybe_result = TryGenerateMissBranch(); if (maybe_result->IsFailure()) return maybe_result; // Return the generated code. - return GetCode(INTERCEPTOR, name); + return TryGetCode(INTERCEPTOR, name); } @@ -2299,7 +2532,7 @@ MaybeObject* CallStubCompiler::CompileCallGlobal(JSObject* object, Label miss; - GenerateNameCheck(name, &miss); + GenerateNameCheck(Handle(name), &miss); // Get the number of arguments. const int argc = arguments().immediate(); @@ -2334,11 +2567,11 @@ MaybeObject* CallStubCompiler::CompileCallGlobal(JSObject* object, // Handle call cache miss. __ bind(&miss); __ IncrementCounter(counters->call_global_inline_miss(), 1); - MaybeObject* maybe_result = GenerateMissBranch(); + MaybeObject* maybe_result = TryGenerateMissBranch(); if (maybe_result->IsFailure()) return maybe_result; // Return the generated code. - return GetCode(NORMAL, name); + return TryGetCode(NORMAL, name); } @@ -2658,11 +2891,11 @@ MaybeObject* LoadStubCompiler::CompileLoadNonexistent(String* name, // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last->IsGlobalObject()) { - MaybeObject* cell = GenerateCheckPropertyCell(masm(), - GlobalObject::cast(last), - name, - rdx, - &miss); + MaybeObject* cell = TryGenerateCheckPropertyCell(masm(), + GlobalObject::cast(last), + name, + rdx, + &miss); if (cell->IsFailure()) { miss.Unuse(); return cell; -- 2.34.1