| OLD | NEW | 
 | (Empty) | 
|     1 // Copyright 2013 the V8 project authors. All rights reserved. |  | 
|     2 // Use of this source code is governed by a BSD-style license that can be |  | 
|     3 // found in the LICENSE file. |  | 
|     4  |  | 
|     5 #include "src/v8.h" |  | 
|     6  |  | 
|     7 #if V8_TARGET_ARCH_ARM64 |  | 
|     8  |  | 
|     9 #include "src/arm64/assembler-arm64.h" |  | 
|    10 #include "src/code-stubs.h" |  | 
|    11 #include "src/codegen.h" |  | 
|    12 #include "src/disasm.h" |  | 
|    13 #include "src/ic-inl.h" |  | 
|    14 #include "src/runtime.h" |  | 
|    15 #include "src/stub-cache.h" |  | 
|    16  |  | 
|    17 namespace v8 { |  | 
|    18 namespace internal { |  | 
|    19  |  | 
|    20  |  | 
|    21 #define __ ACCESS_MASM(masm) |  | 
|    22  |  | 
|    23  |  | 
|    24 // "type" holds an instance type on entry and is not clobbered. |  | 
|    25 // Generated code branch on "global_object" if type is any kind of global |  | 
|    26 // JS object. |  | 
|    27 static void GenerateGlobalInstanceTypeCheck(MacroAssembler* masm, |  | 
|    28                                             Register type, |  | 
|    29                                             Label* global_object) { |  | 
|    30   __ Cmp(type, JS_GLOBAL_OBJECT_TYPE); |  | 
|    31   __ Ccmp(type, JS_BUILTINS_OBJECT_TYPE, ZFlag, ne); |  | 
|    32   __ Ccmp(type, JS_GLOBAL_PROXY_TYPE, ZFlag, ne); |  | 
|    33   __ B(eq, global_object); |  | 
|    34 } |  | 
|    35  |  | 
|    36  |  | 
|    37 // Helper function used from LoadIC GenerateNormal. |  | 
|    38 // |  | 
|    39 // elements: Property dictionary. It is not clobbered if a jump to the miss |  | 
|    40 //           label is done. |  | 
|    41 // name:     Property name. It is not clobbered if a jump to the miss label is |  | 
|    42 //           done |  | 
|    43 // result:   Register for the result. It is only updated if a jump to the miss |  | 
|    44 //           label is not done. |  | 
|    45 // The scratch registers need to be different from elements, name and result. |  | 
|    46 // The generated code assumes that the receiver has slow properties, |  | 
|    47 // is not a global object and does not have interceptors. |  | 
|    48 static void GenerateDictionaryLoad(MacroAssembler* masm, |  | 
|    49                                    Label* miss, |  | 
|    50                                    Register elements, |  | 
|    51                                    Register name, |  | 
|    52                                    Register result, |  | 
|    53                                    Register scratch1, |  | 
|    54                                    Register scratch2) { |  | 
|    55   DCHECK(!AreAliased(elements, name, scratch1, scratch2)); |  | 
|    56   DCHECK(!AreAliased(result, scratch1, scratch2)); |  | 
|    57  |  | 
|    58   Label done; |  | 
|    59  |  | 
|    60   // Probe the dictionary. |  | 
|    61   NameDictionaryLookupStub::GeneratePositiveLookup(masm, |  | 
|    62                                                    miss, |  | 
|    63                                                    &done, |  | 
|    64                                                    elements, |  | 
|    65                                                    name, |  | 
|    66                                                    scratch1, |  | 
|    67                                                    scratch2); |  | 
|    68  |  | 
|    69   // If probing finds an entry check that the value is a normal property. |  | 
|    70   __ Bind(&done); |  | 
|    71  |  | 
|    72   static const int kElementsStartOffset = NameDictionary::kHeaderSize + |  | 
|    73       NameDictionary::kElementsStartIndex * kPointerSize; |  | 
|    74   static const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize; |  | 
|    75   __ Ldr(scratch1, FieldMemOperand(scratch2, kDetailsOffset)); |  | 
|    76   __ Tst(scratch1, Smi::FromInt(PropertyDetails::TypeField::kMask)); |  | 
|    77   __ B(ne, miss); |  | 
|    78  |  | 
|    79   // Get the value at the masked, scaled index and return. |  | 
|    80   __ Ldr(result, |  | 
|    81          FieldMemOperand(scratch2, kElementsStartOffset + 1 * kPointerSize)); |  | 
|    82 } |  | 
|    83  |  | 
|    84  |  | 
|    85 // Helper function used from StoreIC::GenerateNormal. |  | 
|    86 // |  | 
|    87 // elements: Property dictionary. It is not clobbered if a jump to the miss |  | 
|    88 //           label is done. |  | 
|    89 // name:     Property name. It is not clobbered if a jump to the miss label is |  | 
|    90 //           done |  | 
|    91 // value:    The value to store (never clobbered). |  | 
|    92 // |  | 
|    93 // The generated code assumes that the receiver has slow properties, |  | 
|    94 // is not a global object and does not have interceptors. |  | 
|    95 static void GenerateDictionaryStore(MacroAssembler* masm, |  | 
|    96                                     Label* miss, |  | 
|    97                                     Register elements, |  | 
|    98                                     Register name, |  | 
|    99                                     Register value, |  | 
|   100                                     Register scratch1, |  | 
|   101                                     Register scratch2) { |  | 
|   102   DCHECK(!AreAliased(elements, name, value, scratch1, scratch2)); |  | 
|   103  |  | 
|   104   Label done; |  | 
|   105  |  | 
|   106   // Probe the dictionary. |  | 
|   107   NameDictionaryLookupStub::GeneratePositiveLookup(masm, |  | 
|   108                                                    miss, |  | 
|   109                                                    &done, |  | 
|   110                                                    elements, |  | 
|   111                                                    name, |  | 
|   112                                                    scratch1, |  | 
|   113                                                    scratch2); |  | 
|   114  |  | 
|   115   // If probing finds an entry in the dictionary check that the value |  | 
|   116   // is a normal property that is not read only. |  | 
|   117   __ Bind(&done); |  | 
|   118  |  | 
|   119   static const int kElementsStartOffset = NameDictionary::kHeaderSize + |  | 
|   120       NameDictionary::kElementsStartIndex * kPointerSize; |  | 
|   121   static const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize; |  | 
|   122   static const int kTypeAndReadOnlyMask = |  | 
|   123       PropertyDetails::TypeField::kMask | |  | 
|   124       PropertyDetails::AttributesField::encode(READ_ONLY); |  | 
|   125   __ Ldrsw(scratch1, UntagSmiFieldMemOperand(scratch2, kDetailsOffset)); |  | 
|   126   __ Tst(scratch1, kTypeAndReadOnlyMask); |  | 
|   127   __ B(ne, miss); |  | 
|   128  |  | 
|   129   // Store the value at the masked, scaled index and return. |  | 
|   130   static const int kValueOffset = kElementsStartOffset + kPointerSize; |  | 
|   131   __ Add(scratch2, scratch2, kValueOffset - kHeapObjectTag); |  | 
|   132   __ Str(value, MemOperand(scratch2)); |  | 
|   133  |  | 
|   134   // Update the write barrier. Make sure not to clobber the value. |  | 
|   135   __ Mov(scratch1, value); |  | 
|   136   __ RecordWrite( |  | 
|   137       elements, scratch2, scratch1, kLRHasNotBeenSaved, kDontSaveFPRegs); |  | 
|   138 } |  | 
|   139  |  | 
|   140  |  | 
|   141 // Checks the receiver for special cases (value type, slow case bits). |  | 
|   142 // Falls through for regular JS object and return the map of the |  | 
|   143 // receiver in 'map_scratch' if the receiver is not a SMI. |  | 
|   144 static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm, |  | 
|   145                                            Register receiver, |  | 
|   146                                            Register map_scratch, |  | 
|   147                                            Register scratch, |  | 
|   148                                            int interceptor_bit, |  | 
|   149                                            Label* slow) { |  | 
|   150   DCHECK(!AreAliased(map_scratch, scratch)); |  | 
|   151  |  | 
|   152   // Check that the object isn't a smi. |  | 
|   153   __ JumpIfSmi(receiver, slow); |  | 
|   154   // Get the map of the receiver. |  | 
|   155   __ Ldr(map_scratch, FieldMemOperand(receiver, HeapObject::kMapOffset)); |  | 
|   156   // Check bit field. |  | 
|   157   __ Ldrb(scratch, FieldMemOperand(map_scratch, Map::kBitFieldOffset)); |  | 
|   158   __ Tbnz(scratch, Map::kIsAccessCheckNeeded, slow); |  | 
|   159   __ Tbnz(scratch, interceptor_bit, slow); |  | 
|   160  |  | 
|   161   // Check that the object is some kind of JS object EXCEPT JS Value type. |  | 
|   162   // In the case that the object is a value-wrapper object, we enter the |  | 
|   163   // runtime system to make sure that indexing into string objects work |  | 
|   164   // as intended. |  | 
|   165   STATIC_ASSERT(JS_OBJECT_TYPE > JS_VALUE_TYPE); |  | 
|   166   __ Ldrb(scratch, FieldMemOperand(map_scratch, Map::kInstanceTypeOffset)); |  | 
|   167   __ Cmp(scratch, JS_OBJECT_TYPE); |  | 
|   168   __ B(lt, slow); |  | 
|   169 } |  | 
|   170  |  | 
|   171  |  | 
|   172 // Loads an indexed element from a fast case array. |  | 
|   173 // If not_fast_array is NULL, doesn't perform the elements map check. |  | 
|   174 // |  | 
|   175 // receiver     - holds the receiver on entry. |  | 
|   176 //                Unchanged unless 'result' is the same register. |  | 
|   177 // |  | 
|   178 // key          - holds the smi key on entry. |  | 
|   179 //                Unchanged unless 'result' is the same register. |  | 
|   180 // |  | 
|   181 // elements     - holds the elements of the receiver on exit. |  | 
|   182 // |  | 
|   183 // elements_map - holds the elements map on exit if the not_fast_array branch is |  | 
|   184 //                taken. Otherwise, this is used as a scratch register. |  | 
|   185 // |  | 
|   186 // result       - holds the result on exit if the load succeeded. |  | 
|   187 //                Allowed to be the the same as 'receiver' or 'key'. |  | 
|   188 //                Unchanged on bailout so 'receiver' and 'key' can be safely |  | 
|   189 //                used by further computation. |  | 
|   190 static void GenerateFastArrayLoad(MacroAssembler* masm, |  | 
|   191                                   Register receiver, |  | 
|   192                                   Register key, |  | 
|   193                                   Register elements, |  | 
|   194                                   Register elements_map, |  | 
|   195                                   Register scratch2, |  | 
|   196                                   Register result, |  | 
|   197                                   Label* not_fast_array, |  | 
|   198                                   Label* slow) { |  | 
|   199   DCHECK(!AreAliased(receiver, key, elements, elements_map, scratch2)); |  | 
|   200  |  | 
|   201   // Check for fast array. |  | 
|   202   __ Ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset)); |  | 
|   203   if (not_fast_array != NULL) { |  | 
|   204     // Check that the object is in fast mode and writable. |  | 
|   205     __ Ldr(elements_map, FieldMemOperand(elements, HeapObject::kMapOffset)); |  | 
|   206     __ JumpIfNotRoot(elements_map, Heap::kFixedArrayMapRootIndex, |  | 
|   207                      not_fast_array); |  | 
|   208   } else { |  | 
|   209     __ AssertFastElements(elements); |  | 
|   210   } |  | 
|   211  |  | 
|   212   // The elements_map register is only used for the not_fast_array path, which |  | 
|   213   // was handled above. From this point onward it is a scratch register. |  | 
|   214   Register scratch1 = elements_map; |  | 
|   215  |  | 
|   216   // Check that the key (index) is within bounds. |  | 
|   217   __ Ldr(scratch1, FieldMemOperand(elements, FixedArray::kLengthOffset)); |  | 
|   218   __ Cmp(key, scratch1); |  | 
|   219   __ B(hs, slow); |  | 
|   220  |  | 
|   221   // Fast case: Do the load. |  | 
|   222   __ Add(scratch1, elements, FixedArray::kHeaderSize - kHeapObjectTag); |  | 
|   223   __ SmiUntag(scratch2, key); |  | 
|   224   __ Ldr(scratch2, MemOperand(scratch1, scratch2, LSL, kPointerSizeLog2)); |  | 
|   225  |  | 
|   226   // In case the loaded value is the_hole we have to consult GetProperty |  | 
|   227   // to ensure the prototype chain is searched. |  | 
|   228   __ JumpIfRoot(scratch2, Heap::kTheHoleValueRootIndex, slow); |  | 
|   229  |  | 
|   230   // Move the value to the result register. |  | 
|   231   // 'result' can alias with 'receiver' or 'key' but these two must be |  | 
|   232   // preserved if we jump to 'slow'. |  | 
|   233   __ Mov(result, scratch2); |  | 
|   234 } |  | 
|   235  |  | 
|   236  |  | 
|   237 // Checks whether a key is an array index string or a unique name. |  | 
|   238 // Falls through if a key is a unique name. |  | 
|   239 // The map of the key is returned in 'map_scratch'. |  | 
|   240 // If the jump to 'index_string' is done the hash of the key is left |  | 
|   241 // in 'hash_scratch'. |  | 
|   242 static void GenerateKeyNameCheck(MacroAssembler* masm, |  | 
|   243                                  Register key, |  | 
|   244                                  Register map_scratch, |  | 
|   245                                  Register hash_scratch, |  | 
|   246                                  Label* index_string, |  | 
|   247                                  Label* not_unique) { |  | 
|   248   DCHECK(!AreAliased(key, map_scratch, hash_scratch)); |  | 
|   249  |  | 
|   250   // Is the key a name? |  | 
|   251   Label unique; |  | 
|   252   __ JumpIfObjectType(key, map_scratch, hash_scratch, LAST_UNIQUE_NAME_TYPE, |  | 
|   253                       not_unique, hi); |  | 
|   254   STATIC_ASSERT(LAST_UNIQUE_NAME_TYPE == FIRST_NONSTRING_TYPE); |  | 
|   255   __ B(eq, &unique); |  | 
|   256  |  | 
|   257   // Is the string an array index with cached numeric value? |  | 
|   258   __ Ldr(hash_scratch.W(), FieldMemOperand(key, Name::kHashFieldOffset)); |  | 
|   259   __ TestAndBranchIfAllClear(hash_scratch, |  | 
|   260                              Name::kContainsCachedArrayIndexMask, |  | 
|   261                              index_string); |  | 
|   262  |  | 
|   263   // Is the string internalized? We know it's a string, so a single bit test is |  | 
|   264   // enough. |  | 
|   265   __ Ldrb(hash_scratch, FieldMemOperand(map_scratch, Map::kInstanceTypeOffset)); |  | 
|   266   STATIC_ASSERT(kInternalizedTag == 0); |  | 
|   267   __ TestAndBranchIfAnySet(hash_scratch, kIsNotInternalizedMask, not_unique); |  | 
|   268  |  | 
|   269   __ Bind(&unique); |  | 
|   270   // Fall through if the key is a unique name. |  | 
|   271 } |  | 
|   272  |  | 
|   273  |  | 
|   274 // Neither 'object' nor 'key' are modified by this function. |  | 
|   275 // |  | 
|   276 // If the 'unmapped_case' or 'slow_case' exit is taken, the 'map' register is |  | 
|   277 // left with the object's elements map. Otherwise, it is used as a scratch |  | 
|   278 // register. |  | 
|   279 static MemOperand GenerateMappedArgumentsLookup(MacroAssembler* masm, |  | 
|   280                                                 Register object, |  | 
|   281                                                 Register key, |  | 
|   282                                                 Register map, |  | 
|   283                                                 Register scratch1, |  | 
|   284                                                 Register scratch2, |  | 
|   285                                                 Label* unmapped_case, |  | 
|   286                                                 Label* slow_case) { |  | 
|   287   DCHECK(!AreAliased(object, key, map, scratch1, scratch2)); |  | 
|   288  |  | 
|   289   Heap* heap = masm->isolate()->heap(); |  | 
|   290  |  | 
|   291   // Check that the receiver is a JSObject. Because of the elements |  | 
|   292   // map check later, we do not need to check for interceptors or |  | 
|   293   // whether it requires access checks. |  | 
|   294   __ JumpIfSmi(object, slow_case); |  | 
|   295   // Check that the object is some kind of JSObject. |  | 
|   296   __ JumpIfObjectType(object, map, scratch1, FIRST_JS_RECEIVER_TYPE, |  | 
|   297                       slow_case, lt); |  | 
|   298  |  | 
|   299   // Check that the key is a positive smi. |  | 
|   300   __ JumpIfNotSmi(key, slow_case); |  | 
|   301   __ Tbnz(key, kXSignBit, slow_case); |  | 
|   302  |  | 
|   303   // Load the elements object and check its map. |  | 
|   304   Handle<Map> arguments_map(heap->sloppy_arguments_elements_map()); |  | 
|   305   __ Ldr(map, FieldMemOperand(object, JSObject::kElementsOffset)); |  | 
|   306   __ CheckMap(map, scratch1, arguments_map, slow_case, DONT_DO_SMI_CHECK); |  | 
|   307  |  | 
|   308   // Check if element is in the range of mapped arguments. If not, jump |  | 
|   309   // to the unmapped lookup. |  | 
|   310   __ Ldr(scratch1, FieldMemOperand(map, FixedArray::kLengthOffset)); |  | 
|   311   __ Sub(scratch1, scratch1, Smi::FromInt(2)); |  | 
|   312   __ Cmp(key, scratch1); |  | 
|   313   __ B(hs, unmapped_case); |  | 
|   314  |  | 
|   315   // Load element index and check whether it is the hole. |  | 
|   316   static const int offset = |  | 
|   317       FixedArray::kHeaderSize + 2 * kPointerSize - kHeapObjectTag; |  | 
|   318  |  | 
|   319   __ Add(scratch1, map, offset); |  | 
|   320   __ SmiUntag(scratch2, key); |  | 
|   321   __ Ldr(scratch1, MemOperand(scratch1, scratch2, LSL, kPointerSizeLog2)); |  | 
|   322   __ JumpIfRoot(scratch1, Heap::kTheHoleValueRootIndex, unmapped_case); |  | 
|   323  |  | 
|   324   // Load value from context and return it. |  | 
|   325   __ Ldr(scratch2, FieldMemOperand(map, FixedArray::kHeaderSize)); |  | 
|   326   __ SmiUntag(scratch1); |  | 
|   327   __ Lsl(scratch1, scratch1, kPointerSizeLog2); |  | 
|   328   __ Add(scratch1, scratch1, Context::kHeaderSize - kHeapObjectTag); |  | 
|   329   // The base of the result (scratch2) is passed to RecordWrite in |  | 
|   330   // KeyedStoreIC::GenerateSloppyArguments and it must be a HeapObject. |  | 
|   331   return MemOperand(scratch2, scratch1); |  | 
|   332 } |  | 
|   333  |  | 
|   334  |  | 
|   335 // The 'parameter_map' register must be loaded with the parameter map of the |  | 
|   336 // arguments object and is overwritten. |  | 
|   337 static MemOperand GenerateUnmappedArgumentsLookup(MacroAssembler* masm, |  | 
|   338                                                   Register key, |  | 
|   339                                                   Register parameter_map, |  | 
|   340                                                   Register scratch, |  | 
|   341                                                   Label* slow_case) { |  | 
|   342   DCHECK(!AreAliased(key, parameter_map, scratch)); |  | 
|   343  |  | 
|   344   // Element is in arguments backing store, which is referenced by the |  | 
|   345   // second element of the parameter_map. |  | 
|   346   const int kBackingStoreOffset = FixedArray::kHeaderSize + kPointerSize; |  | 
|   347   Register backing_store = parameter_map; |  | 
|   348   __ Ldr(backing_store, FieldMemOperand(parameter_map, kBackingStoreOffset)); |  | 
|   349   Handle<Map> fixed_array_map(masm->isolate()->heap()->fixed_array_map()); |  | 
|   350   __ CheckMap( |  | 
|   351       backing_store, scratch, fixed_array_map, slow_case, DONT_DO_SMI_CHECK); |  | 
|   352   __ Ldr(scratch, FieldMemOperand(backing_store, FixedArray::kLengthOffset)); |  | 
|   353   __ Cmp(key, scratch); |  | 
|   354   __ B(hs, slow_case); |  | 
|   355  |  | 
|   356   __ Add(backing_store, |  | 
|   357          backing_store, |  | 
|   358          FixedArray::kHeaderSize - kHeapObjectTag); |  | 
|   359   __ SmiUntag(scratch, key); |  | 
|   360   return MemOperand(backing_store, scratch, LSL, kPointerSizeLog2); |  | 
|   361 } |  | 
|   362  |  | 
|   363  |  | 
|   364 void LoadIC::GenerateMegamorphic(MacroAssembler* masm) { |  | 
|   365   // The return address is in lr. |  | 
|   366   Register receiver = ReceiverRegister(); |  | 
|   367   Register name = NameRegister(); |  | 
|   368   DCHECK(receiver.is(x1)); |  | 
|   369   DCHECK(name.is(x2)); |  | 
|   370  |  | 
|   371   // Probe the stub cache. |  | 
|   372   Code::Flags flags = Code::RemoveTypeAndHolderFromFlags( |  | 
|   373       Code::ComputeHandlerFlags(Code::LOAD_IC)); |  | 
|   374   masm->isolate()->stub_cache()->GenerateProbe( |  | 
|   375       masm, flags, receiver, name, x3, x4, x5, x6); |  | 
|   376  |  | 
|   377   // Cache miss: Jump to runtime. |  | 
|   378   GenerateMiss(masm); |  | 
|   379 } |  | 
|   380  |  | 
|   381  |  | 
|   382 void LoadIC::GenerateNormal(MacroAssembler* masm) { |  | 
|   383   Register dictionary = x0; |  | 
|   384   DCHECK(!dictionary.is(ReceiverRegister())); |  | 
|   385   DCHECK(!dictionary.is(NameRegister())); |  | 
|   386   Label slow; |  | 
|   387  |  | 
|   388   __ Ldr(dictionary, |  | 
|   389          FieldMemOperand(ReceiverRegister(), JSObject::kPropertiesOffset)); |  | 
|   390   GenerateDictionaryLoad(masm, &slow, dictionary, NameRegister(), x0, x3, x4); |  | 
|   391   __ Ret(); |  | 
|   392  |  | 
|   393   // Dictionary load failed, go slow (but don't miss). |  | 
|   394   __ Bind(&slow); |  | 
|   395   GenerateRuntimeGetProperty(masm); |  | 
|   396 } |  | 
|   397  |  | 
|   398  |  | 
|   399 void LoadIC::GenerateMiss(MacroAssembler* masm) { |  | 
|   400   // The return address is in lr. |  | 
|   401   Isolate* isolate = masm->isolate(); |  | 
|   402   ASM_LOCATION("LoadIC::GenerateMiss"); |  | 
|   403  |  | 
|   404   __ IncrementCounter(isolate->counters()->load_miss(), 1, x3, x4); |  | 
|   405  |  | 
|   406   // Perform tail call to the entry. |  | 
|   407   __ Push(ReceiverRegister(), NameRegister()); |  | 
|   408   ExternalReference ref = |  | 
|   409       ExternalReference(IC_Utility(kLoadIC_Miss), isolate); |  | 
|   410   __ TailCallExternalReference(ref, 2, 1); |  | 
|   411 } |  | 
|   412  |  | 
|   413  |  | 
|   414 void LoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) { |  | 
|   415   // The return address is in lr. |  | 
|   416   __ Push(ReceiverRegister(), NameRegister()); |  | 
|   417   __ TailCallRuntime(Runtime::kGetProperty, 2, 1); |  | 
|   418 } |  | 
|   419  |  | 
|   420  |  | 
|   421 void KeyedLoadIC::GenerateSloppyArguments(MacroAssembler* masm) { |  | 
|   422   // The return address is in lr. |  | 
|   423   Register result = x0; |  | 
|   424   Register receiver = ReceiverRegister(); |  | 
|   425   Register key = NameRegister(); |  | 
|   426   DCHECK(receiver.is(x1)); |  | 
|   427   DCHECK(key.is(x2)); |  | 
|   428  |  | 
|   429   Label miss, unmapped; |  | 
|   430  |  | 
|   431   Register map_scratch = x0; |  | 
|   432   MemOperand mapped_location = GenerateMappedArgumentsLookup( |  | 
|   433       masm, receiver, key, map_scratch, x3, x4, &unmapped, &miss); |  | 
|   434   __ Ldr(result, mapped_location); |  | 
|   435   __ Ret(); |  | 
|   436  |  | 
|   437   __ Bind(&unmapped); |  | 
|   438   // Parameter map is left in map_scratch when a jump on unmapped is done. |  | 
|   439   MemOperand unmapped_location = |  | 
|   440       GenerateUnmappedArgumentsLookup(masm, key, map_scratch, x3, &miss); |  | 
|   441   __ Ldr(result, unmapped_location); |  | 
|   442   __ JumpIfRoot(result, Heap::kTheHoleValueRootIndex, &miss); |  | 
|   443   __ Ret(); |  | 
|   444  |  | 
|   445   __ Bind(&miss); |  | 
|   446   GenerateMiss(masm); |  | 
|   447 } |  | 
|   448  |  | 
|   449  |  | 
|   450 void KeyedStoreIC::GenerateSloppyArguments(MacroAssembler* masm) { |  | 
|   451   ASM_LOCATION("KeyedStoreIC::GenerateSloppyArguments"); |  | 
|   452   Label slow, notin; |  | 
|   453   Register value = ValueRegister(); |  | 
|   454   Register key = NameRegister(); |  | 
|   455   Register receiver = ReceiverRegister(); |  | 
|   456   DCHECK(receiver.is(x1)); |  | 
|   457   DCHECK(key.is(x2)); |  | 
|   458   DCHECK(value.is(x0)); |  | 
|   459  |  | 
|   460   Register map = x3; |  | 
|   461  |  | 
|   462   // These registers are used by GenerateMappedArgumentsLookup to build a |  | 
|   463   // MemOperand. They are live for as long as the MemOperand is live. |  | 
|   464   Register mapped1 = x4; |  | 
|   465   Register mapped2 = x5; |  | 
|   466  |  | 
|   467   MemOperand mapped = |  | 
|   468       GenerateMappedArgumentsLookup(masm, receiver, key, map, |  | 
|   469                                     mapped1, mapped2, |  | 
|   470                                     ¬in, &slow); |  | 
|   471   Operand mapped_offset = mapped.OffsetAsOperand(); |  | 
|   472   __ Str(value, mapped); |  | 
|   473   __ Add(x10, mapped.base(), mapped_offset); |  | 
|   474   __ Mov(x11, value); |  | 
|   475   __ RecordWrite(mapped.base(), x10, x11, kLRHasNotBeenSaved, kDontSaveFPRegs); |  | 
|   476   __ Ret(); |  | 
|   477  |  | 
|   478   __ Bind(¬in); |  | 
|   479  |  | 
|   480   // These registers are used by GenerateMappedArgumentsLookup to build a |  | 
|   481   // MemOperand. They are live for as long as the MemOperand is live. |  | 
|   482   Register unmapped1 = map;   // This is assumed to alias 'map'. |  | 
|   483   Register unmapped2 = x4; |  | 
|   484   MemOperand unmapped = |  | 
|   485       GenerateUnmappedArgumentsLookup(masm, key, unmapped1, unmapped2, &slow); |  | 
|   486   Operand unmapped_offset = unmapped.OffsetAsOperand(); |  | 
|   487   __ Str(value, unmapped); |  | 
|   488   __ Add(x10, unmapped.base(), unmapped_offset); |  | 
|   489   __ Mov(x11, value); |  | 
|   490   __ RecordWrite(unmapped.base(), x10, x11, |  | 
|   491                  kLRHasNotBeenSaved, kDontSaveFPRegs); |  | 
|   492   __ Ret(); |  | 
|   493   __ Bind(&slow); |  | 
|   494   GenerateMiss(masm); |  | 
|   495 } |  | 
|   496  |  | 
|   497  |  | 
|   498 void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) { |  | 
|   499   // The return address is in lr. |  | 
|   500   Isolate* isolate = masm->isolate(); |  | 
|   501  |  | 
|   502   __ IncrementCounter(isolate->counters()->keyed_load_miss(), 1, x10, x11); |  | 
|   503  |  | 
|   504   __ Push(ReceiverRegister(), NameRegister()); |  | 
|   505  |  | 
|   506   // Perform tail call to the entry. |  | 
|   507   ExternalReference ref = |  | 
|   508       ExternalReference(IC_Utility(kKeyedLoadIC_Miss), isolate); |  | 
|   509  |  | 
|   510   __ TailCallExternalReference(ref, 2, 1); |  | 
|   511 } |  | 
|   512  |  | 
|   513  |  | 
|   514 // IC register specifications |  | 
|   515 const Register LoadIC::ReceiverRegister() { return x1; } |  | 
|   516 const Register LoadIC::NameRegister() { return x2; } |  | 
|   517  |  | 
|   518 const Register LoadIC::SlotRegister() { |  | 
|   519   DCHECK(FLAG_vector_ics); |  | 
|   520   return x0; |  | 
|   521 } |  | 
|   522  |  | 
|   523  |  | 
|   524 const Register LoadIC::VectorRegister() { |  | 
|   525   DCHECK(FLAG_vector_ics); |  | 
|   526   return x3; |  | 
|   527 } |  | 
|   528  |  | 
|   529  |  | 
|   530 const Register StoreIC::ReceiverRegister() { return x1; } |  | 
|   531 const Register StoreIC::NameRegister() { return x2; } |  | 
|   532 const Register StoreIC::ValueRegister() { return x0; } |  | 
|   533  |  | 
|   534  |  | 
|   535 const Register KeyedStoreIC::MapRegister() { |  | 
|   536   return x3; |  | 
|   537 } |  | 
|   538  |  | 
|   539  |  | 
|   540 void KeyedLoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) { |  | 
|   541   // The return address is in lr. |  | 
|   542   __ Push(ReceiverRegister(), NameRegister()); |  | 
|   543   __ TailCallRuntime(Runtime::kKeyedGetProperty, 2, 1); |  | 
|   544 } |  | 
|   545  |  | 
|   546  |  | 
|   547 static void GenerateKeyedLoadWithSmiKey(MacroAssembler* masm, |  | 
|   548                                         Register key, |  | 
|   549                                         Register receiver, |  | 
|   550                                         Register scratch1, |  | 
|   551                                         Register scratch2, |  | 
|   552                                         Register scratch3, |  | 
|   553                                         Register scratch4, |  | 
|   554                                         Register scratch5, |  | 
|   555                                         Label *slow) { |  | 
|   556   DCHECK(!AreAliased( |  | 
|   557       key, receiver, scratch1, scratch2, scratch3, scratch4, scratch5)); |  | 
|   558  |  | 
|   559   Isolate* isolate = masm->isolate(); |  | 
|   560   Label check_number_dictionary; |  | 
|   561   // If we can load the value, it should be returned in x0. |  | 
|   562   Register result = x0; |  | 
|   563  |  | 
|   564   GenerateKeyedLoadReceiverCheck( |  | 
|   565       masm, receiver, scratch1, scratch2, Map::kHasIndexedInterceptor, slow); |  | 
|   566  |  | 
|   567   // Check the receiver's map to see if it has fast elements. |  | 
|   568   __ CheckFastElements(scratch1, scratch2, &check_number_dictionary); |  | 
|   569  |  | 
|   570   GenerateFastArrayLoad( |  | 
|   571       masm, receiver, key, scratch3, scratch2, scratch1, result, NULL, slow); |  | 
|   572   __ IncrementCounter( |  | 
|   573       isolate->counters()->keyed_load_generic_smi(), 1, scratch1, scratch2); |  | 
|   574   __ Ret(); |  | 
|   575  |  | 
|   576   __ Bind(&check_number_dictionary); |  | 
|   577   __ Ldr(scratch3, FieldMemOperand(receiver, JSObject::kElementsOffset)); |  | 
|   578   __ Ldr(scratch2, FieldMemOperand(scratch3, JSObject::kMapOffset)); |  | 
|   579  |  | 
|   580   // Check whether we have a number dictionary. |  | 
|   581   __ JumpIfNotRoot(scratch2, Heap::kHashTableMapRootIndex, slow); |  | 
|   582  |  | 
|   583   __ LoadFromNumberDictionary( |  | 
|   584       slow, scratch3, key, result, scratch1, scratch2, scratch4, scratch5); |  | 
|   585   __ Ret(); |  | 
|   586 } |  | 
|   587  |  | 
|   588 static void GenerateKeyedLoadWithNameKey(MacroAssembler* masm, |  | 
|   589                                          Register key, |  | 
|   590                                          Register receiver, |  | 
|   591                                          Register scratch1, |  | 
|   592                                          Register scratch2, |  | 
|   593                                          Register scratch3, |  | 
|   594                                          Register scratch4, |  | 
|   595                                          Register scratch5, |  | 
|   596                                          Label *slow) { |  | 
|   597   DCHECK(!AreAliased( |  | 
|   598       key, receiver, scratch1, scratch2, scratch3, scratch4, scratch5)); |  | 
|   599  |  | 
|   600   Isolate* isolate = masm->isolate(); |  | 
|   601   Label probe_dictionary, property_array_property; |  | 
|   602   // If we can load the value, it should be returned in x0. |  | 
|   603   Register result = x0; |  | 
|   604  |  | 
|   605   GenerateKeyedLoadReceiverCheck( |  | 
|   606       masm, receiver, scratch1, scratch2, Map::kHasNamedInterceptor, slow); |  | 
|   607  |  | 
|   608   // If the receiver is a fast-case object, check the keyed lookup cache. |  | 
|   609   // Otherwise probe the dictionary. |  | 
|   610   __ Ldr(scratch2, FieldMemOperand(receiver, JSObject::kPropertiesOffset)); |  | 
|   611   __ Ldr(scratch3, FieldMemOperand(scratch2, HeapObject::kMapOffset)); |  | 
|   612   __ JumpIfRoot(scratch3, Heap::kHashTableMapRootIndex, &probe_dictionary); |  | 
|   613  |  | 
|   614   // We keep the map of the receiver in scratch1. |  | 
|   615   Register receiver_map = scratch1; |  | 
|   616  |  | 
|   617   // Load the map of the receiver, compute the keyed lookup cache hash |  | 
|   618   // based on 32 bits of the map pointer and the name hash. |  | 
|   619   __ Ldr(receiver_map, FieldMemOperand(receiver, HeapObject::kMapOffset)); |  | 
|   620   __ Mov(scratch2, Operand(receiver_map, ASR, KeyedLookupCache::kMapHashShift)); |  | 
|   621   __ Ldr(scratch3.W(), FieldMemOperand(key, Name::kHashFieldOffset)); |  | 
|   622   __ Eor(scratch2, scratch2, Operand(scratch3, ASR, Name::kHashShift)); |  | 
|   623   int mask = KeyedLookupCache::kCapacityMask & KeyedLookupCache::kHashMask; |  | 
|   624   __ And(scratch2, scratch2, mask); |  | 
|   625  |  | 
|   626   // Load the key (consisting of map and unique name) from the cache and |  | 
|   627   // check for match. |  | 
|   628   Label load_in_object_property; |  | 
|   629   static const int kEntriesPerBucket = KeyedLookupCache::kEntriesPerBucket; |  | 
|   630   Label hit_on_nth_entry[kEntriesPerBucket]; |  | 
|   631   ExternalReference cache_keys = |  | 
|   632       ExternalReference::keyed_lookup_cache_keys(isolate); |  | 
|   633  |  | 
|   634   __ Mov(scratch3, cache_keys); |  | 
|   635   __ Add(scratch3, scratch3, Operand(scratch2, LSL, kPointerSizeLog2 + 1)); |  | 
|   636  |  | 
|   637   for (int i = 0; i < kEntriesPerBucket - 1; i++) { |  | 
|   638     Label try_next_entry; |  | 
|   639     // Load map and make scratch3 pointing to the next entry. |  | 
|   640     __ Ldr(scratch4, MemOperand(scratch3, kPointerSize * 2, PostIndex)); |  | 
|   641     __ Cmp(receiver_map, scratch4); |  | 
|   642     __ B(ne, &try_next_entry); |  | 
|   643     __ Ldr(scratch4, MemOperand(scratch3, -kPointerSize));  // Load name |  | 
|   644     __ Cmp(key, scratch4); |  | 
|   645     __ B(eq, &hit_on_nth_entry[i]); |  | 
|   646     __ Bind(&try_next_entry); |  | 
|   647   } |  | 
|   648  |  | 
|   649   // Last entry. |  | 
|   650   __ Ldr(scratch4, MemOperand(scratch3, kPointerSize, PostIndex)); |  | 
|   651   __ Cmp(receiver_map, scratch4); |  | 
|   652   __ B(ne, slow); |  | 
|   653   __ Ldr(scratch4, MemOperand(scratch3)); |  | 
|   654   __ Cmp(key, scratch4); |  | 
|   655   __ B(ne, slow); |  | 
|   656  |  | 
|   657   // Get field offset. |  | 
|   658   ExternalReference cache_field_offsets = |  | 
|   659       ExternalReference::keyed_lookup_cache_field_offsets(isolate); |  | 
|   660  |  | 
|   661   // Hit on nth entry. |  | 
|   662   for (int i = kEntriesPerBucket - 1; i >= 0; i--) { |  | 
|   663     __ Bind(&hit_on_nth_entry[i]); |  | 
|   664     __ Mov(scratch3, cache_field_offsets); |  | 
|   665     if (i != 0) { |  | 
|   666       __ Add(scratch2, scratch2, i); |  | 
|   667     } |  | 
|   668     __ Ldr(scratch4.W(), MemOperand(scratch3, scratch2, LSL, 2)); |  | 
|   669     __ Ldrb(scratch5, |  | 
|   670             FieldMemOperand(receiver_map, Map::kInObjectPropertiesOffset)); |  | 
|   671     __ Subs(scratch4, scratch4, scratch5); |  | 
|   672     __ B(ge, &property_array_property); |  | 
|   673     if (i != 0) { |  | 
|   674       __ B(&load_in_object_property); |  | 
|   675     } |  | 
|   676   } |  | 
|   677  |  | 
|   678   // Load in-object property. |  | 
|   679   __ Bind(&load_in_object_property); |  | 
|   680   __ Ldrb(scratch5, FieldMemOperand(receiver_map, Map::kInstanceSizeOffset)); |  | 
|   681   __ Add(scratch5, scratch5, scratch4);  // Index from start of object. |  | 
|   682   __ Sub(receiver, receiver, kHeapObjectTag);  // Remove the heap tag. |  | 
|   683   __ Ldr(result, MemOperand(receiver, scratch5, LSL, kPointerSizeLog2)); |  | 
|   684   __ IncrementCounter(isolate->counters()->keyed_load_generic_lookup_cache(), |  | 
|   685                       1, scratch1, scratch2); |  | 
|   686   __ Ret(); |  | 
|   687  |  | 
|   688   // Load property array property. |  | 
|   689   __ Bind(&property_array_property); |  | 
|   690   __ Ldr(scratch1, FieldMemOperand(receiver, JSObject::kPropertiesOffset)); |  | 
|   691   __ Add(scratch1, scratch1, FixedArray::kHeaderSize - kHeapObjectTag); |  | 
|   692   __ Ldr(result, MemOperand(scratch1, scratch4, LSL, kPointerSizeLog2)); |  | 
|   693   __ IncrementCounter(isolate->counters()->keyed_load_generic_lookup_cache(), |  | 
|   694                       1, scratch1, scratch2); |  | 
|   695   __ Ret(); |  | 
|   696  |  | 
|   697   // Do a quick inline probe of the receiver's dictionary, if it exists. |  | 
|   698   __ Bind(&probe_dictionary); |  | 
|   699   __ Ldr(scratch1, FieldMemOperand(receiver, HeapObject::kMapOffset)); |  | 
|   700   __ Ldrb(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset)); |  | 
|   701   GenerateGlobalInstanceTypeCheck(masm, scratch1, slow); |  | 
|   702   // Load the property. |  | 
|   703   GenerateDictionaryLoad(masm, slow, scratch2, key, result, scratch1, scratch3); |  | 
|   704   __ IncrementCounter(isolate->counters()->keyed_load_generic_symbol(), |  | 
|   705                       1, scratch1, scratch2); |  | 
|   706   __ Ret(); |  | 
|   707 } |  | 
|   708  |  | 
|   709  |  | 
|   710 void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { |  | 
|   711   // The return address is in lr. |  | 
|   712   Label slow, check_name, index_smi, index_name; |  | 
|   713  |  | 
|   714   Register key = NameRegister(); |  | 
|   715   Register receiver = ReceiverRegister(); |  | 
|   716   DCHECK(key.is(x2)); |  | 
|   717   DCHECK(receiver.is(x1)); |  | 
|   718  |  | 
|   719   __ JumpIfNotSmi(key, &check_name); |  | 
|   720   __ Bind(&index_smi); |  | 
|   721   // Now the key is known to be a smi. This place is also jumped to from below |  | 
|   722   // where a numeric string is converted to a smi. |  | 
|   723   GenerateKeyedLoadWithSmiKey(masm, key, receiver, x7, x3, x4, x5, x6, &slow); |  | 
|   724  |  | 
|   725   // Slow case. |  | 
|   726   __ Bind(&slow); |  | 
|   727   __ IncrementCounter( |  | 
|   728       masm->isolate()->counters()->keyed_load_generic_slow(), 1, x4, x3); |  | 
|   729   GenerateRuntimeGetProperty(masm); |  | 
|   730  |  | 
|   731   __ Bind(&check_name); |  | 
|   732   GenerateKeyNameCheck(masm, key, x0, x3, &index_name, &slow); |  | 
|   733  |  | 
|   734   GenerateKeyedLoadWithNameKey(masm, key, receiver, x7, x3, x4, x5, x6, &slow); |  | 
|   735  |  | 
|   736   __ Bind(&index_name); |  | 
|   737   __ IndexFromHash(x3, key); |  | 
|   738   // Now jump to the place where smi keys are handled. |  | 
|   739   __ B(&index_smi); |  | 
|   740 } |  | 
|   741  |  | 
|   742  |  | 
|   743 void KeyedLoadIC::GenerateString(MacroAssembler* masm) { |  | 
|   744   // Return address is in lr. |  | 
|   745   Label miss; |  | 
|   746  |  | 
|   747   Register receiver = ReceiverRegister(); |  | 
|   748   Register index = NameRegister(); |  | 
|   749   Register result = x0; |  | 
|   750   Register scratch = x3; |  | 
|   751   DCHECK(!scratch.is(receiver) && !scratch.is(index)); |  | 
|   752  |  | 
|   753   StringCharAtGenerator char_at_generator(receiver, |  | 
|   754                                           index, |  | 
|   755                                           scratch, |  | 
|   756                                           result, |  | 
|   757                                           &miss,  // When not a string. |  | 
|   758                                           &miss,  // When not a number. |  | 
|   759                                           &miss,  // When index out of range. |  | 
|   760                                           STRING_INDEX_IS_ARRAY_INDEX); |  | 
|   761   char_at_generator.GenerateFast(masm); |  | 
|   762   __ Ret(); |  | 
|   763  |  | 
|   764   StubRuntimeCallHelper call_helper; |  | 
|   765   char_at_generator.GenerateSlow(masm, call_helper); |  | 
|   766  |  | 
|   767   __ Bind(&miss); |  | 
|   768   GenerateMiss(masm); |  | 
|   769 } |  | 
|   770  |  | 
|   771  |  | 
|   772 void KeyedLoadIC::GenerateIndexedInterceptor(MacroAssembler* masm) { |  | 
|   773   // Return address is in lr. |  | 
|   774   Label slow; |  | 
|   775  |  | 
|   776   Register receiver = ReceiverRegister(); |  | 
|   777   Register key = NameRegister(); |  | 
|   778   Register scratch1 = x3; |  | 
|   779   Register scratch2 = x4; |  | 
|   780   DCHECK(!AreAliased(scratch1, scratch2, receiver, key)); |  | 
|   781  |  | 
|   782   // Check that the receiver isn't a smi. |  | 
|   783   __ JumpIfSmi(receiver, &slow); |  | 
|   784  |  | 
|   785   // Check that the key is an array index, that is Uint32. |  | 
|   786   __ TestAndBranchIfAnySet(key, kSmiTagMask | kSmiSignMask, &slow); |  | 
|   787  |  | 
|   788   // Get the map of the receiver. |  | 
|   789   Register map = scratch1; |  | 
|   790   __ Ldr(map, FieldMemOperand(receiver, HeapObject::kMapOffset)); |  | 
|   791  |  | 
|   792   // Check that it has indexed interceptor and access checks |  | 
|   793   // are not enabled for this object. |  | 
|   794   __ Ldrb(scratch2, FieldMemOperand(map, Map::kBitFieldOffset)); |  | 
|   795   DCHECK(kSlowCaseBitFieldMask == |  | 
|   796       ((1 << Map::kIsAccessCheckNeeded) | (1 << Map::kHasIndexedInterceptor))); |  | 
|   797   __ Tbnz(scratch2, Map::kIsAccessCheckNeeded, &slow); |  | 
|   798   __ Tbz(scratch2, Map::kHasIndexedInterceptor, &slow); |  | 
|   799  |  | 
|   800   // Everything is fine, call runtime. |  | 
|   801   __ Push(receiver, key); |  | 
|   802   __ TailCallExternalReference( |  | 
|   803       ExternalReference(IC_Utility(kLoadElementWithInterceptor), |  | 
|   804                         masm->isolate()), |  | 
|   805       2, 1); |  | 
|   806  |  | 
|   807   __ Bind(&slow); |  | 
|   808   GenerateMiss(masm); |  | 
|   809 } |  | 
|   810  |  | 
|   811  |  | 
|   812 void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) { |  | 
|   813   ASM_LOCATION("KeyedStoreIC::GenerateMiss"); |  | 
|   814  |  | 
|   815   // Push receiver, key and value for runtime call. |  | 
|   816   __ Push(ReceiverRegister(), NameRegister(), ValueRegister()); |  | 
|   817  |  | 
|   818   ExternalReference ref = |  | 
|   819       ExternalReference(IC_Utility(kKeyedStoreIC_Miss), masm->isolate()); |  | 
|   820   __ TailCallExternalReference(ref, 3, 1); |  | 
|   821 } |  | 
|   822  |  | 
|   823  |  | 
|   824 void KeyedStoreIC::GenerateSlow(MacroAssembler* masm) { |  | 
|   825   ASM_LOCATION("KeyedStoreIC::GenerateSlow"); |  | 
|   826  |  | 
|   827   // Push receiver, key and value for runtime call. |  | 
|   828   __ Push(ReceiverRegister(), NameRegister(), ValueRegister()); |  | 
|   829  |  | 
|   830   // The slow case calls into the runtime to complete the store without causing |  | 
|   831   // an IC miss that would otherwise cause a transition to the generic stub. |  | 
|   832   ExternalReference ref = |  | 
|   833       ExternalReference(IC_Utility(kKeyedStoreIC_Slow), masm->isolate()); |  | 
|   834   __ TailCallExternalReference(ref, 3, 1); |  | 
|   835 } |  | 
|   836  |  | 
|   837  |  | 
|   838 void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm, |  | 
|   839                                               StrictMode strict_mode) { |  | 
|   840   ASM_LOCATION("KeyedStoreIC::GenerateRuntimeSetProperty"); |  | 
|   841  |  | 
|   842   // Push receiver, key and value for runtime call. |  | 
|   843   __ Push(ReceiverRegister(), NameRegister(), ValueRegister()); |  | 
|   844  |  | 
|   845   // Push strict_mode for runtime call. |  | 
|   846   __ Mov(x10, Smi::FromInt(strict_mode)); |  | 
|   847   __ Push(x10); |  | 
|   848  |  | 
|   849   __ TailCallRuntime(Runtime::kSetProperty, 4, 1); |  | 
|   850 } |  | 
|   851  |  | 
|   852  |  | 
|   853 static void KeyedStoreGenerateGenericHelper( |  | 
|   854     MacroAssembler* masm, |  | 
|   855     Label* fast_object, |  | 
|   856     Label* fast_double, |  | 
|   857     Label* slow, |  | 
|   858     KeyedStoreCheckMap check_map, |  | 
|   859     KeyedStoreIncrementLength increment_length, |  | 
|   860     Register value, |  | 
|   861     Register key, |  | 
|   862     Register receiver, |  | 
|   863     Register receiver_map, |  | 
|   864     Register elements_map, |  | 
|   865     Register elements) { |  | 
|   866   DCHECK(!AreAliased( |  | 
|   867       value, key, receiver, receiver_map, elements_map, elements, x10, x11)); |  | 
|   868  |  | 
|   869   Label transition_smi_elements; |  | 
|   870   Label transition_double_elements; |  | 
|   871   Label fast_double_without_map_check; |  | 
|   872   Label non_double_value; |  | 
|   873   Label finish_store; |  | 
|   874  |  | 
|   875   __ Bind(fast_object); |  | 
|   876   if (check_map == kCheckMap) { |  | 
|   877     __ Ldr(elements_map, FieldMemOperand(elements, HeapObject::kMapOffset)); |  | 
|   878     __ Cmp(elements_map, |  | 
|   879            Operand(masm->isolate()->factory()->fixed_array_map())); |  | 
|   880     __ B(ne, fast_double); |  | 
|   881   } |  | 
|   882  |  | 
|   883   // HOLECHECK: guards "A[i] = V" |  | 
|   884   // We have to go to the runtime if the current value is the hole because there |  | 
|   885   // may be a callback on the element. |  | 
|   886   Label holecheck_passed; |  | 
|   887   __ Add(x10, elements, FixedArray::kHeaderSize - kHeapObjectTag); |  | 
|   888   __ Add(x10, x10, Operand::UntagSmiAndScale(key, kPointerSizeLog2)); |  | 
|   889   __ Ldr(x11, MemOperand(x10)); |  | 
|   890   __ JumpIfNotRoot(x11, Heap::kTheHoleValueRootIndex, &holecheck_passed); |  | 
|   891   __ JumpIfDictionaryInPrototypeChain(receiver, elements_map, x10, slow); |  | 
|   892   __ bind(&holecheck_passed); |  | 
|   893  |  | 
|   894   // Smi stores don't require further checks. |  | 
|   895   __ JumpIfSmi(value, &finish_store); |  | 
|   896  |  | 
|   897   // Escape to elements kind transition case. |  | 
|   898   __ CheckFastObjectElements(receiver_map, x10, &transition_smi_elements); |  | 
|   899  |  | 
|   900   __ Bind(&finish_store); |  | 
|   901   if (increment_length == kIncrementLength) { |  | 
|   902     // Add 1 to receiver->length. |  | 
|   903     __ Add(x10, key, Smi::FromInt(1)); |  | 
|   904     __ Str(x10, FieldMemOperand(receiver, JSArray::kLengthOffset)); |  | 
|   905   } |  | 
|   906  |  | 
|   907   Register address = x11; |  | 
|   908   __ Add(address, elements, FixedArray::kHeaderSize - kHeapObjectTag); |  | 
|   909   __ Add(address, address, Operand::UntagSmiAndScale(key, kPointerSizeLog2)); |  | 
|   910   __ Str(value, MemOperand(address)); |  | 
|   911  |  | 
|   912   Label dont_record_write; |  | 
|   913   __ JumpIfSmi(value, &dont_record_write); |  | 
|   914  |  | 
|   915   // Update write barrier for the elements array address. |  | 
|   916   __ Mov(x10, value);  // Preserve the value which is returned. |  | 
|   917   __ RecordWrite(elements, |  | 
|   918                  address, |  | 
|   919                  x10, |  | 
|   920                  kLRHasNotBeenSaved, |  | 
|   921                  kDontSaveFPRegs, |  | 
|   922                  EMIT_REMEMBERED_SET, |  | 
|   923                  OMIT_SMI_CHECK); |  | 
|   924  |  | 
|   925   __ Bind(&dont_record_write); |  | 
|   926   __ Ret(); |  | 
|   927  |  | 
|   928  |  | 
|   929   __ Bind(fast_double); |  | 
|   930   if (check_map == kCheckMap) { |  | 
|   931     // Check for fast double array case. If this fails, call through to the |  | 
|   932     // runtime. |  | 
|   933     __ JumpIfNotRoot(elements_map, Heap::kFixedDoubleArrayMapRootIndex, slow); |  | 
|   934   } |  | 
|   935  |  | 
|   936   // HOLECHECK: guards "A[i] double hole?" |  | 
|   937   // We have to see if the double version of the hole is present. If so go to |  | 
|   938   // the runtime. |  | 
|   939   __ Add(x10, elements, FixedDoubleArray::kHeaderSize - kHeapObjectTag); |  | 
|   940   __ Add(x10, x10, Operand::UntagSmiAndScale(key, kPointerSizeLog2)); |  | 
|   941   __ Ldr(x11, MemOperand(x10)); |  | 
|   942   __ CompareAndBranch(x11, kHoleNanInt64, ne, &fast_double_without_map_check); |  | 
|   943   __ JumpIfDictionaryInPrototypeChain(receiver, elements_map, x10, slow); |  | 
|   944  |  | 
|   945   __ Bind(&fast_double_without_map_check); |  | 
|   946   __ StoreNumberToDoubleElements(value, |  | 
|   947                                  key, |  | 
|   948                                  elements, |  | 
|   949                                  x10, |  | 
|   950                                  d0, |  | 
|   951                                  &transition_double_elements); |  | 
|   952   if (increment_length == kIncrementLength) { |  | 
|   953     // Add 1 to receiver->length. |  | 
|   954     __ Add(x10, key, Smi::FromInt(1)); |  | 
|   955     __ Str(x10, FieldMemOperand(receiver, JSArray::kLengthOffset)); |  | 
|   956   } |  | 
|   957   __ Ret(); |  | 
|   958  |  | 
|   959  |  | 
|   960   __ Bind(&transition_smi_elements); |  | 
|   961   // Transition the array appropriately depending on the value type. |  | 
|   962   __ Ldr(x10, FieldMemOperand(value, HeapObject::kMapOffset)); |  | 
|   963   __ JumpIfNotRoot(x10, Heap::kHeapNumberMapRootIndex, &non_double_value); |  | 
|   964  |  | 
|   965   // Value is a double. Transition FAST_SMI_ELEMENTS -> |  | 
|   966   // FAST_DOUBLE_ELEMENTS and complete the store. |  | 
|   967   __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, |  | 
|   968                                          FAST_DOUBLE_ELEMENTS, |  | 
|   969                                          receiver_map, |  | 
|   970                                          x10, |  | 
|   971                                          x11, |  | 
|   972                                          slow); |  | 
|   973   AllocationSiteMode mode = AllocationSite::GetMode(FAST_SMI_ELEMENTS, |  | 
|   974                                                     FAST_DOUBLE_ELEMENTS); |  | 
|   975   ElementsTransitionGenerator::GenerateSmiToDouble( |  | 
|   976       masm, receiver, key, value, receiver_map, mode, slow); |  | 
|   977   __ Ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset)); |  | 
|   978   __ B(&fast_double_without_map_check); |  | 
|   979  |  | 
|   980   __ Bind(&non_double_value); |  | 
|   981   // Value is not a double, FAST_SMI_ELEMENTS -> FAST_ELEMENTS. |  | 
|   982   __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, |  | 
|   983                                          FAST_ELEMENTS, |  | 
|   984                                          receiver_map, |  | 
|   985                                          x10, |  | 
|   986                                          x11, |  | 
|   987                                          slow); |  | 
|   988  |  | 
|   989   mode = AllocationSite::GetMode(FAST_SMI_ELEMENTS, FAST_ELEMENTS); |  | 
|   990   ElementsTransitionGenerator::GenerateMapChangeElementsTransition( |  | 
|   991       masm, receiver, key, value, receiver_map, mode, slow); |  | 
|   992  |  | 
|   993   __ Ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset)); |  | 
|   994   __ B(&finish_store); |  | 
|   995  |  | 
|   996   __ Bind(&transition_double_elements); |  | 
|   997   // Elements are FAST_DOUBLE_ELEMENTS, but value is an Object that's not a |  | 
|   998   // HeapNumber. Make sure that the receiver is a Array with FAST_ELEMENTS and |  | 
|   999   // transition array from FAST_DOUBLE_ELEMENTS to FAST_ELEMENTS |  | 
|  1000   __ LoadTransitionedArrayMapConditional(FAST_DOUBLE_ELEMENTS, |  | 
|  1001                                          FAST_ELEMENTS, |  | 
|  1002                                          receiver_map, |  | 
|  1003                                          x10, |  | 
|  1004                                          x11, |  | 
|  1005                                          slow); |  | 
|  1006   mode = AllocationSite::GetMode(FAST_DOUBLE_ELEMENTS, FAST_ELEMENTS); |  | 
|  1007   ElementsTransitionGenerator::GenerateDoubleToObject( |  | 
|  1008       masm, receiver, key, value, receiver_map, mode, slow); |  | 
|  1009   __ Ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset)); |  | 
|  1010   __ B(&finish_store); |  | 
|  1011 } |  | 
|  1012  |  | 
|  1013  |  | 
|  1014 void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm, |  | 
|  1015                                    StrictMode strict_mode) { |  | 
|  1016   ASM_LOCATION("KeyedStoreIC::GenerateGeneric"); |  | 
|  1017   Label slow; |  | 
|  1018   Label array; |  | 
|  1019   Label fast_object; |  | 
|  1020   Label extra; |  | 
|  1021   Label fast_object_grow; |  | 
|  1022   Label fast_double_grow; |  | 
|  1023   Label fast_double; |  | 
|  1024  |  | 
|  1025   Register value = ValueRegister(); |  | 
|  1026   Register key = NameRegister(); |  | 
|  1027   Register receiver = ReceiverRegister(); |  | 
|  1028   DCHECK(receiver.is(x1)); |  | 
|  1029   DCHECK(key.is(x2)); |  | 
|  1030   DCHECK(value.is(x0)); |  | 
|  1031  |  | 
|  1032   Register receiver_map = x3; |  | 
|  1033   Register elements = x4; |  | 
|  1034   Register elements_map = x5; |  | 
|  1035  |  | 
|  1036   __ JumpIfNotSmi(key, &slow); |  | 
|  1037   __ JumpIfSmi(receiver, &slow); |  | 
|  1038   __ Ldr(receiver_map, FieldMemOperand(receiver, HeapObject::kMapOffset)); |  | 
|  1039  |  | 
|  1040   // Check that the receiver does not require access checks and is not observed. |  | 
|  1041   // The generic stub does not perform map checks or handle observed objects. |  | 
|  1042   __ Ldrb(x10, FieldMemOperand(receiver_map, Map::kBitFieldOffset)); |  | 
|  1043   __ TestAndBranchIfAnySet( |  | 
|  1044       x10, (1 << Map::kIsAccessCheckNeeded) | (1 << Map::kIsObserved), &slow); |  | 
|  1045  |  | 
|  1046   // Check if the object is a JS array or not. |  | 
|  1047   Register instance_type = x10; |  | 
|  1048   __ CompareInstanceType(receiver_map, instance_type, JS_ARRAY_TYPE); |  | 
|  1049   __ B(eq, &array); |  | 
|  1050   // Check that the object is some kind of JSObject. |  | 
|  1051   __ Cmp(instance_type, FIRST_JS_OBJECT_TYPE); |  | 
|  1052   __ B(lt, &slow); |  | 
|  1053  |  | 
|  1054   // Object case: Check key against length in the elements array. |  | 
|  1055   __ Ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset)); |  | 
|  1056   // Check array bounds. Both the key and the length of FixedArray are smis. |  | 
|  1057   __ Ldrsw(x10, UntagSmiFieldMemOperand(elements, FixedArray::kLengthOffset)); |  | 
|  1058   __ Cmp(x10, Operand::UntagSmi(key)); |  | 
|  1059   __ B(hi, &fast_object); |  | 
|  1060  |  | 
|  1061  |  | 
|  1062   __ Bind(&slow); |  | 
|  1063   // Slow case, handle jump to runtime. |  | 
|  1064   // Live values: |  | 
|  1065   //  x0: value |  | 
|  1066   //  x1: key |  | 
|  1067   //  x2: receiver |  | 
|  1068   GenerateRuntimeSetProperty(masm, strict_mode); |  | 
|  1069  |  | 
|  1070  |  | 
|  1071   __ Bind(&extra); |  | 
|  1072   // Extra capacity case: Check if there is extra capacity to |  | 
|  1073   // perform the store and update the length. Used for adding one |  | 
|  1074   // element to the array by writing to array[array.length]. |  | 
|  1075  |  | 
|  1076   // Check for room in the elements backing store. |  | 
|  1077   // Both the key and the length of FixedArray are smis. |  | 
|  1078   __ Ldrsw(x10, UntagSmiFieldMemOperand(elements, FixedArray::kLengthOffset)); |  | 
|  1079   __ Cmp(x10, Operand::UntagSmi(key)); |  | 
|  1080   __ B(ls, &slow); |  | 
|  1081  |  | 
|  1082   __ Ldr(elements_map, FieldMemOperand(elements, HeapObject::kMapOffset)); |  | 
|  1083   __ Cmp(elements_map, Operand(masm->isolate()->factory()->fixed_array_map())); |  | 
|  1084   __ B(eq, &fast_object_grow); |  | 
|  1085   __ Cmp(elements_map, |  | 
|  1086          Operand(masm->isolate()->factory()->fixed_double_array_map())); |  | 
|  1087   __ B(eq, &fast_double_grow); |  | 
|  1088   __ B(&slow); |  | 
|  1089  |  | 
|  1090  |  | 
|  1091   __ Bind(&array); |  | 
|  1092   // Array case: Get the length and the elements array from the JS |  | 
|  1093   // array. Check that the array is in fast mode (and writable); if it |  | 
|  1094   // is the length is always a smi. |  | 
|  1095  |  | 
|  1096   __ Ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset)); |  | 
|  1097  |  | 
|  1098   // Check the key against the length in the array. |  | 
|  1099   __ Ldrsw(x10, UntagSmiFieldMemOperand(receiver, JSArray::kLengthOffset)); |  | 
|  1100   __ Cmp(x10, Operand::UntagSmi(key)); |  | 
|  1101   __ B(eq, &extra);  // We can handle the case where we are appending 1 element. |  | 
|  1102   __ B(lo, &slow); |  | 
|  1103  |  | 
|  1104   KeyedStoreGenerateGenericHelper(masm, &fast_object, &fast_double, |  | 
|  1105                                   &slow, kCheckMap, kDontIncrementLength, |  | 
|  1106                                   value, key, receiver, receiver_map, |  | 
|  1107                                   elements_map, elements); |  | 
|  1108   KeyedStoreGenerateGenericHelper(masm, &fast_object_grow, &fast_double_grow, |  | 
|  1109                                   &slow, kDontCheckMap, kIncrementLength, |  | 
|  1110                                   value, key, receiver, receiver_map, |  | 
|  1111                                   elements_map, elements); |  | 
|  1112 } |  | 
|  1113  |  | 
|  1114  |  | 
|  1115 void StoreIC::GenerateMegamorphic(MacroAssembler* masm) { |  | 
|  1116   Register receiver = ReceiverRegister(); |  | 
|  1117   Register name = NameRegister(); |  | 
|  1118   DCHECK(!AreAliased(receiver, name, ValueRegister(), x3, x4, x5, x6)); |  | 
|  1119  |  | 
|  1120   // Probe the stub cache. |  | 
|  1121   Code::Flags flags = Code::RemoveTypeAndHolderFromFlags( |  | 
|  1122       Code::ComputeHandlerFlags(Code::STORE_IC)); |  | 
|  1123   masm->isolate()->stub_cache()->GenerateProbe( |  | 
|  1124       masm, flags, receiver, name, x3, x4, x5, x6); |  | 
|  1125  |  | 
|  1126   // Cache miss: Jump to runtime. |  | 
|  1127   GenerateMiss(masm); |  | 
|  1128 } |  | 
|  1129  |  | 
|  1130  |  | 
|  1131 void StoreIC::GenerateMiss(MacroAssembler* masm) { |  | 
|  1132   __ Push(ReceiverRegister(), NameRegister(), ValueRegister()); |  | 
|  1133  |  | 
|  1134   // Tail call to the entry. |  | 
|  1135   ExternalReference ref = |  | 
|  1136       ExternalReference(IC_Utility(kStoreIC_Miss), masm->isolate()); |  | 
|  1137   __ TailCallExternalReference(ref, 3, 1); |  | 
|  1138 } |  | 
|  1139  |  | 
|  1140  |  | 
|  1141 void StoreIC::GenerateNormal(MacroAssembler* masm) { |  | 
|  1142   Label miss; |  | 
|  1143   Register value = ValueRegister(); |  | 
|  1144   Register receiver = ReceiverRegister(); |  | 
|  1145   Register name = NameRegister(); |  | 
|  1146   Register dictionary = x3; |  | 
|  1147   DCHECK(!AreAliased(value, receiver, name, x3, x4, x5)); |  | 
|  1148  |  | 
|  1149   __ Ldr(dictionary, FieldMemOperand(receiver, JSObject::kPropertiesOffset)); |  | 
|  1150  |  | 
|  1151   GenerateDictionaryStore(masm, &miss, dictionary, name, value, x4, x5); |  | 
|  1152   Counters* counters = masm->isolate()->counters(); |  | 
|  1153   __ IncrementCounter(counters->store_normal_hit(), 1, x4, x5); |  | 
|  1154   __ Ret(); |  | 
|  1155  |  | 
|  1156   // Cache miss: Jump to runtime. |  | 
|  1157   __ Bind(&miss); |  | 
|  1158   __ IncrementCounter(counters->store_normal_miss(), 1, x4, x5); |  | 
|  1159   GenerateMiss(masm); |  | 
|  1160 } |  | 
|  1161  |  | 
|  1162  |  | 
|  1163 void StoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm, |  | 
|  1164                                          StrictMode strict_mode) { |  | 
|  1165   ASM_LOCATION("StoreIC::GenerateRuntimeSetProperty"); |  | 
|  1166  |  | 
|  1167   __ Push(ReceiverRegister(), NameRegister(), ValueRegister()); |  | 
|  1168  |  | 
|  1169   __ Mov(x10, Smi::FromInt(strict_mode)); |  | 
|  1170   __ Push(x10); |  | 
|  1171  |  | 
|  1172   // Do tail-call to runtime routine. |  | 
|  1173   __ TailCallRuntime(Runtime::kSetProperty, 4, 1); |  | 
|  1174 } |  | 
|  1175  |  | 
|  1176  |  | 
|  1177 void StoreIC::GenerateSlow(MacroAssembler* masm) { |  | 
|  1178   // ---------- S t a t e -------------- |  | 
|  1179   //  -- x0     : value |  | 
|  1180   //  -- x1     : receiver |  | 
|  1181   //  -- x2     : name |  | 
|  1182   //  -- lr     : return address |  | 
|  1183   // ----------------------------------- |  | 
|  1184  |  | 
|  1185   // Push receiver, name and value for runtime call. |  | 
|  1186   __ Push(ReceiverRegister(), NameRegister(), ValueRegister()); |  | 
|  1187  |  | 
|  1188   // The slow case calls into the runtime to complete the store without causing |  | 
|  1189   // an IC miss that would otherwise cause a transition to the generic stub. |  | 
|  1190   ExternalReference ref = |  | 
|  1191       ExternalReference(IC_Utility(kStoreIC_Slow), masm->isolate()); |  | 
|  1192   __ TailCallExternalReference(ref, 3, 1); |  | 
|  1193 } |  | 
|  1194  |  | 
|  1195  |  | 
|  1196 Condition CompareIC::ComputeCondition(Token::Value op) { |  | 
|  1197   switch (op) { |  | 
|  1198     case Token::EQ_STRICT: |  | 
|  1199     case Token::EQ: |  | 
|  1200       return eq; |  | 
|  1201     case Token::LT: |  | 
|  1202       return lt; |  | 
|  1203     case Token::GT: |  | 
|  1204       return gt; |  | 
|  1205     case Token::LTE: |  | 
|  1206       return le; |  | 
|  1207     case Token::GTE: |  | 
|  1208       return ge; |  | 
|  1209     default: |  | 
|  1210       UNREACHABLE(); |  | 
|  1211       return al; |  | 
|  1212   } |  | 
|  1213 } |  | 
|  1214  |  | 
|  1215  |  | 
|  1216 bool CompareIC::HasInlinedSmiCode(Address address) { |  | 
|  1217   // The address of the instruction following the call. |  | 
|  1218   Address info_address = |  | 
|  1219       Assembler::return_address_from_call_start(address); |  | 
|  1220  |  | 
|  1221   InstructionSequence* patch_info = InstructionSequence::At(info_address); |  | 
|  1222   return patch_info->IsInlineData(); |  | 
|  1223 } |  | 
|  1224  |  | 
|  1225  |  | 
|  1226 // Activate a SMI fast-path by patching the instructions generated by |  | 
|  1227 // JumpPatchSite::EmitJumpIf(Not)Smi(), using the information encoded by |  | 
|  1228 // JumpPatchSite::EmitPatchInfo(). |  | 
|  1229 void PatchInlinedSmiCode(Address address, InlinedSmiCheck check) { |  | 
|  1230   // The patch information is encoded in the instruction stream using |  | 
|  1231   // instructions which have no side effects, so we can safely execute them. |  | 
|  1232   // The patch information is encoded directly after the call to the helper |  | 
|  1233   // function which is requesting this patch operation. |  | 
|  1234   Address info_address = |  | 
|  1235       Assembler::return_address_from_call_start(address); |  | 
|  1236   InlineSmiCheckInfo info(info_address); |  | 
|  1237  |  | 
|  1238   // Check and decode the patch information instruction. |  | 
|  1239   if (!info.HasSmiCheck()) { |  | 
|  1240     return; |  | 
|  1241   } |  | 
|  1242  |  | 
|  1243   if (FLAG_trace_ic) { |  | 
|  1244     PrintF("[  Patching ic at %p, marker=%p, SMI check=%p\n", |  | 
|  1245            address, info_address, reinterpret_cast<void*>(info.SmiCheck())); |  | 
|  1246   } |  | 
|  1247  |  | 
|  1248   // Patch and activate code generated by JumpPatchSite::EmitJumpIfNotSmi() |  | 
|  1249   // and JumpPatchSite::EmitJumpIfSmi(). |  | 
|  1250   // Changing |  | 
|  1251   //   tb(n)z xzr, #0, <target> |  | 
|  1252   // to |  | 
|  1253   //   tb(!n)z test_reg, #0, <target> |  | 
|  1254   Instruction* to_patch = info.SmiCheck(); |  | 
|  1255   PatchingAssembler patcher(to_patch, 1); |  | 
|  1256   DCHECK(to_patch->IsTestBranch()); |  | 
|  1257   DCHECK(to_patch->ImmTestBranchBit5() == 0); |  | 
|  1258   DCHECK(to_patch->ImmTestBranchBit40() == 0); |  | 
|  1259  |  | 
|  1260   STATIC_ASSERT(kSmiTag == 0); |  | 
|  1261   STATIC_ASSERT(kSmiTagMask == 1); |  | 
|  1262  |  | 
|  1263   int branch_imm = to_patch->ImmTestBranch(); |  | 
|  1264   Register smi_reg; |  | 
|  1265   if (check == ENABLE_INLINED_SMI_CHECK) { |  | 
|  1266     DCHECK(to_patch->Rt() == xzr.code()); |  | 
|  1267     smi_reg = info.SmiRegister(); |  | 
|  1268   } else { |  | 
|  1269     DCHECK(check == DISABLE_INLINED_SMI_CHECK); |  | 
|  1270     DCHECK(to_patch->Rt() != xzr.code()); |  | 
|  1271     smi_reg = xzr; |  | 
|  1272   } |  | 
|  1273  |  | 
|  1274   if (to_patch->Mask(TestBranchMask) == TBZ) { |  | 
|  1275     // This is JumpIfNotSmi(smi_reg, branch_imm). |  | 
|  1276     patcher.tbnz(smi_reg, 0, branch_imm); |  | 
|  1277   } else { |  | 
|  1278     DCHECK(to_patch->Mask(TestBranchMask) == TBNZ); |  | 
|  1279     // This is JumpIfSmi(smi_reg, branch_imm). |  | 
|  1280     patcher.tbz(smi_reg, 0, branch_imm); |  | 
|  1281   } |  | 
|  1282 } |  | 
|  1283  |  | 
|  1284  |  | 
|  1285 } }  // namespace v8::internal |  | 
|  1286  |  | 
|  1287 #endif  // V8_TARGET_ARCH_ARM64 |  | 
| OLD | NEW |