| OLD | NEW |
| 1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2013 the V8 project authors. All rights reserved. |
| 2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
| 3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
| 4 // met: | 4 // met: |
| 5 // | 5 // |
| 6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
| 7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
| 8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
| 9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
| 10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
| 11 // with the distribution. | 11 // with the distribution. |
| 12 // * Neither the name of Google Inc. nor the names of its | 12 // * Neither the name of Google Inc. nor the names of its |
| 13 // contributors may be used to endorse or promote products derived | 13 // contributors may be used to endorse or promote products derived |
| 14 // from this software without specific prior written permission. | 14 // from this software without specific prior written permission. |
| 15 // | 15 // |
| 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 27 | 27 |
| 28 #include "v8.h" | 28 #include "v8.h" |
| 29 | 29 |
| 30 #if V8_TARGET_ARCH_ARM | 30 #if V8_TARGET_ARCH_A64 |
| 31 | 31 |
| 32 #include "ic-inl.h" | 32 #include "ic-inl.h" |
| 33 #include "codegen.h" | 33 #include "codegen.h" |
| 34 #include "stub-cache.h" | 34 #include "stub-cache.h" |
| 35 | 35 |
| 36 namespace v8 { | 36 namespace v8 { |
| 37 namespace internal { | 37 namespace internal { |
| 38 | 38 |
| 39 |
| 39 #define __ ACCESS_MASM(masm) | 40 #define __ ACCESS_MASM(masm) |
| 40 | 41 |
| 41 | 42 |
| 42 static void ProbeTable(Isolate* isolate, | |
| 43 MacroAssembler* masm, | |
| 44 Code::Flags flags, | |
| 45 StubCache::Table table, | |
| 46 Register receiver, | |
| 47 Register name, | |
| 48 // Number of the cache entry, not scaled. | |
| 49 Register offset, | |
| 50 Register scratch, | |
| 51 Register scratch2, | |
| 52 Register offset_scratch) { | |
| 53 ExternalReference key_offset(isolate->stub_cache()->key_reference(table)); | |
| 54 ExternalReference value_offset(isolate->stub_cache()->value_reference(table)); | |
| 55 ExternalReference map_offset(isolate->stub_cache()->map_reference(table)); | |
| 56 | |
| 57 uint32_t key_off_addr = reinterpret_cast<uint32_t>(key_offset.address()); | |
| 58 uint32_t value_off_addr = reinterpret_cast<uint32_t>(value_offset.address()); | |
| 59 uint32_t map_off_addr = reinterpret_cast<uint32_t>(map_offset.address()); | |
| 60 | |
| 61 // Check the relative positions of the address fields. | |
| 62 ASSERT(value_off_addr > key_off_addr); | |
| 63 ASSERT((value_off_addr - key_off_addr) % 4 == 0); | |
| 64 ASSERT((value_off_addr - key_off_addr) < (256 * 4)); | |
| 65 ASSERT(map_off_addr > key_off_addr); | |
| 66 ASSERT((map_off_addr - key_off_addr) % 4 == 0); | |
| 67 ASSERT((map_off_addr - key_off_addr) < (256 * 4)); | |
| 68 | |
| 69 Label miss; | |
| 70 Register base_addr = scratch; | |
| 71 scratch = no_reg; | |
| 72 | |
| 73 // Multiply by 3 because there are 3 fields per entry (name, code, map). | |
| 74 __ add(offset_scratch, offset, Operand(offset, LSL, 1)); | |
| 75 | |
| 76 // Calculate the base address of the entry. | |
| 77 __ mov(base_addr, Operand(key_offset)); | |
| 78 __ add(base_addr, base_addr, Operand(offset_scratch, LSL, kPointerSizeLog2)); | |
| 79 | |
| 80 // Check that the key in the entry matches the name. | |
| 81 __ ldr(ip, MemOperand(base_addr, 0)); | |
| 82 __ cmp(name, ip); | |
| 83 __ b(ne, &miss); | |
| 84 | |
| 85 // Check the map matches. | |
| 86 __ ldr(ip, MemOperand(base_addr, map_off_addr - key_off_addr)); | |
| 87 __ ldr(scratch2, FieldMemOperand(receiver, HeapObject::kMapOffset)); | |
| 88 __ cmp(ip, scratch2); | |
| 89 __ b(ne, &miss); | |
| 90 | |
| 91 // Get the code entry from the cache. | |
| 92 Register code = scratch2; | |
| 93 scratch2 = no_reg; | |
| 94 __ ldr(code, MemOperand(base_addr, value_off_addr - key_off_addr)); | |
| 95 | |
| 96 // Check that the flags match what we're looking for. | |
| 97 Register flags_reg = base_addr; | |
| 98 base_addr = no_reg; | |
| 99 __ ldr(flags_reg, FieldMemOperand(code, Code::kFlagsOffset)); | |
| 100 // It's a nice optimization if this constant is encodable in the bic insn. | |
| 101 | |
| 102 uint32_t mask = Code::kFlagsNotUsedInLookup; | |
| 103 ASSERT(__ ImmediateFitsAddrMode1Instruction(mask)); | |
| 104 __ bic(flags_reg, flags_reg, Operand(mask)); | |
| 105 __ cmp(flags_reg, Operand(flags)); | |
| 106 __ b(ne, &miss); | |
| 107 | |
| 108 #ifdef DEBUG | |
| 109 if (FLAG_test_secondary_stub_cache && table == StubCache::kPrimary) { | |
| 110 __ jmp(&miss); | |
| 111 } else if (FLAG_test_primary_stub_cache && table == StubCache::kSecondary) { | |
| 112 __ jmp(&miss); | |
| 113 } | |
| 114 #endif | |
| 115 | |
| 116 // Jump to the first instruction in the code stub. | |
| 117 __ add(pc, code, Operand(Code::kHeaderSize - kHeapObjectTag)); | |
| 118 | |
| 119 // Miss: fall through. | |
| 120 __ bind(&miss); | |
| 121 } | |
| 122 | |
| 123 | |
| 124 void StubCompiler::GenerateDictionaryNegativeLookup(MacroAssembler* masm, | 43 void StubCompiler::GenerateDictionaryNegativeLookup(MacroAssembler* masm, |
| 125 Label* miss_label, | 44 Label* miss_label, |
| 126 Register receiver, | 45 Register receiver, |
| 127 Handle<Name> name, | 46 Handle<Name> name, |
| 128 Register scratch0, | 47 Register scratch0, |
| 129 Register scratch1) { | 48 Register scratch1) { |
| 49 ASSERT(!AreAliased(receiver, scratch0, scratch1)); |
| 130 ASSERT(name->IsUniqueName()); | 50 ASSERT(name->IsUniqueName()); |
| 131 ASSERT(!receiver.is(scratch0)); | |
| 132 Counters* counters = masm->isolate()->counters(); | 51 Counters* counters = masm->isolate()->counters(); |
| 133 __ IncrementCounter(counters->negative_lookups(), 1, scratch0, scratch1); | 52 __ IncrementCounter(counters->negative_lookups(), 1, scratch0, scratch1); |
| 134 __ IncrementCounter(counters->negative_lookups_miss(), 1, scratch0, scratch1); | 53 __ IncrementCounter(counters->negative_lookups_miss(), 1, scratch0, scratch1); |
| 135 | 54 |
| 136 Label done; | 55 Label done; |
| 137 | 56 |
| 138 const int kInterceptorOrAccessCheckNeededMask = | 57 const int kInterceptorOrAccessCheckNeededMask = |
| 139 (1 << Map::kHasNamedInterceptor) | (1 << Map::kIsAccessCheckNeeded); | 58 (1 << Map::kHasNamedInterceptor) | (1 << Map::kIsAccessCheckNeeded); |
| 140 | 59 |
| 141 // Bail out if the receiver has a named interceptor or requires access checks. | 60 // Bail out if the receiver has a named interceptor or requires access checks. |
| 142 Register map = scratch1; | 61 Register map = scratch1; |
| 143 __ ldr(map, FieldMemOperand(receiver, HeapObject::kMapOffset)); | 62 __ Ldr(map, FieldMemOperand(receiver, HeapObject::kMapOffset)); |
| 144 __ ldrb(scratch0, FieldMemOperand(map, Map::kBitFieldOffset)); | 63 __ Ldrb(scratch0, FieldMemOperand(map, Map::kBitFieldOffset)); |
| 145 __ tst(scratch0, Operand(kInterceptorOrAccessCheckNeededMask)); | 64 __ Tst(scratch0, kInterceptorOrAccessCheckNeededMask); |
| 146 __ b(ne, miss_label); | 65 __ B(ne, miss_label); |
| 147 | 66 |
| 148 // Check that receiver is a JSObject. | 67 // Check that receiver is a JSObject. |
| 149 __ ldrb(scratch0, FieldMemOperand(map, Map::kInstanceTypeOffset)); | 68 __ Ldrb(scratch0, FieldMemOperand(map, Map::kInstanceTypeOffset)); |
| 150 __ cmp(scratch0, Operand(FIRST_SPEC_OBJECT_TYPE)); | 69 __ Cmp(scratch0, FIRST_SPEC_OBJECT_TYPE); |
| 151 __ b(lt, miss_label); | 70 __ B(lt, miss_label); |
| 152 | 71 |
| 153 // Load properties array. | 72 // Load properties array. |
| 154 Register properties = scratch0; | 73 Register properties = scratch0; |
| 155 __ ldr(properties, FieldMemOperand(receiver, JSObject::kPropertiesOffset)); | 74 __ Ldr(properties, FieldMemOperand(receiver, JSObject::kPropertiesOffset)); |
| 156 // Check that the properties array is a dictionary. | 75 // Check that the properties array is a dictionary. |
| 157 __ ldr(map, FieldMemOperand(properties, HeapObject::kMapOffset)); | 76 __ Ldr(map, FieldMemOperand(properties, HeapObject::kMapOffset)); |
| 158 Register tmp = properties; | 77 __ JumpIfNotRoot(map, Heap::kHashTableMapRootIndex, miss_label); |
| 159 __ LoadRoot(tmp, Heap::kHashTableMapRootIndex); | |
| 160 __ cmp(map, tmp); | |
| 161 __ b(ne, miss_label); | |
| 162 | |
| 163 // Restore the temporarily used register. | |
| 164 __ ldr(properties, FieldMemOperand(receiver, JSObject::kPropertiesOffset)); | |
| 165 | |
| 166 | 78 |
| 167 NameDictionaryLookupStub::GenerateNegativeLookup(masm, | 79 NameDictionaryLookupStub::GenerateNegativeLookup(masm, |
| 168 miss_label, | 80 miss_label, |
| 169 &done, | 81 &done, |
| 170 receiver, | 82 receiver, |
| 171 properties, | 83 properties, |
| 172 name, | 84 name, |
| 173 scratch1); | 85 scratch1); |
| 174 __ bind(&done); | 86 __ Bind(&done); |
| 175 __ DecrementCounter(counters->negative_lookups_miss(), 1, scratch0, scratch1); | 87 __ DecrementCounter(counters->negative_lookups_miss(), 1, scratch0, scratch1); |
| 176 } | 88 } |
| 177 | 89 |
| 178 | 90 |
| 91 // Probe primary or secondary table. |
| 92 // If the entry is found in the cache, the generated code jump to the first |
| 93 // instruction of the stub in the cache. |
| 94 // If there is a miss the code fall trough. |
| 95 // |
| 96 // 'receiver', 'name' and 'offset' registers are preserved on miss. |
| 97 static void ProbeTable(Isolate* isolate, |
| 98 MacroAssembler* masm, |
| 99 Code::Flags flags, |
| 100 StubCache::Table table, |
| 101 Register receiver, |
| 102 Register name, |
| 103 Register offset, |
| 104 Register scratch, |
| 105 Register scratch2, |
| 106 Register scratch3) { |
| 107 // Some code below relies on the fact that the Entry struct contains |
| 108 // 3 pointers (name, code, map). |
| 109 STATIC_ASSERT(sizeof(StubCache::Entry) == (3 * kPointerSize)); |
| 110 |
| 111 ExternalReference key_offset(isolate->stub_cache()->key_reference(table)); |
| 112 ExternalReference value_offset(isolate->stub_cache()->value_reference(table)); |
| 113 ExternalReference map_offset(isolate->stub_cache()->map_reference(table)); |
| 114 |
| 115 uintptr_t key_off_addr = reinterpret_cast<uintptr_t>(key_offset.address()); |
| 116 uintptr_t value_off_addr = |
| 117 reinterpret_cast<uintptr_t>(value_offset.address()); |
| 118 uintptr_t map_off_addr = reinterpret_cast<uintptr_t>(map_offset.address()); |
| 119 |
| 120 Label miss; |
| 121 |
| 122 ASSERT(!AreAliased(name, offset, scratch, scratch2, scratch3)); |
| 123 |
| 124 // Multiply by 3 because there are 3 fields per entry. |
| 125 __ Add(scratch3, offset, Operand(offset, LSL, 1)); |
| 126 |
| 127 // Calculate the base address of the entry. |
| 128 __ Mov(scratch, Operand(key_offset)); |
| 129 __ Add(scratch, scratch, Operand(scratch3, LSL, kPointerSizeLog2)); |
| 130 |
| 131 // Check that the key in the entry matches the name. |
| 132 __ Ldr(scratch2, MemOperand(scratch)); |
| 133 __ Cmp(name, scratch2); |
| 134 __ B(ne, &miss); |
| 135 |
| 136 // Check the map matches. |
| 137 __ Ldr(scratch2, MemOperand(scratch, map_off_addr - key_off_addr)); |
| 138 __ Ldr(scratch3, FieldMemOperand(receiver, HeapObject::kMapOffset)); |
| 139 __ Cmp(scratch2, scratch3); |
| 140 __ B(ne, &miss); |
| 141 |
| 142 // Get the code entry from the cache. |
| 143 __ Ldr(scratch, MemOperand(scratch, value_off_addr - key_off_addr)); |
| 144 |
| 145 // Check that the flags match what we're looking for. |
| 146 __ Ldr(scratch2.W(), FieldMemOperand(scratch, Code::kFlagsOffset)); |
| 147 __ Bic(scratch2.W(), scratch2.W(), Code::kFlagsNotUsedInLookup); |
| 148 __ Cmp(scratch2.W(), flags); |
| 149 __ B(ne, &miss); |
| 150 |
| 151 #ifdef DEBUG |
| 152 if (FLAG_test_secondary_stub_cache && table == StubCache::kPrimary) { |
| 153 __ B(&miss); |
| 154 } else if (FLAG_test_primary_stub_cache && table == StubCache::kSecondary) { |
| 155 __ B(&miss); |
| 156 } |
| 157 #endif |
| 158 |
| 159 // Jump to the first instruction in the code stub. |
| 160 __ Add(scratch, scratch, Code::kHeaderSize - kHeapObjectTag); |
| 161 __ Br(scratch); |
| 162 |
| 163 // Miss: fall through. |
| 164 __ Bind(&miss); |
| 165 } |
| 166 |
| 167 |
| 179 void StubCache::GenerateProbe(MacroAssembler* masm, | 168 void StubCache::GenerateProbe(MacroAssembler* masm, |
| 180 Code::Flags flags, | 169 Code::Flags flags, |
| 181 Register receiver, | 170 Register receiver, |
| 182 Register name, | 171 Register name, |
| 183 Register scratch, | 172 Register scratch, |
| 184 Register extra, | 173 Register extra, |
| 185 Register extra2, | 174 Register extra2, |
| 186 Register extra3) { | 175 Register extra3) { |
| 187 Isolate* isolate = masm->isolate(); | 176 Isolate* isolate = masm->isolate(); |
| 188 Label miss; | 177 Label miss; |
| 189 | 178 |
| 190 // Make sure that code is valid. The multiplying code relies on the | |
| 191 // entry size being 12. | |
| 192 ASSERT(sizeof(Entry) == 12); | |
| 193 | |
| 194 // Make sure the flags does not name a specific type. | 179 // Make sure the flags does not name a specific type. |
| 195 ASSERT(Code::ExtractTypeFromFlags(flags) == 0); | 180 ASSERT(Code::ExtractTypeFromFlags(flags) == 0); |
| 196 | 181 |
| 197 // Make sure that there are no register conflicts. | 182 // Make sure that there are no register conflicts. |
| 198 ASSERT(!scratch.is(receiver)); | 183 ASSERT(!AreAliased(receiver, name, scratch, extra, extra2, extra3)); |
| 199 ASSERT(!scratch.is(name)); | |
| 200 ASSERT(!extra.is(receiver)); | |
| 201 ASSERT(!extra.is(name)); | |
| 202 ASSERT(!extra.is(scratch)); | |
| 203 ASSERT(!extra2.is(receiver)); | |
| 204 ASSERT(!extra2.is(name)); | |
| 205 ASSERT(!extra2.is(scratch)); | |
| 206 ASSERT(!extra2.is(extra)); | |
| 207 | 184 |
| 208 // Check scratch, extra and extra2 registers are valid. | 185 // Make sure extra and extra2 registers are valid. |
| 209 ASSERT(!scratch.is(no_reg)); | |
| 210 ASSERT(!extra.is(no_reg)); | 186 ASSERT(!extra.is(no_reg)); |
| 211 ASSERT(!extra2.is(no_reg)); | 187 ASSERT(!extra2.is(no_reg)); |
| 212 ASSERT(!extra3.is(no_reg)); | 188 ASSERT(!extra3.is(no_reg)); |
| 213 | 189 |
| 214 Counters* counters = masm->isolate()->counters(); | 190 Counters* counters = masm->isolate()->counters(); |
| 215 __ IncrementCounter(counters->megamorphic_stub_cache_probes(), 1, | 191 __ IncrementCounter(counters->megamorphic_stub_cache_probes(), 1, |
| 216 extra2, extra3); | 192 extra2, extra3); |
| 217 | 193 |
| 218 // Check that the receiver isn't a smi. | 194 // Check that the receiver isn't a smi. |
| 219 __ JumpIfSmi(receiver, &miss); | 195 __ JumpIfSmi(receiver, &miss); |
| 220 | 196 |
| 221 // Get the map of the receiver and compute the hash. | 197 // Compute the hash for primary table. |
| 222 __ ldr(scratch, FieldMemOperand(name, Name::kHashFieldOffset)); | 198 __ Ldr(scratch, FieldMemOperand(name, Name::kHashFieldOffset)); |
| 223 __ ldr(ip, FieldMemOperand(receiver, HeapObject::kMapOffset)); | 199 __ Ldr(extra, FieldMemOperand(receiver, HeapObject::kMapOffset)); |
| 224 __ add(scratch, scratch, Operand(ip)); | 200 __ Add(scratch, scratch, extra); |
| 225 uint32_t mask = kPrimaryTableSize - 1; | 201 __ Eor(scratch, scratch, flags); |
| 226 // We shift out the last two bits because they are not part of the hash and | 202 // We shift out the last two bits because they are not part of the hash. |
| 227 // they are always 01 for maps. | 203 __ Ubfx(scratch, scratch, kHeapObjectTagSize, |
| 228 __ mov(scratch, Operand(scratch, LSR, kHeapObjectTagSize)); | 204 CountTrailingZeros(kPrimaryTableSize, 64)); |
| 229 // Mask down the eor argument to the minimum to keep the immediate | |
| 230 // ARM-encodable. | |
| 231 __ eor(scratch, scratch, Operand((flags >> kHeapObjectTagSize) & mask)); | |
| 232 // Prefer and_ to ubfx here because ubfx takes 2 cycles. | |
| 233 __ and_(scratch, scratch, Operand(mask)); | |
| 234 | 205 |
| 235 // Probe the primary table. | 206 // Probe the primary table. |
| 236 ProbeTable(isolate, | 207 ProbeTable(isolate, masm, flags, kPrimary, receiver, name, |
| 237 masm, | 208 scratch, extra, extra2, extra3); |
| 238 flags, | |
| 239 kPrimary, | |
| 240 receiver, | |
| 241 name, | |
| 242 scratch, | |
| 243 extra, | |
| 244 extra2, | |
| 245 extra3); | |
| 246 | 209 |
| 247 // Primary miss: Compute hash for secondary probe. | 210 // Primary miss: Compute hash for secondary table. |
| 248 __ sub(scratch, scratch, Operand(name, LSR, kHeapObjectTagSize)); | 211 __ Sub(scratch, scratch, Operand(name, LSR, kHeapObjectTagSize)); |
| 249 uint32_t mask2 = kSecondaryTableSize - 1; | 212 __ Add(scratch, scratch, flags >> kHeapObjectTagSize); |
| 250 __ add(scratch, scratch, Operand((flags >> kHeapObjectTagSize) & mask2)); | 213 __ And(scratch, scratch, kSecondaryTableSize - 1); |
| 251 __ and_(scratch, scratch, Operand(mask2)); | |
| 252 | 214 |
| 253 // Probe the secondary table. | 215 // Probe the secondary table. |
| 254 ProbeTable(isolate, | 216 ProbeTable(isolate, masm, flags, kSecondary, receiver, name, |
| 255 masm, | 217 scratch, extra, extra2, extra3); |
| 256 flags, | |
| 257 kSecondary, | |
| 258 receiver, | |
| 259 name, | |
| 260 scratch, | |
| 261 extra, | |
| 262 extra2, | |
| 263 extra3); | |
| 264 | 218 |
| 265 // Cache miss: Fall-through and let caller handle the miss by | 219 // Cache miss: Fall-through and let caller handle the miss by |
| 266 // entering the runtime system. | 220 // entering the runtime system. |
| 267 __ bind(&miss); | 221 __ Bind(&miss); |
| 268 __ IncrementCounter(counters->megamorphic_stub_cache_misses(), 1, | 222 __ IncrementCounter(counters->megamorphic_stub_cache_misses(), 1, |
| 269 extra2, extra3); | 223 extra2, extra3); |
| 270 } | 224 } |
| 271 | 225 |
| 272 | 226 |
| 273 void StubCompiler::GenerateLoadGlobalFunctionPrototype(MacroAssembler* masm, | 227 void StubCompiler::GenerateLoadGlobalFunctionPrototype(MacroAssembler* masm, |
| 274 int index, | 228 int index, |
| 275 Register prototype) { | 229 Register prototype) { |
| 276 // Load the global or builtins object from the current context. | 230 // Load the global or builtins object from the current context. |
| 277 __ ldr(prototype, | 231 __ Ldr(prototype, GlobalObjectMemOperand()); |
| 278 MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX))); | |
| 279 // Load the native context from the global or builtins object. | 232 // Load the native context from the global or builtins object. |
| 280 __ ldr(prototype, | 233 __ Ldr(prototype, |
| 281 FieldMemOperand(prototype, GlobalObject::kNativeContextOffset)); | 234 FieldMemOperand(prototype, GlobalObject::kNativeContextOffset)); |
| 282 // Load the function from the native context. | 235 // Load the function from the native context. |
| 283 __ ldr(prototype, MemOperand(prototype, Context::SlotOffset(index))); | 236 __ Ldr(prototype, ContextMemOperand(prototype, index)); |
| 284 // Load the initial map. The global functions all have initial maps. | 237 // Load the initial map. The global functions all have initial maps. |
| 285 __ ldr(prototype, | 238 __ Ldr(prototype, |
| 286 FieldMemOperand(prototype, JSFunction::kPrototypeOrInitialMapOffset)); | 239 FieldMemOperand(prototype, JSFunction::kPrototypeOrInitialMapOffset)); |
| 287 // Load the prototype from the initial map. | 240 // Load the prototype from the initial map. |
| 288 __ ldr(prototype, FieldMemOperand(prototype, Map::kPrototypeOffset)); | 241 __ Ldr(prototype, FieldMemOperand(prototype, Map::kPrototypeOffset)); |
| 289 } | 242 } |
| 290 | 243 |
| 291 | 244 |
| 292 void StubCompiler::GenerateDirectLoadGlobalFunctionPrototype( | 245 void StubCompiler::GenerateDirectLoadGlobalFunctionPrototype( |
| 293 MacroAssembler* masm, | 246 MacroAssembler* masm, |
| 294 int index, | 247 int index, |
| 295 Register prototype, | 248 Register prototype, |
| 296 Label* miss) { | 249 Label* miss) { |
| 297 Isolate* isolate = masm->isolate(); | 250 Isolate* isolate = masm->isolate(); |
| 298 // Get the global function with the given index. | 251 // Get the global function with the given index. |
| 299 Handle<JSFunction> function( | 252 Handle<JSFunction> function( |
| 300 JSFunction::cast(isolate->native_context()->get(index))); | 253 JSFunction::cast(isolate->native_context()->get(index))); |
| 301 | 254 |
| 302 // Check we're still in the same context. | 255 // Check we're still in the same context. |
| 303 Register scratch = prototype; | 256 Register scratch = prototype; |
| 304 const int offset = Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX); | 257 __ Ldr(scratch, GlobalObjectMemOperand()); |
| 305 __ ldr(scratch, MemOperand(cp, offset)); | 258 __ Ldr(scratch, FieldMemOperand(scratch, GlobalObject::kNativeContextOffset)); |
| 306 __ ldr(scratch, FieldMemOperand(scratch, GlobalObject::kNativeContextOffset)); | 259 __ Ldr(scratch, ContextMemOperand(scratch, index)); |
| 307 __ ldr(scratch, MemOperand(scratch, Context::SlotOffset(index))); | 260 __ Cmp(scratch, Operand(function)); |
| 308 __ Move(ip, function); | 261 __ B(ne, miss); |
| 309 __ cmp(ip, scratch); | |
| 310 __ b(ne, miss); | |
| 311 | 262 |
| 312 // Load its initial map. The global functions all have initial maps. | 263 // Load its initial map. The global functions all have initial maps. |
| 313 __ Move(prototype, Handle<Map>(function->initial_map())); | 264 __ Mov(prototype, Operand(Handle<Map>(function->initial_map()))); |
| 314 // Load the prototype from the initial map. | 265 // Load the prototype from the initial map. |
| 315 __ ldr(prototype, FieldMemOperand(prototype, Map::kPrototypeOffset)); | 266 __ Ldr(prototype, FieldMemOperand(prototype, Map::kPrototypeOffset)); |
| 316 } | 267 } |
| 317 | 268 |
| 318 | 269 |
| 319 void StubCompiler::GenerateFastPropertyLoad(MacroAssembler* masm, | 270 void StubCompiler::GenerateFastPropertyLoad(MacroAssembler* masm, |
| 320 Register dst, | 271 Register dst, |
| 321 Register src, | 272 Register src, |
| 322 bool inobject, | 273 bool inobject, |
| 323 int index, | 274 int index, |
| 324 Representation representation) { | 275 Representation representation) { |
| 325 ASSERT(!FLAG_track_double_fields || !representation.IsDouble()); | 276 ASSERT(!FLAG_track_double_fields || !representation.IsDouble()); |
| 326 int offset = index * kPointerSize; | 277 USE(representation); |
| 327 if (!inobject) { | 278 if (inobject) { |
| 279 int offset = index * kPointerSize; |
| 280 __ Ldr(dst, FieldMemOperand(src, offset)); |
| 281 } else { |
| 328 // Calculate the offset into the properties array. | 282 // Calculate the offset into the properties array. |
| 329 offset = offset + FixedArray::kHeaderSize; | 283 int offset = index * kPointerSize + FixedArray::kHeaderSize; |
| 330 __ ldr(dst, FieldMemOperand(src, JSObject::kPropertiesOffset)); | 284 __ Ldr(dst, FieldMemOperand(src, JSObject::kPropertiesOffset)); |
| 331 src = dst; | 285 __ Ldr(dst, FieldMemOperand(dst, offset)); |
| 332 } | 286 } |
| 333 __ ldr(dst, FieldMemOperand(src, offset)); | |
| 334 } | 287 } |
| 335 | 288 |
| 336 | 289 |
| 337 void StubCompiler::GenerateLoadArrayLength(MacroAssembler* masm, | 290 void StubCompiler::GenerateLoadArrayLength(MacroAssembler* masm, |
| 338 Register receiver, | 291 Register receiver, |
| 339 Register scratch, | 292 Register scratch, |
| 340 Label* miss_label) { | 293 Label* miss_label) { |
| 294 ASSERT(!AreAliased(receiver, scratch)); |
| 295 |
| 341 // Check that the receiver isn't a smi. | 296 // Check that the receiver isn't a smi. |
| 342 __ JumpIfSmi(receiver, miss_label); | 297 __ JumpIfSmi(receiver, miss_label); |
| 343 | 298 |
| 344 // Check that the object is a JS array. | 299 // Check that the object is a JS array. |
| 345 __ CompareObjectType(receiver, scratch, scratch, JS_ARRAY_TYPE); | 300 __ JumpIfNotObjectType(receiver, scratch, scratch, JS_ARRAY_TYPE, |
| 346 __ b(ne, miss_label); | 301 miss_label); |
| 347 | 302 |
| 348 // Load length directly from the JS array. | 303 // Load length directly from the JS array. |
| 349 __ ldr(r0, FieldMemOperand(receiver, JSArray::kLengthOffset)); | 304 __ Ldr(x0, FieldMemOperand(receiver, JSArray::kLengthOffset)); |
| 350 __ Ret(); | 305 __ Ret(); |
| 351 } | 306 } |
| 352 | 307 |
| 353 | 308 |
| 354 // Generate code to check if an object is a string. If the object is a | 309 // Generate code to check if an object is a string. If the object is a |
| 355 // heap object, its map's instance type is left in the scratch1 register. | 310 // heap object, its map's instance type is left in the scratch1 register. |
| 356 // If this is not needed, scratch1 and scratch2 may be the same register. | |
| 357 static void GenerateStringCheck(MacroAssembler* masm, | 311 static void GenerateStringCheck(MacroAssembler* masm, |
| 358 Register receiver, | 312 Register receiver, |
| 359 Register scratch1, | 313 Register scratch1, |
| 360 Register scratch2, | |
| 361 Label* smi, | 314 Label* smi, |
| 362 Label* non_string_object) { | 315 Label* non_string_object) { |
| 363 // Check that the receiver isn't a smi. | 316 // Check that the receiver isn't a smi. |
| 364 __ JumpIfSmi(receiver, smi); | 317 __ JumpIfSmi(receiver, smi); |
| 365 | 318 |
| 366 // Check that the object is a string. | 319 // Get the object's instance type filed. |
| 367 __ ldr(scratch1, FieldMemOperand(receiver, HeapObject::kMapOffset)); | 320 __ Ldr(scratch1, FieldMemOperand(receiver, HeapObject::kMapOffset)); |
| 368 __ ldrb(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset)); | 321 __ Ldrb(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset)); |
| 369 __ and_(scratch2, scratch1, Operand(kIsNotStringMask)); | 322 // Check if the "not string" bit is set. |
| 370 // The cast is to resolve the overload for the argument of 0x0. | 323 __ Tbnz(scratch1, MaskToBit(kNotStringTag), non_string_object); |
| 371 __ cmp(scratch2, Operand(static_cast<int32_t>(kStringTag))); | |
| 372 __ b(ne, non_string_object); | |
| 373 } | 324 } |
| 374 | 325 |
| 375 | 326 |
| 376 // Generate code to load the length from a string object and return the length. | 327 // Generate code to load the length from a string object and return the length. |
| 377 // If the receiver object is not a string or a wrapped string object the | 328 // If the receiver object is not a string or a wrapped string object the |
| 378 // execution continues at the miss label. The register containing the | 329 // execution continues at the miss label. The register containing the |
| 379 // receiver is potentially clobbered. | 330 // receiver is not clobbered if the receiver is not a string. |
| 380 void StubCompiler::GenerateLoadStringLength(MacroAssembler* masm, | 331 void StubCompiler::GenerateLoadStringLength(MacroAssembler* masm, |
| 381 Register receiver, | 332 Register receiver, |
| 382 Register scratch1, | 333 Register scratch1, |
| 383 Register scratch2, | 334 Register scratch2, |
| 384 Label* miss) { | 335 Label* miss) { |
| 336 // Input registers can't alias because we don't want to clobber the |
| 337 // receiver register if the object is not a string. |
| 338 ASSERT(!AreAliased(receiver, scratch1, scratch2)); |
| 339 |
| 385 Label check_wrapper; | 340 Label check_wrapper; |
| 386 | 341 |
| 387 // Check if the object is a string leaving the instance type in the | 342 // Check if the object is a string leaving the instance type in the |
| 388 // scratch1 register. | 343 // scratch1 register. |
| 389 GenerateStringCheck(masm, receiver, scratch1, scratch2, miss, &check_wrapper); | 344 GenerateStringCheck(masm, receiver, scratch1, miss, &check_wrapper); |
| 390 | 345 |
| 391 // Load length directly from the string. | 346 // Load length directly from the string. |
| 392 __ ldr(r0, FieldMemOperand(receiver, String::kLengthOffset)); | 347 __ Ldr(x0, FieldMemOperand(receiver, String::kLengthOffset)); |
| 393 __ Ret(); | 348 __ Ret(); |
| 394 | 349 |
| 395 // Check if the object is a JSValue wrapper. | 350 // Check if the object is a JSValue wrapper. |
| 396 __ bind(&check_wrapper); | 351 __ Bind(&check_wrapper); |
| 397 __ cmp(scratch1, Operand(JS_VALUE_TYPE)); | 352 __ Cmp(scratch1, Operand(JS_VALUE_TYPE)); |
| 398 __ b(ne, miss); | 353 __ B(ne, miss); |
| 399 | 354 |
| 400 // Unwrap the value and check if the wrapped value is a string. | 355 // Unwrap the value and check if the wrapped value is a string. |
| 401 __ ldr(scratch1, FieldMemOperand(receiver, JSValue::kValueOffset)); | 356 __ Ldr(scratch1, FieldMemOperand(receiver, JSValue::kValueOffset)); |
| 402 GenerateStringCheck(masm, scratch1, scratch2, scratch2, miss, miss); | 357 GenerateStringCheck(masm, scratch1, scratch2, miss, miss); |
| 403 __ ldr(r0, FieldMemOperand(scratch1, String::kLengthOffset)); | 358 __ Ldr(x0, FieldMemOperand(scratch1, String::kLengthOffset)); |
| 404 __ Ret(); | 359 __ Ret(); |
| 405 } | 360 } |
| 406 | 361 |
| 407 | 362 |
| 408 void StubCompiler::GenerateLoadFunctionPrototype(MacroAssembler* masm, | 363 void StubCompiler::GenerateLoadFunctionPrototype(MacroAssembler* masm, |
| 409 Register receiver, | 364 Register receiver, |
| 410 Register scratch1, | 365 Register scratch1, |
| 411 Register scratch2, | 366 Register scratch2, |
| 412 Label* miss_label) { | 367 Label* miss_label) { |
| 413 __ TryGetFunctionPrototype(receiver, scratch1, scratch2, miss_label); | 368 __ TryGetFunctionPrototype(receiver, scratch1, scratch2, miss_label); |
| 414 __ mov(r0, scratch1); | 369 // TryGetFunctionPrototype can't put the result directly in x0 because the |
| 370 // 3 inputs registers can't alias and we call this function from |
| 371 // LoadIC::GenerateFunctionPrototype, where receiver is x0. So we explicitly |
| 372 // move the result in x0. |
| 373 __ Mov(x0, scratch1); |
| 415 __ Ret(); | 374 __ Ret(); |
| 416 } | 375 } |
| 417 | 376 |
| 418 | 377 |
| 419 // Generate code to check that a global property cell is empty. Create | 378 // Generate code to check that a global property cell is empty. Create |
| 420 // the property cell at compilation time if no cell exists for the | 379 // the property cell at compilation time if no cell exists for the |
| 421 // property. | 380 // property. |
| 422 void StubCompiler::GenerateCheckPropertyCell(MacroAssembler* masm, | 381 void StubCompiler::GenerateCheckPropertyCell(MacroAssembler* masm, |
| 423 Handle<JSGlobalObject> global, | 382 Handle<JSGlobalObject> global, |
| 424 Handle<Name> name, | 383 Handle<Name> name, |
| 425 Register scratch, | 384 Register scratch, |
| 426 Label* miss) { | 385 Label* miss) { |
| 427 Handle<Cell> cell = JSGlobalObject::EnsurePropertyCell(global, name); | 386 Handle<Cell> cell = JSGlobalObject::EnsurePropertyCell(global, name); |
| 428 ASSERT(cell->value()->IsTheHole()); | 387 ASSERT(cell->value()->IsTheHole()); |
| 429 __ mov(scratch, Operand(cell)); | 388 __ Mov(scratch, Operand(cell)); |
| 430 __ ldr(scratch, FieldMemOperand(scratch, Cell::kValueOffset)); | 389 __ Ldr(scratch, FieldMemOperand(scratch, Cell::kValueOffset)); |
| 431 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); | 390 __ JumpIfNotRoot(scratch, Heap::kTheHoleValueRootIndex, miss); |
| 432 __ cmp(scratch, ip); | |
| 433 __ b(ne, miss); | |
| 434 } | 391 } |
| 435 | 392 |
| 436 | 393 |
| 437 void StoreStubCompiler::GenerateNegativeHolderLookup( | 394 void StoreStubCompiler::GenerateNegativeHolderLookup( |
| 438 MacroAssembler* masm, | 395 MacroAssembler* masm, |
| 439 Handle<JSObject> holder, | 396 Handle<JSObject> holder, |
| 440 Register holder_reg, | 397 Register holder_reg, |
| 441 Handle<Name> name, | 398 Handle<Name> name, |
| 442 Label* miss) { | 399 Label* miss) { |
| 443 if (holder->IsJSGlobalObject()) { | 400 if (holder->IsJSGlobalObject()) { |
| 444 GenerateCheckPropertyCell( | 401 GenerateCheckPropertyCell( |
| 445 masm, Handle<JSGlobalObject>::cast(holder), name, scratch1(), miss); | 402 masm, Handle<JSGlobalObject>::cast(holder), name, scratch1(), miss); |
| 446 } else if (!holder->HasFastProperties() && !holder->IsJSGlobalProxy()) { | 403 } else if (!holder->HasFastProperties() && !holder->IsJSGlobalProxy()) { |
| 447 GenerateDictionaryNegativeLookup( | 404 GenerateDictionaryNegativeLookup( |
| 448 masm, miss, holder_reg, name, scratch1(), scratch2()); | 405 masm, miss, holder_reg, name, scratch1(), scratch2()); |
| 449 } | 406 } |
| 450 } | 407 } |
| 451 | 408 |
| 452 | 409 |
| 453 // Generate StoreTransition code, value is passed in r0 register. | 410 // Generate StoreTransition code, value is passed in x0 register. |
| 454 // When leaving generated code after success, the receiver_reg and name_reg | 411 // When leaving generated code after success, the receiver_reg and storage_reg |
| 455 // may be clobbered. Upon branch to miss_label, the receiver and name | 412 // may be clobbered. Upon branch to miss_label, the receiver and name registers |
| 456 // registers have their original values. | 413 // have their original values. |
| 457 void StoreStubCompiler::GenerateStoreTransition(MacroAssembler* masm, | 414 void StoreStubCompiler::GenerateStoreTransition(MacroAssembler* masm, |
| 458 Handle<JSObject> object, | 415 Handle<JSObject> object, |
| 459 LookupResult* lookup, | 416 LookupResult* lookup, |
| 460 Handle<Map> transition, | 417 Handle<Map> transition, |
| 461 Handle<Name> name, | 418 Handle<Name> name, |
| 462 Register receiver_reg, | 419 Register receiver_reg, |
| 463 Register storage_reg, | 420 Register storage_reg, |
| 464 Register value_reg, | 421 Register value_reg, |
| 465 Register scratch1, | 422 Register scratch1, |
| 466 Register scratch2, | 423 Register scratch2, |
| 467 Register scratch3, | 424 Register scratch3, |
| 468 Label* miss_label, | 425 Label* miss_label, |
| 469 Label* slow) { | 426 Label* slow) { |
| 470 // r0 : value | |
| 471 Label exit; | 427 Label exit; |
| 472 | 428 |
| 429 ASSERT(!AreAliased(receiver_reg, storage_reg, value_reg, |
| 430 scratch1, scratch2, scratch3)); |
| 431 |
| 432 // We don't need scratch3. |
| 433 scratch3 = NoReg; |
| 434 |
| 473 int descriptor = transition->LastAdded(); | 435 int descriptor = transition->LastAdded(); |
| 474 DescriptorArray* descriptors = transition->instance_descriptors(); | 436 DescriptorArray* descriptors = transition->instance_descriptors(); |
| 475 PropertyDetails details = descriptors->GetDetails(descriptor); | 437 PropertyDetails details = descriptors->GetDetails(descriptor); |
| 476 Representation representation = details.representation(); | 438 Representation representation = details.representation(); |
| 477 ASSERT(!representation.IsNone()); | 439 ASSERT(!representation.IsNone()); |
| 478 | 440 |
| 479 if (details.type() == CONSTANT) { | 441 if (details.type() == CONSTANT) { |
| 480 Handle<Object> constant(descriptors->GetValue(descriptor), masm->isolate()); | 442 Handle<Object> constant(descriptors->GetValue(descriptor), masm->isolate()); |
| 481 __ Move(scratch1, constant); | 443 __ LoadObject(scratch1, constant); |
| 482 __ cmp(value_reg, scratch1); | 444 __ Cmp(value_reg, scratch1); |
| 483 __ b(ne, miss_label); | 445 __ B(ne, miss_label); |
| 484 } else if (FLAG_track_fields && representation.IsSmi()) { | 446 } else if (FLAG_track_fields && representation.IsSmi()) { |
| 485 __ JumpIfNotSmi(value_reg, miss_label); | 447 __ JumpIfNotSmi(value_reg, miss_label); |
| 486 } else if (FLAG_track_heap_object_fields && representation.IsHeapObject()) { | 448 } else if (FLAG_track_heap_object_fields && representation.IsHeapObject()) { |
| 487 __ JumpIfSmi(value_reg, miss_label); | 449 __ JumpIfSmi(value_reg, miss_label); |
| 488 } else if (FLAG_track_double_fields && representation.IsDouble()) { | 450 } else if (FLAG_track_double_fields && representation.IsDouble()) { |
| 489 Label do_store, heap_number; | 451 Label do_store, heap_number; |
| 490 __ LoadRoot(scratch3, Heap::kHeapNumberMapRootIndex); | 452 __ AllocateHeapNumber(storage_reg, slow, scratch1, scratch2); |
| 491 __ AllocateHeapNumber(storage_reg, scratch1, scratch2, scratch3, slow); | |
| 492 | 453 |
| 454 // TODO(jbramley): Is fp_scratch the most appropriate FP scratch register? |
| 455 // It's only used in Fcmp, but it's not really safe to use it like this. |
| 493 __ JumpIfNotSmi(value_reg, &heap_number); | 456 __ JumpIfNotSmi(value_reg, &heap_number); |
| 494 __ SmiUntag(scratch1, value_reg); | 457 __ SmiUntagToDouble(fp_scratch, value_reg); |
| 495 __ vmov(s0, scratch1); | 458 __ B(&do_store); |
| 496 __ vcvt_f64_s32(d0, s0); | |
| 497 __ jmp(&do_store); | |
| 498 | 459 |
| 499 __ bind(&heap_number); | 460 __ Bind(&heap_number); |
| 500 __ CheckMap(value_reg, scratch1, Heap::kHeapNumberMapRootIndex, | 461 __ CheckMap(value_reg, scratch1, Heap::kHeapNumberMapRootIndex, |
| 501 miss_label, DONT_DO_SMI_CHECK); | 462 miss_label, DONT_DO_SMI_CHECK); |
| 502 __ vldr(d0, FieldMemOperand(value_reg, HeapNumber::kValueOffset)); | 463 __ Ldr(fp_scratch, FieldMemOperand(value_reg, HeapNumber::kValueOffset)); |
| 503 | 464 |
| 504 __ bind(&do_store); | 465 __ Bind(&do_store); |
| 505 __ vstr(d0, FieldMemOperand(storage_reg, HeapNumber::kValueOffset)); | 466 __ Str(fp_scratch, FieldMemOperand(storage_reg, HeapNumber::kValueOffset)); |
| 506 } | 467 } |
| 507 | 468 |
| 508 // Stub never generated for non-global objects that require access | 469 // Stub never generated for non-global objects that require access checks. |
| 509 // checks. | |
| 510 ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded()); | 470 ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded()); |
| 511 | 471 |
| 512 // Perform map transition for the receiver if necessary. | 472 // Perform map transition for the receiver if necessary. |
| 513 if (details.type() == FIELD && | 473 if ((details.type() == FIELD) && |
| 514 object->map()->unused_property_fields() == 0) { | 474 (object->map()->unused_property_fields() == 0)) { |
| 515 // The properties must be extended before we can store the value. | 475 // The properties must be extended before we can store the value. |
| 516 // We jump to a runtime call that extends the properties array. | 476 // We jump to a runtime call that extends the properties array. |
| 517 __ push(receiver_reg); | 477 __ Mov(scratch1, Operand(transition)); |
| 518 __ mov(r2, Operand(transition)); | 478 __ Push(receiver_reg, scratch1, value_reg); |
| 519 __ Push(r2, r0); | |
| 520 __ TailCallExternalReference( | 479 __ TailCallExternalReference( |
| 521 ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage), | 480 ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage), |
| 522 masm->isolate()), | 481 masm->isolate()), |
| 523 3, | 482 3, |
| 524 1); | 483 1); |
| 525 return; | 484 return; |
| 526 } | 485 } |
| 527 | 486 |
| 528 // Update the map of the object. | 487 // Update the map of the object. |
| 529 __ mov(scratch1, Operand(transition)); | 488 __ Mov(scratch1, Operand(transition)); |
| 530 __ str(scratch1, FieldMemOperand(receiver_reg, HeapObject::kMapOffset)); | 489 __ Str(scratch1, FieldMemOperand(receiver_reg, HeapObject::kMapOffset)); |
| 531 | 490 |
| 532 // Update the write barrier for the map field. | 491 // Update the write barrier for the map field. |
| 533 __ RecordWriteField(receiver_reg, | 492 __ RecordWriteField(receiver_reg, |
| 534 HeapObject::kMapOffset, | 493 HeapObject::kMapOffset, |
| 535 scratch1, | 494 scratch1, |
| 536 scratch2, | 495 scratch2, |
| 537 kLRHasNotBeenSaved, | 496 kLRHasNotBeenSaved, |
| 538 kDontSaveFPRegs, | 497 kDontSaveFPRegs, |
| 539 OMIT_REMEMBERED_SET, | 498 OMIT_REMEMBERED_SET, |
| 540 OMIT_SMI_CHECK); | 499 OMIT_SMI_CHECK); |
| 541 | 500 |
| 542 if (details.type() == CONSTANT) { | 501 if (details.type() == CONSTANT) { |
| 543 ASSERT(value_reg.is(r0)); | 502 ASSERT(value_reg.is(x0)); |
| 544 __ Ret(); | 503 __ Ret(); |
| 545 return; | 504 return; |
| 546 } | 505 } |
| 547 | 506 |
| 548 int index = transition->instance_descriptors()->GetFieldIndex( | 507 int index = transition->instance_descriptors()->GetFieldIndex( |
| 549 transition->LastAdded()); | 508 transition->LastAdded()); |
| 550 | 509 |
| 551 // Adjust for the number of properties stored in the object. Even in the | 510 // Adjust for the number of properties stored in the object. Even in the |
| 552 // face of a transition we can use the old map here because the size of the | 511 // face of a transition we can use the old map here because the size of the |
| 553 // object and the number of in-object properties is not going to change. | 512 // object and the number of in-object properties is not going to change. |
| 554 index -= object->map()->inobject_properties(); | 513 index -= object->map()->inobject_properties(); |
| 555 | 514 |
| 556 // TODO(verwaest): Share this code as a code stub. | 515 // TODO(verwaest): Share this code as a code stub. |
| 557 SmiCheck smi_check = representation.IsTagged() | 516 SmiCheck smi_check = representation.IsTagged() |
| 558 ? INLINE_SMI_CHECK : OMIT_SMI_CHECK; | 517 ? INLINE_SMI_CHECK : OMIT_SMI_CHECK; |
| 559 if (index < 0) { | 518 if (index < 0) { |
| 560 // Set the property straight into the object. | 519 // Set the property straight into the object. |
| 561 int offset = object->map()->instance_size() + (index * kPointerSize); | 520 int offset = object->map()->instance_size() + (index * kPointerSize); |
| 521 // TODO(jbramley): This construct appears in several places in this |
| 522 // function. Try to clean it up, perhaps using a result_reg. |
| 562 if (FLAG_track_double_fields && representation.IsDouble()) { | 523 if (FLAG_track_double_fields && representation.IsDouble()) { |
| 563 __ str(storage_reg, FieldMemOperand(receiver_reg, offset)); | 524 __ Str(storage_reg, FieldMemOperand(receiver_reg, offset)); |
| 564 } else { | 525 } else { |
| 565 __ str(value_reg, FieldMemOperand(receiver_reg, offset)); | 526 __ Str(value_reg, FieldMemOperand(receiver_reg, offset)); |
| 566 } | 527 } |
| 567 | 528 |
| 568 if (!FLAG_track_fields || !representation.IsSmi()) { | 529 if (!FLAG_track_fields || !representation.IsSmi()) { |
| 569 // Update the write barrier for the array address. | 530 // Update the write barrier for the array address. |
| 570 if (!FLAG_track_double_fields || !representation.IsDouble()) { | 531 if (!FLAG_track_double_fields || !representation.IsDouble()) { |
| 571 __ mov(storage_reg, value_reg); | 532 __ Mov(storage_reg, value_reg); |
| 572 } | 533 } |
| 573 __ RecordWriteField(receiver_reg, | 534 __ RecordWriteField(receiver_reg, |
| 574 offset, | 535 offset, |
| 575 storage_reg, | 536 storage_reg, |
| 576 scratch1, | 537 scratch1, |
| 577 kLRHasNotBeenSaved, | 538 kLRHasNotBeenSaved, |
| 578 kDontSaveFPRegs, | 539 kDontSaveFPRegs, |
| 579 EMIT_REMEMBERED_SET, | 540 EMIT_REMEMBERED_SET, |
| 580 smi_check); | 541 smi_check); |
| 581 } | 542 } |
| 582 } else { | 543 } else { |
| 583 // Write to the properties array. | 544 // Write to the properties array. |
| 584 int offset = index * kPointerSize + FixedArray::kHeaderSize; | 545 int offset = index * kPointerSize + FixedArray::kHeaderSize; |
| 585 // Get the properties array | 546 // Get the properties array |
| 586 __ ldr(scratch1, | 547 __ Ldr(scratch1, |
| 587 FieldMemOperand(receiver_reg, JSObject::kPropertiesOffset)); | 548 FieldMemOperand(receiver_reg, JSObject::kPropertiesOffset)); |
| 588 if (FLAG_track_double_fields && representation.IsDouble()) { | 549 if (FLAG_track_double_fields && representation.IsDouble()) { |
| 589 __ str(storage_reg, FieldMemOperand(scratch1, offset)); | 550 __ Str(storage_reg, FieldMemOperand(scratch1, offset)); |
| 590 } else { | 551 } else { |
| 591 __ str(value_reg, FieldMemOperand(scratch1, offset)); | 552 __ Str(value_reg, FieldMemOperand(scratch1, offset)); |
| 592 } | 553 } |
| 593 | 554 |
| 594 if (!FLAG_track_fields || !representation.IsSmi()) { | 555 if (!FLAG_track_fields || !representation.IsSmi()) { |
| 595 // Update the write barrier for the array address. | 556 // Update the write barrier for the array address. |
| 596 if (!FLAG_track_double_fields || !representation.IsDouble()) { | 557 if (!FLAG_track_double_fields || !representation.IsDouble()) { |
| 597 __ mov(storage_reg, value_reg); | 558 __ Mov(storage_reg, value_reg); |
| 598 } | 559 } |
| 599 __ RecordWriteField(scratch1, | 560 __ RecordWriteField(scratch1, |
| 600 offset, | 561 offset, |
| 601 storage_reg, | 562 storage_reg, |
| 602 receiver_reg, | 563 receiver_reg, |
| 603 kLRHasNotBeenSaved, | 564 kLRHasNotBeenSaved, |
| 604 kDontSaveFPRegs, | 565 kDontSaveFPRegs, |
| 605 EMIT_REMEMBERED_SET, | 566 EMIT_REMEMBERED_SET, |
| 606 smi_check); | 567 smi_check); |
| 607 } | 568 } |
| 608 } | 569 } |
| 609 | 570 |
| 610 // Return the value (register r0). | 571 __ Bind(&exit); |
| 611 ASSERT(value_reg.is(r0)); | 572 // Return the value (register x0). |
| 612 __ bind(&exit); | 573 ASSERT(value_reg.is(x0)); |
| 613 __ Ret(); | 574 __ Ret(); |
| 614 } | 575 } |
| 615 | 576 |
| 616 | 577 |
| 617 // Generate StoreField code, value is passed in r0 register. | 578 // Generate StoreField code, value is passed in x0 register. |
| 618 // When leaving generated code after success, the receiver_reg and name_reg | 579 // When leaving generated code after success, the receiver_reg and name_reg may |
| 619 // may be clobbered. Upon branch to miss_label, the receiver and name | 580 // be clobbered. Upon branch to miss_label, the receiver and name registers have |
| 620 // registers have their original values. | 581 // their original values. |
| 621 void StoreStubCompiler::GenerateStoreField(MacroAssembler* masm, | 582 void StoreStubCompiler::GenerateStoreField(MacroAssembler* masm, |
| 622 Handle<JSObject> object, | 583 Handle<JSObject> object, |
| 623 LookupResult* lookup, | 584 LookupResult* lookup, |
| 624 Register receiver_reg, | 585 Register receiver_reg, |
| 625 Register name_reg, | 586 Register name_reg, |
| 626 Register value_reg, | 587 Register value_reg, |
| 627 Register scratch1, | 588 Register scratch1, |
| 628 Register scratch2, | 589 Register scratch2, |
| 629 Label* miss_label) { | 590 Label* miss_label) { |
| 630 // r0 : value | 591 // x0 : value |
| 631 Label exit; | 592 Label exit; |
| 632 | 593 |
| 633 // Stub never generated for non-global objects that require access | 594 // Stub never generated for non-global objects that require access |
| 634 // checks. | 595 // checks. |
| 635 ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded()); | 596 ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded()); |
| 636 | 597 |
| 637 int index = lookup->GetFieldIndex().field_index(); | 598 int index = lookup->GetFieldIndex().field_index(); |
| 638 | 599 |
| 639 // Adjust for the number of properties stored in the object. Even in the | 600 // Adjust for the number of properties stored in the object. Even in the |
| 640 // face of a transition we can use the old map here because the size of the | 601 // face of a transition we can use the old map here because the size of the |
| 641 // object and the number of in-object properties is not going to change. | 602 // object and the number of in-object properties is not going to change. |
| 642 index -= object->map()->inobject_properties(); | 603 index -= object->map()->inobject_properties(); |
| 643 | 604 |
| 644 Representation representation = lookup->representation(); | 605 Representation representation = lookup->representation(); |
| 645 ASSERT(!representation.IsNone()); | 606 ASSERT(!representation.IsNone()); |
| 646 if (FLAG_track_fields && representation.IsSmi()) { | 607 if (FLAG_track_fields && representation.IsSmi()) { |
| 647 __ JumpIfNotSmi(value_reg, miss_label); | 608 __ JumpIfNotSmi(value_reg, miss_label); |
| 648 } else if (FLAG_track_heap_object_fields && representation.IsHeapObject()) { | 609 } else if (FLAG_track_heap_object_fields && representation.IsHeapObject()) { |
| 649 __ JumpIfSmi(value_reg, miss_label); | 610 __ JumpIfSmi(value_reg, miss_label); |
| 650 } else if (FLAG_track_double_fields && representation.IsDouble()) { | 611 } else if (FLAG_track_double_fields && representation.IsDouble()) { |
| 651 // Load the double storage. | 612 // Load the double storage. |
| 652 if (index < 0) { | 613 if (index < 0) { |
| 653 int offset = object->map()->instance_size() + (index * kPointerSize); | 614 int offset = (index * kPointerSize) + object->map()->instance_size(); |
| 654 __ ldr(scratch1, FieldMemOperand(receiver_reg, offset)); | 615 __ Ldr(scratch1, FieldMemOperand(receiver_reg, offset)); |
| 655 } else { | 616 } else { |
| 656 __ ldr(scratch1, | 617 int offset = (index * kPointerSize) + FixedArray::kHeaderSize; |
| 618 __ Ldr(scratch1, |
| 657 FieldMemOperand(receiver_reg, JSObject::kPropertiesOffset)); | 619 FieldMemOperand(receiver_reg, JSObject::kPropertiesOffset)); |
| 658 int offset = index * kPointerSize + FixedArray::kHeaderSize; | 620 __ Ldr(scratch1, FieldMemOperand(scratch1, offset)); |
| 659 __ ldr(scratch1, FieldMemOperand(scratch1, offset)); | |
| 660 } | 621 } |
| 661 | 622 |
| 662 // Store the value into the storage. | 623 // Store the value into the storage. |
| 663 Label do_store, heap_number; | 624 Label do_store, heap_number; |
| 625 // TODO(jbramley): Is fp_scratch the most appropriate FP scratch register? |
| 626 // It's only used in Fcmp, but it's not really safe to use it like this. |
| 664 __ JumpIfNotSmi(value_reg, &heap_number); | 627 __ JumpIfNotSmi(value_reg, &heap_number); |
| 665 __ SmiUntag(scratch2, value_reg); | 628 __ SmiUntagToDouble(fp_scratch, value_reg); |
| 666 __ vmov(s0, scratch2); | 629 __ B(&do_store); |
| 667 __ vcvt_f64_s32(d0, s0); | |
| 668 __ jmp(&do_store); | |
| 669 | 630 |
| 670 __ bind(&heap_number); | 631 __ Bind(&heap_number); |
| 671 __ CheckMap(value_reg, scratch2, Heap::kHeapNumberMapRootIndex, | 632 __ CheckMap(value_reg, scratch2, Heap::kHeapNumberMapRootIndex, |
| 672 miss_label, DONT_DO_SMI_CHECK); | 633 miss_label, DONT_DO_SMI_CHECK); |
| 673 __ vldr(d0, FieldMemOperand(value_reg, HeapNumber::kValueOffset)); | 634 __ Ldr(fp_scratch, FieldMemOperand(value_reg, HeapNumber::kValueOffset)); |
| 674 | 635 |
| 675 __ bind(&do_store); | 636 __ Bind(&do_store); |
| 676 __ vstr(d0, FieldMemOperand(scratch1, HeapNumber::kValueOffset)); | 637 __ Str(fp_scratch, FieldMemOperand(scratch1, HeapNumber::kValueOffset)); |
| 677 // Return the value (register r0). | 638 |
| 678 ASSERT(value_reg.is(r0)); | 639 // Return the value (register x0). |
| 640 ASSERT(value_reg.is(x0)); |
| 679 __ Ret(); | 641 __ Ret(); |
| 680 return; | 642 return; |
| 681 } | 643 } |
| 682 | 644 |
| 683 // TODO(verwaest): Share this code as a code stub. | 645 // TODO(verwaest): Share this code as a code stub. |
| 684 SmiCheck smi_check = representation.IsTagged() | 646 SmiCheck smi_check = representation.IsTagged() |
| 685 ? INLINE_SMI_CHECK : OMIT_SMI_CHECK; | 647 ? INLINE_SMI_CHECK : OMIT_SMI_CHECK; |
| 686 if (index < 0) { | 648 if (index < 0) { |
| 687 // Set the property straight into the object. | 649 // Set the property straight into the object. |
| 688 int offset = object->map()->instance_size() + (index * kPointerSize); | 650 int offset = object->map()->instance_size() + (index * kPointerSize); |
| 689 __ str(value_reg, FieldMemOperand(receiver_reg, offset)); | 651 __ Str(value_reg, FieldMemOperand(receiver_reg, offset)); |
| 690 | 652 |
| 691 if (!FLAG_track_fields || !representation.IsSmi()) { | 653 if (!FLAG_track_fields || !representation.IsSmi()) { |
| 692 // Skip updating write barrier if storing a smi. | 654 // Skip updating write barrier if storing a smi. |
| 693 __ JumpIfSmi(value_reg, &exit); | 655 __ JumpIfSmi(value_reg, &exit); |
| 694 | 656 |
| 695 // Update the write barrier for the array address. | 657 // Update the write barrier for the array address. |
| 696 // Pass the now unused name_reg as a scratch register. | 658 // Pass the now unused name_reg as a scratch register. |
| 697 __ mov(name_reg, value_reg); | 659 __ Mov(name_reg, value_reg); |
| 698 __ RecordWriteField(receiver_reg, | 660 __ RecordWriteField(receiver_reg, |
| 699 offset, | 661 offset, |
| 700 name_reg, | 662 name_reg, |
| 701 scratch1, | 663 scratch1, |
| 702 kLRHasNotBeenSaved, | 664 kLRHasNotBeenSaved, |
| 703 kDontSaveFPRegs, | 665 kDontSaveFPRegs, |
| 704 EMIT_REMEMBERED_SET, | 666 EMIT_REMEMBERED_SET, |
| 705 smi_check); | 667 smi_check); |
| 706 } | 668 } |
| 707 } else { | 669 } else { |
| 708 // Write to the properties array. | 670 // Write to the properties array. |
| 709 int offset = index * kPointerSize + FixedArray::kHeaderSize; | 671 int offset = index * kPointerSize + FixedArray::kHeaderSize; |
| 710 // Get the properties array | 672 // Get the properties array |
| 711 __ ldr(scratch1, | 673 __ Ldr(scratch1, |
| 712 FieldMemOperand(receiver_reg, JSObject::kPropertiesOffset)); | 674 FieldMemOperand(receiver_reg, JSObject::kPropertiesOffset)); |
| 713 __ str(value_reg, FieldMemOperand(scratch1, offset)); | 675 __ Str(value_reg, FieldMemOperand(scratch1, offset)); |
| 714 | 676 |
| 715 if (!FLAG_track_fields || !representation.IsSmi()) { | 677 if (!FLAG_track_fields || !representation.IsSmi()) { |
| 716 // Skip updating write barrier if storing a smi. | 678 // Skip updating write barrier if storing a smi. |
| 717 __ JumpIfSmi(value_reg, &exit); | 679 __ JumpIfSmi(value_reg, &exit); |
| 718 | 680 |
| 719 // Update the write barrier for the array address. | 681 // Update the write barrier for the array address. |
| 720 // Ok to clobber receiver_reg and name_reg, since we return. | 682 // Ok to clobber receiver_reg and name_reg, since we return. |
| 721 __ mov(name_reg, value_reg); | 683 __ Mov(name_reg, value_reg); |
| 722 __ RecordWriteField(scratch1, | 684 __ RecordWriteField(scratch1, |
| 723 offset, | 685 offset, |
| 724 name_reg, | 686 name_reg, |
| 725 receiver_reg, | 687 receiver_reg, |
| 726 kLRHasNotBeenSaved, | 688 kLRHasNotBeenSaved, |
| 727 kDontSaveFPRegs, | 689 kDontSaveFPRegs, |
| 728 EMIT_REMEMBERED_SET, | 690 EMIT_REMEMBERED_SET, |
| 729 smi_check); | 691 smi_check); |
| 730 } | 692 } |
| 731 } | 693 } |
| 732 | 694 |
| 733 // Return the value (register r0). | 695 __ Bind(&exit); |
| 734 ASSERT(value_reg.is(r0)); | 696 // Return the value (register x0). |
| 735 __ bind(&exit); | 697 ASSERT(value_reg.is(x0)); |
| 736 __ Ret(); | 698 __ Ret(); |
| 737 } | 699 } |
| 738 | 700 |
| 739 | 701 |
| 740 void StoreStubCompiler::GenerateRestoreName(MacroAssembler* masm, | 702 void StoreStubCompiler::GenerateRestoreName(MacroAssembler* masm, |
| 741 Label* label, | 703 Label* label, |
| 742 Handle<Name> name) { | 704 Handle<Name> name) { |
| 743 if (!label->is_unused()) { | 705 if (!label->is_unused()) { |
| 744 __ bind(label); | 706 __ Bind(label); |
| 745 __ mov(this->name(), Operand(name)); | 707 __ Mov(this->name(), Operand(name)); |
| 746 } | 708 } |
| 747 } | 709 } |
| 748 | 710 |
| 749 | 711 |
| 750 static void PushInterceptorArguments(MacroAssembler* masm, | 712 static void PushInterceptorArguments(MacroAssembler* masm, |
| 751 Register receiver, | 713 Register receiver, |
| 752 Register holder, | 714 Register holder, |
| 753 Register name, | 715 Register name, |
| 754 Handle<JSObject> holder_obj) { | 716 Handle<JSObject> holder_obj) { |
| 755 STATIC_ASSERT(StubCache::kInterceptorArgsNameIndex == 0); | 717 STATIC_ASSERT(StubCache::kInterceptorArgsNameIndex == 0); |
| 756 STATIC_ASSERT(StubCache::kInterceptorArgsInfoIndex == 1); | 718 STATIC_ASSERT(StubCache::kInterceptorArgsInfoIndex == 1); |
| 757 STATIC_ASSERT(StubCache::kInterceptorArgsThisIndex == 2); | 719 STATIC_ASSERT(StubCache::kInterceptorArgsThisIndex == 2); |
| 758 STATIC_ASSERT(StubCache::kInterceptorArgsHolderIndex == 3); | 720 STATIC_ASSERT(StubCache::kInterceptorArgsHolderIndex == 3); |
| 759 STATIC_ASSERT(StubCache::kInterceptorArgsLength == 4); | 721 STATIC_ASSERT(StubCache::kInterceptorArgsLength == 4); |
| 760 __ push(name); | 722 |
| 723 __ Push(name); |
| 761 Handle<InterceptorInfo> interceptor(holder_obj->GetNamedInterceptor()); | 724 Handle<InterceptorInfo> interceptor(holder_obj->GetNamedInterceptor()); |
| 762 ASSERT(!masm->isolate()->heap()->InNewSpace(*interceptor)); | 725 ASSERT(!masm->isolate()->heap()->InNewSpace(*interceptor)); |
| 763 Register scratch = name; | 726 Register scratch = name; |
| 764 __ mov(scratch, Operand(interceptor)); | 727 __ Mov(scratch, Operand(interceptor)); |
| 765 __ push(scratch); | 728 __ Push(scratch, receiver, holder); |
| 766 __ push(receiver); | |
| 767 __ push(holder); | |
| 768 } | 729 } |
| 769 | 730 |
| 770 | 731 |
| 771 static void CompileCallLoadPropertyWithInterceptor( | 732 static void CompileCallLoadPropertyWithInterceptor( |
| 772 MacroAssembler* masm, | 733 MacroAssembler* masm, |
| 773 Register receiver, | 734 Register receiver, |
| 774 Register holder, | 735 Register holder, |
| 775 Register name, | 736 Register name, |
| 776 Handle<JSObject> holder_obj, | 737 Handle<JSObject> holder_obj, |
| 777 IC::UtilityId id) { | 738 IC::UtilityId id) { |
| 778 PushInterceptorArguments(masm, receiver, holder, name, holder_obj); | 739 PushInterceptorArguments(masm, receiver, holder, name, holder_obj); |
| 740 |
| 779 __ CallExternalReference( | 741 __ CallExternalReference( |
| 780 ExternalReference(IC_Utility(id), masm->isolate()), | 742 ExternalReference(IC_Utility(id), masm->isolate()), |
| 781 StubCache::kInterceptorArgsLength); | 743 StubCache::kInterceptorArgsLength); |
| 782 } | 744 } |
| 783 | 745 |
| 784 | 746 |
| 785 // Generate call to api function. | 747 // Generate call to api function. |
| 786 static void GenerateFastApiCall(MacroAssembler* masm, | 748 static void GenerateFastApiCall(MacroAssembler* masm, |
| 787 const CallOptimization& optimization, | 749 const CallOptimization& optimization, |
| 788 Handle<Map> receiver_map, | 750 Handle<Map> receiver_map, |
| 789 Register receiver, | 751 Register receiver, |
| 790 Register scratch_in, | 752 Register scratch, |
| 791 int argc, | 753 int argc, |
| 792 Register* values) { | 754 Register* values) { |
| 793 ASSERT(!receiver.is(scratch_in)); | 755 ASSERT(!AreAliased(receiver, scratch)); |
| 794 __ push(receiver); | 756 __ Push(receiver); |
| 795 // Write the arguments to stack frame. | 757 // Write the arguments to stack frame. |
| 796 for (int i = 0; i < argc; i++) { | 758 for (int i = 0; i < argc; i++) { |
| 759 // TODO(jbramley): Push these in as few Push() calls as possible. |
| 797 Register arg = values[argc-1-i]; | 760 Register arg = values[argc-1-i]; |
| 798 ASSERT(!receiver.is(arg)); | 761 ASSERT(!AreAliased(receiver, scratch, arg)); |
| 799 ASSERT(!scratch_in.is(arg)); | 762 __ Push(arg); |
| 800 __ push(arg); | |
| 801 } | 763 } |
| 764 |
| 802 ASSERT(optimization.is_simple_api_call()); | 765 ASSERT(optimization.is_simple_api_call()); |
| 803 | 766 |
| 804 // Abi for CallApiFunctionStub. | 767 // Abi for CallApiFunctionStub. |
| 805 Register callee = r0; | 768 Register callee = x0; |
| 806 Register call_data = r4; | 769 Register call_data = x4; |
| 807 Register holder = r2; | 770 Register holder = x2; |
| 808 Register api_function_address = r1; | 771 Register api_function_address = x1; |
| 809 | 772 |
| 810 // Put holder in place. | 773 // Put holder in place. |
| 811 CallOptimization::HolderLookup holder_lookup; | 774 CallOptimization::HolderLookup holder_lookup; |
| 812 Handle<JSObject> api_holder = optimization.LookupHolderOfExpectedType( | 775 Handle<JSObject> api_holder = |
| 813 receiver_map, | 776 optimization.LookupHolderOfExpectedType(receiver_map, &holder_lookup); |
| 814 &holder_lookup); | |
| 815 switch (holder_lookup) { | 777 switch (holder_lookup) { |
| 816 case CallOptimization::kHolderIsReceiver: | 778 case CallOptimization::kHolderIsReceiver: |
| 817 __ Move(holder, receiver); | 779 __ Mov(holder, receiver); |
| 818 break; | 780 break; |
| 819 case CallOptimization::kHolderFound: | 781 case CallOptimization::kHolderFound: |
| 820 __ Move(holder, api_holder); | 782 __ LoadObject(holder, api_holder); |
| 821 break; | 783 break; |
| 822 case CallOptimization::kHolderNotFound: | 784 case CallOptimization::kHolderNotFound: |
| 823 UNREACHABLE(); | 785 UNREACHABLE(); |
| 824 break; | 786 break; |
| 825 } | 787 } |
| 826 | 788 |
| 827 Isolate* isolate = masm->isolate(); | 789 Isolate* isolate = masm->isolate(); |
| 828 Handle<JSFunction> function = optimization.constant_function(); | 790 Handle<JSFunction> function = optimization.constant_function(); |
| 829 Handle<CallHandlerInfo> api_call_info = optimization.api_call_info(); | 791 Handle<CallHandlerInfo> api_call_info = optimization.api_call_info(); |
| 830 Handle<Object> call_data_obj(api_call_info->data(), isolate); | 792 Handle<Object> call_data_obj(api_call_info->data(), isolate); |
| 831 | 793 |
| 832 // Put callee in place. | 794 // Put callee in place. |
| 833 __ Move(callee, function); | 795 __ LoadObject(callee, function); |
| 834 | 796 |
| 835 bool call_data_undefined = false; | 797 bool call_data_undefined = false; |
| 836 // Put call_data in place. | 798 // Put call_data in place. |
| 837 if (isolate->heap()->InNewSpace(*call_data_obj)) { | 799 if (isolate->heap()->InNewSpace(*call_data_obj)) { |
| 838 __ Move(call_data, api_call_info); | 800 __ LoadObject(call_data, api_call_info); |
| 839 __ ldr(call_data, FieldMemOperand(call_data, CallHandlerInfo::kDataOffset)); | 801 __ Ldr(call_data, FieldMemOperand(call_data, CallHandlerInfo::kDataOffset)); |
| 840 } else if (call_data_obj->IsUndefined()) { | 802 } else if (call_data_obj->IsUndefined()) { |
| 841 call_data_undefined = true; | 803 call_data_undefined = true; |
| 842 __ LoadRoot(call_data, Heap::kUndefinedValueRootIndex); | 804 __ LoadRoot(call_data, Heap::kUndefinedValueRootIndex); |
| 843 } else { | 805 } else { |
| 844 __ Move(call_data, call_data_obj); | 806 __ LoadObject(call_data, call_data_obj); |
| 845 } | 807 } |
| 846 | 808 |
| 847 // Put api_function_address in place. | 809 // Put api_function_address in place. |
| 848 Address function_address = v8::ToCData<Address>(api_call_info->callback()); | 810 Address function_address = v8::ToCData<Address>(api_call_info->callback()); |
| 849 ApiFunction fun(function_address); | 811 ApiFunction fun(function_address); |
| 850 ExternalReference::Type type = ExternalReference::DIRECT_API_CALL; | |
| 851 ExternalReference ref = ExternalReference(&fun, | 812 ExternalReference ref = ExternalReference(&fun, |
| 852 type, | 813 ExternalReference::DIRECT_API_CALL, |
| 853 masm->isolate()); | 814 masm->isolate()); |
| 854 __ mov(api_function_address, Operand(ref)); | 815 __ Mov(api_function_address, Operand(ref)); |
| 855 | 816 |
| 856 // Jump to stub. | 817 // Jump to stub. |
| 857 CallApiFunctionStub stub(true, call_data_undefined, argc); | 818 CallApiFunctionStub stub(true, call_data_undefined, argc); |
| 858 __ TailCallStub(&stub); | 819 __ TailCallStub(&stub); |
| 859 } | 820 } |
| 860 | 821 |
| 861 | 822 |
| 862 void StubCompiler::GenerateTailCall(MacroAssembler* masm, Handle<Code> code) { | 823 void StubCompiler::GenerateTailCall(MacroAssembler* masm, Handle<Code> code) { |
| 863 __ Jump(code, RelocInfo::CODE_TARGET); | 824 __ Jump(code, RelocInfo::CODE_TARGET); |
| 864 } | 825 } |
| 865 | 826 |
| 866 | 827 |
| 867 #undef __ | 828 #undef __ |
| 868 #define __ ACCESS_MASM(masm()) | 829 #define __ ACCESS_MASM(masm()) |
| 869 | 830 |
| 870 | 831 |
| 871 Register StubCompiler::CheckPrototypes(Handle<HeapType> type, | 832 Register StubCompiler::CheckPrototypes(Handle<HeapType> type, |
| 872 Register object_reg, | 833 Register object_reg, |
| 873 Handle<JSObject> holder, | 834 Handle<JSObject> holder, |
| 874 Register holder_reg, | 835 Register holder_reg, |
| 875 Register scratch1, | 836 Register scratch1, |
| 876 Register scratch2, | 837 Register scratch2, |
| 877 Handle<Name> name, | 838 Handle<Name> name, |
| 878 Label* miss, | 839 Label* miss, |
| 879 PrototypeCheckType check) { | 840 PrototypeCheckType check) { |
| 880 Handle<Map> receiver_map(IC::TypeToMap(*type, isolate())); | 841 Handle<Map> receiver_map(IC::TypeToMap(*type, isolate())); |
| 881 // Make sure that the type feedback oracle harvests the receiver map. | 842 // Make sure that the type feedback oracle harvests the receiver map. |
| 882 // TODO(svenpanne) Remove this hack when all ICs are reworked. | 843 // TODO(svenpanne) Remove this hack when all ICs are reworked. |
| 883 __ mov(scratch1, Operand(receiver_map)); | 844 __ Mov(scratch1, Operand(receiver_map)); |
| 884 | 845 |
| 885 // Make sure there's no overlap between holder and object registers. | 846 // object_reg and holder_reg registers can alias. |
| 886 ASSERT(!scratch1.is(object_reg) && !scratch1.is(holder_reg)); | 847 ASSERT(!AreAliased(object_reg, scratch1, scratch2)); |
| 887 ASSERT(!scratch2.is(object_reg) && !scratch2.is(holder_reg) | 848 ASSERT(!AreAliased(holder_reg, scratch1, scratch2)); |
| 888 && !scratch2.is(scratch1)); | |
| 889 | 849 |
| 890 // Keep track of the current object in register reg. | 850 // Keep track of the current object in register reg. |
| 891 Register reg = object_reg; | 851 Register reg = object_reg; |
| 892 int depth = 0; | 852 int depth = 0; |
| 893 | 853 |
| 894 Handle<JSObject> current = Handle<JSObject>::null(); | 854 Handle<JSObject> current = Handle<JSObject>::null(); |
| 895 if (type->IsConstant()) current = Handle<JSObject>::cast(type->AsConstant()); | 855 if (type->IsConstant()) { |
| 856 current = Handle<JSObject>::cast(type->AsConstant()); |
| 857 } |
| 896 Handle<JSObject> prototype = Handle<JSObject>::null(); | 858 Handle<JSObject> prototype = Handle<JSObject>::null(); |
| 897 Handle<Map> current_map = receiver_map; | 859 Handle<Map> current_map = receiver_map; |
| 898 Handle<Map> holder_map(holder->map()); | 860 Handle<Map> holder_map(holder->map()); |
| 899 // Traverse the prototype chain and check the maps in the prototype chain for | 861 // Traverse the prototype chain and check the maps in the prototype chain for |
| 900 // fast and global objects or do negative lookup for normal objects. | 862 // fast and global objects or do negative lookup for normal objects. |
| 901 while (!current_map.is_identical_to(holder_map)) { | 863 while (!current_map.is_identical_to(holder_map)) { |
| 902 ++depth; | 864 ++depth; |
| 903 | 865 |
| 904 // Only global objects and objects that do not require access | 866 // Only global objects and objects that do not require access |
| 905 // checks are allowed in stubs. | 867 // checks are allowed in stubs. |
| 906 ASSERT(current_map->IsJSGlobalProxyMap() || | 868 ASSERT(current_map->IsJSGlobalProxyMap() || |
| 907 !current_map->is_access_check_needed()); | 869 !current_map->is_access_check_needed()); |
| 908 | 870 |
| 909 prototype = handle(JSObject::cast(current_map->prototype())); | 871 prototype = handle(JSObject::cast(current_map->prototype())); |
| 910 if (current_map->is_dictionary_map() && | 872 if (current_map->is_dictionary_map() && |
| 911 !current_map->IsJSGlobalObjectMap() && | 873 !current_map->IsJSGlobalObjectMap() && |
| 912 !current_map->IsJSGlobalProxyMap()) { | 874 !current_map->IsJSGlobalProxyMap()) { |
| 913 if (!name->IsUniqueName()) { | 875 if (!name->IsUniqueName()) { |
| 914 ASSERT(name->IsString()); | 876 ASSERT(name->IsString()); |
| 915 name = factory()->InternalizeString(Handle<String>::cast(name)); | 877 name = factory()->InternalizeString(Handle<String>::cast(name)); |
| 916 } | 878 } |
| 917 ASSERT(current.is_null() || | 879 ASSERT(current.is_null() || |
| 918 current->property_dictionary()->FindEntry(*name) == | 880 (current->property_dictionary()->FindEntry(*name) == |
| 919 NameDictionary::kNotFound); | 881 NameDictionary::kNotFound)); |
| 920 | 882 |
| 921 GenerateDictionaryNegativeLookup(masm(), miss, reg, name, | 883 GenerateDictionaryNegativeLookup(masm(), miss, reg, name, |
| 922 scratch1, scratch2); | 884 scratch1, scratch2); |
| 923 | 885 |
| 924 __ ldr(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset)); | 886 __ Ldr(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset)); |
| 925 reg = holder_reg; // From now on the object will be in holder_reg. | 887 reg = holder_reg; // From now on the object will be in holder_reg. |
| 926 __ ldr(reg, FieldMemOperand(scratch1, Map::kPrototypeOffset)); | 888 __ Ldr(reg, FieldMemOperand(scratch1, Map::kPrototypeOffset)); |
| 927 } else { | 889 } else { |
| 928 Register map_reg = scratch1; | 890 Register map_reg = scratch1; |
| 891 // TODO(jbramley): Skip this load when we don't need the map. |
| 892 __ Ldr(map_reg, FieldMemOperand(reg, HeapObject::kMapOffset)); |
| 893 |
| 929 if (depth != 1 || check == CHECK_ALL_MAPS) { | 894 if (depth != 1 || check == CHECK_ALL_MAPS) { |
| 930 // CheckMap implicitly loads the map of |reg| into |map_reg|. | 895 __ CheckMap(map_reg, current_map, miss, DONT_DO_SMI_CHECK); |
| 931 __ CheckMap(reg, map_reg, current_map, miss, DONT_DO_SMI_CHECK); | |
| 932 } else { | |
| 933 __ ldr(map_reg, FieldMemOperand(reg, HeapObject::kMapOffset)); | |
| 934 } | 896 } |
| 935 | 897 |
| 936 // Check access rights to the global object. This has to happen after | 898 // Check access rights to the global object. This has to happen after |
| 937 // the map check so that we know that the object is actually a global | 899 // the map check so that we know that the object is actually a global |
| 938 // object. | 900 // object. |
| 939 if (current_map->IsJSGlobalProxyMap()) { | 901 if (current_map->IsJSGlobalProxyMap()) { |
| 940 __ CheckAccessGlobalProxy(reg, scratch2, miss); | 902 __ CheckAccessGlobalProxy(reg, scratch2, miss); |
| 941 } else if (current_map->IsJSGlobalObjectMap()) { | 903 } else if (current_map->IsJSGlobalObjectMap()) { |
| 942 GenerateCheckPropertyCell( | 904 GenerateCheckPropertyCell( |
| 943 masm(), Handle<JSGlobalObject>::cast(current), name, | 905 masm(), Handle<JSGlobalObject>::cast(current), name, |
| 944 scratch2, miss); | 906 scratch2, miss); |
| 945 } | 907 } |
| 946 | 908 |
| 947 reg = holder_reg; // From now on the object will be in holder_reg. | 909 reg = holder_reg; // From now on the object will be in holder_reg. |
| 948 | 910 |
| 949 if (heap()->InNewSpace(*prototype)) { | 911 if (heap()->InNewSpace(*prototype)) { |
| 950 // The prototype is in new space; we cannot store a reference to it | 912 // The prototype is in new space; we cannot store a reference to it |
| 951 // in the code. Load it from the map. | 913 // in the code. Load it from the map. |
| 952 __ ldr(reg, FieldMemOperand(map_reg, Map::kPrototypeOffset)); | 914 __ Ldr(reg, FieldMemOperand(map_reg, Map::kPrototypeOffset)); |
| 953 } else { | 915 } else { |
| 954 // The prototype is in old space; load it directly. | 916 // The prototype is in old space; load it directly. |
| 955 __ mov(reg, Operand(prototype)); | 917 __ Mov(reg, Operand(prototype)); |
| 956 } | 918 } |
| 957 } | 919 } |
| 958 | 920 |
| 959 // Go to the next object in the prototype chain. | 921 // Go to the next object in the prototype chain. |
| 960 current = prototype; | 922 current = prototype; |
| 961 current_map = handle(current->map()); | 923 current_map = handle(current->map()); |
| 962 } | 924 } |
| 963 | 925 |
| 964 // Log the check depth. | 926 // Log the check depth. |
| 965 LOG(isolate(), IntEvent("check-maps-depth", depth + 1)); | 927 LOG(isolate(), IntEvent("check-maps-depth", depth + 1)); |
| 966 | 928 |
| 929 // Check the holder map. |
| 967 if (depth != 0 || check == CHECK_ALL_MAPS) { | 930 if (depth != 0 || check == CHECK_ALL_MAPS) { |
| 968 // Check the holder map. | 931 // Check the holder map. |
| 969 __ CheckMap(reg, scratch1, current_map, miss, DONT_DO_SMI_CHECK); | 932 __ CheckMap(reg, scratch1, current_map, miss, DONT_DO_SMI_CHECK); |
| 970 } | 933 } |
| 971 | 934 |
| 972 // Perform security check for access to the global object. | 935 // Perform security check for access to the global object. |
| 973 ASSERT(current_map->IsJSGlobalProxyMap() || | 936 ASSERT(current_map->IsJSGlobalProxyMap() || |
| 974 !current_map->is_access_check_needed()); | 937 !current_map->is_access_check_needed()); |
| 975 if (current_map->IsJSGlobalProxyMap()) { | 938 if (current_map->IsJSGlobalProxyMap()) { |
| 976 __ CheckAccessGlobalProxy(reg, scratch1, miss); | 939 __ CheckAccessGlobalProxy(reg, scratch1, miss); |
| 977 } | 940 } |
| 978 | 941 |
| 979 // Return the register containing the holder. | 942 // Return the register containing the holder. |
| 980 return reg; | 943 return reg; |
| 981 } | 944 } |
| 982 | 945 |
| 983 | 946 |
| 984 void LoadStubCompiler::HandlerFrontendFooter(Handle<Name> name, Label* miss) { | 947 void LoadStubCompiler::HandlerFrontendFooter(Handle<Name> name, Label* miss) { |
| 985 if (!miss->is_unused()) { | 948 if (!miss->is_unused()) { |
| 986 Label success; | 949 Label success; |
| 987 __ b(&success); | 950 __ B(&success); |
| 988 __ bind(miss); | 951 |
| 952 __ Bind(miss); |
| 989 TailCallBuiltin(masm(), MissBuiltin(kind())); | 953 TailCallBuiltin(masm(), MissBuiltin(kind())); |
| 990 __ bind(&success); | 954 |
| 955 __ Bind(&success); |
| 991 } | 956 } |
| 992 } | 957 } |
| 993 | 958 |
| 994 | 959 |
| 995 void StoreStubCompiler::HandlerFrontendFooter(Handle<Name> name, Label* miss) { | 960 void StoreStubCompiler::HandlerFrontendFooter(Handle<Name> name, Label* miss) { |
| 996 if (!miss->is_unused()) { | 961 if (!miss->is_unused()) { |
| 997 Label success; | 962 Label success; |
| 998 __ b(&success); | 963 __ B(&success); |
| 964 |
| 999 GenerateRestoreName(masm(), miss, name); | 965 GenerateRestoreName(masm(), miss, name); |
| 1000 TailCallBuiltin(masm(), MissBuiltin(kind())); | 966 TailCallBuiltin(masm(), MissBuiltin(kind())); |
| 1001 __ bind(&success); | 967 |
| 968 __ Bind(&success); |
| 1002 } | 969 } |
| 1003 } | 970 } |
| 1004 | 971 |
| 1005 | 972 |
| 1006 Register LoadStubCompiler::CallbackHandlerFrontend( | 973 Register LoadStubCompiler::CallbackHandlerFrontend(Handle<HeapType> type, |
| 1007 Handle<HeapType> type, | 974 Register object_reg, |
| 1008 Register object_reg, | 975 Handle<JSObject> holder, |
| 1009 Handle<JSObject> holder, | 976 Handle<Name> name, |
| 1010 Handle<Name> name, | 977 Handle<Object> callback) { |
| 1011 Handle<Object> callback) { | |
| 1012 Label miss; | 978 Label miss; |
| 1013 | 979 |
| 1014 Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); | 980 Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); |
| 1015 | 981 |
| 982 // TODO(jbramely): HandlerFrontendHeader returns its result in scratch1(), so |
| 983 // we can't use it below, but that isn't very obvious. Is there a better way |
| 984 // of handling this? |
| 985 |
| 1016 if (!holder->HasFastProperties() && !holder->IsJSGlobalObject()) { | 986 if (!holder->HasFastProperties() && !holder->IsJSGlobalObject()) { |
| 1017 ASSERT(!reg.is(scratch2())); | 987 ASSERT(!AreAliased(reg, scratch2(), scratch3(), scratch4())); |
| 1018 ASSERT(!reg.is(scratch3())); | |
| 1019 ASSERT(!reg.is(scratch4())); | |
| 1020 | 988 |
| 1021 // Load the properties dictionary. | 989 // Load the properties dictionary. |
| 1022 Register dictionary = scratch4(); | 990 Register dictionary = scratch4(); |
| 1023 __ ldr(dictionary, FieldMemOperand(reg, JSObject::kPropertiesOffset)); | 991 __ Ldr(dictionary, FieldMemOperand(reg, JSObject::kPropertiesOffset)); |
| 1024 | 992 |
| 1025 // Probe the dictionary. | 993 // Probe the dictionary. |
| 1026 Label probe_done; | 994 Label probe_done; |
| 1027 NameDictionaryLookupStub::GeneratePositiveLookup(masm(), | 995 NameDictionaryLookupStub::GeneratePositiveLookup(masm(), |
| 1028 &miss, | 996 &miss, |
| 1029 &probe_done, | 997 &probe_done, |
| 1030 dictionary, | 998 dictionary, |
| 1031 this->name(), | 999 this->name(), |
| 1032 scratch2(), | 1000 scratch2(), |
| 1033 scratch3()); | 1001 scratch3()); |
| 1034 __ bind(&probe_done); | 1002 __ Bind(&probe_done); |
| 1035 | 1003 |
| 1036 // If probing finds an entry in the dictionary, scratch3 contains the | 1004 // If probing finds an entry in the dictionary, scratch3 contains the |
| 1037 // pointer into the dictionary. Check that the value is the callback. | 1005 // pointer into the dictionary. Check that the value is the callback. |
| 1038 Register pointer = scratch3(); | 1006 Register pointer = scratch3(); |
| 1039 const int kElementsStartOffset = NameDictionary::kHeaderSize + | 1007 const int kElementsStartOffset = NameDictionary::kHeaderSize + |
| 1040 NameDictionary::kElementsStartIndex * kPointerSize; | 1008 NameDictionary::kElementsStartIndex * kPointerSize; |
| 1041 const int kValueOffset = kElementsStartOffset + kPointerSize; | 1009 const int kValueOffset = kElementsStartOffset + kPointerSize; |
| 1042 __ ldr(scratch2(), FieldMemOperand(pointer, kValueOffset)); | 1010 __ Ldr(scratch2(), FieldMemOperand(pointer, kValueOffset)); |
| 1043 __ cmp(scratch2(), Operand(callback)); | 1011 __ Cmp(scratch2(), Operand(callback)); |
| 1044 __ b(ne, &miss); | 1012 __ B(ne, &miss); |
| 1045 } | 1013 } |
| 1046 | 1014 |
| 1047 HandlerFrontendFooter(name, &miss); | 1015 HandlerFrontendFooter(name, &miss); |
| 1048 return reg; | 1016 return reg; |
| 1049 } | 1017 } |
| 1050 | 1018 |
| 1051 | 1019 |
| 1052 void LoadStubCompiler::GenerateLoadField(Register reg, | 1020 void LoadStubCompiler::GenerateLoadField(Register reg, |
| 1053 Handle<JSObject> holder, | 1021 Handle<JSObject> holder, |
| 1054 PropertyIndex field, | 1022 PropertyIndex field, |
| 1055 Representation representation) { | 1023 Representation representation) { |
| 1056 if (!reg.is(receiver())) __ mov(receiver(), reg); | 1024 __ Mov(receiver(), reg); |
| 1057 if (kind() == Code::LOAD_IC) { | 1025 if (kind() == Code::LOAD_IC) { |
| 1058 LoadFieldStub stub(field.is_inobject(holder), | 1026 LoadFieldStub stub(field.is_inobject(holder), |
| 1059 field.translate(holder), | 1027 field.translate(holder), |
| 1060 representation); | 1028 representation); |
| 1061 GenerateTailCall(masm(), stub.GetCode(isolate())); | 1029 GenerateTailCall(masm(), stub.GetCode(isolate())); |
| 1062 } else { | 1030 } else { |
| 1063 KeyedLoadFieldStub stub(field.is_inobject(holder), | 1031 KeyedLoadFieldStub stub(field.is_inobject(holder), |
| 1064 field.translate(holder), | 1032 field.translate(holder), |
| 1065 representation); | 1033 representation); |
| 1066 GenerateTailCall(masm(), stub.GetCode(isolate())); | 1034 GenerateTailCall(masm(), stub.GetCode(isolate())); |
| 1067 } | 1035 } |
| 1068 } | 1036 } |
| 1069 | 1037 |
| 1070 | 1038 |
| 1071 void LoadStubCompiler::GenerateLoadConstant(Handle<Object> value) { | 1039 void LoadStubCompiler::GenerateLoadConstant(Handle<Object> value) { |
| 1072 // Return the constant value. | 1040 // Return the constant value. |
| 1073 __ Move(r0, value); | 1041 __ LoadObject(x0, value); |
| 1074 __ Ret(); | 1042 __ Ret(); |
| 1075 } | 1043 } |
| 1076 | 1044 |
| 1077 | 1045 |
| 1078 void LoadStubCompiler::GenerateLoadCallback( | 1046 void LoadStubCompiler::GenerateLoadCallback( |
| 1079 const CallOptimization& call_optimization, | 1047 const CallOptimization& call_optimization, |
| 1080 Handle<Map> receiver_map) { | 1048 Handle<Map> receiver_map) { |
| 1081 GenerateFastApiCall( | 1049 GenerateFastApiCall( |
| 1082 masm(), call_optimization, receiver_map, | 1050 masm(), call_optimization, receiver_map, receiver(), scratch3(), 0, NULL); |
| 1083 receiver(), scratch3(), 0, NULL); | |
| 1084 } | 1051 } |
| 1085 | 1052 |
| 1086 | 1053 |
| 1087 void LoadStubCompiler::GenerateLoadCallback( | 1054 void LoadStubCompiler::GenerateLoadCallback( |
| 1088 Register reg, | 1055 Register reg, |
| 1089 Handle<ExecutableAccessorInfo> callback) { | 1056 Handle<ExecutableAccessorInfo> callback) { |
| 1090 // Build AccessorInfo::args_ list on the stack and push property name below | 1057 ASSERT(!AreAliased(scratch2(), scratch3(), scratch4(), reg)); |
| 1091 // the exit frame to make GC aware of them and store pointers to them. | 1058 |
| 1059 // Build ExecutableAccessorInfo::args_ list on the stack and push property |
| 1060 // name below the exit frame to make GC aware of them and store pointers to |
| 1061 // them. |
| 1092 STATIC_ASSERT(PropertyCallbackArguments::kHolderIndex == 0); | 1062 STATIC_ASSERT(PropertyCallbackArguments::kHolderIndex == 0); |
| 1093 STATIC_ASSERT(PropertyCallbackArguments::kIsolateIndex == 1); | 1063 STATIC_ASSERT(PropertyCallbackArguments::kIsolateIndex == 1); |
| 1094 STATIC_ASSERT(PropertyCallbackArguments::kReturnValueDefaultValueIndex == 2); | 1064 STATIC_ASSERT(PropertyCallbackArguments::kReturnValueDefaultValueIndex == 2); |
| 1095 STATIC_ASSERT(PropertyCallbackArguments::kReturnValueOffset == 3); | 1065 STATIC_ASSERT(PropertyCallbackArguments::kReturnValueOffset == 3); |
| 1096 STATIC_ASSERT(PropertyCallbackArguments::kDataIndex == 4); | 1066 STATIC_ASSERT(PropertyCallbackArguments::kDataIndex == 4); |
| 1097 STATIC_ASSERT(PropertyCallbackArguments::kThisIndex == 5); | 1067 STATIC_ASSERT(PropertyCallbackArguments::kThisIndex == 5); |
| 1098 STATIC_ASSERT(PropertyCallbackArguments::kArgsLength == 6); | 1068 STATIC_ASSERT(PropertyCallbackArguments::kArgsLength == 6); |
| 1099 ASSERT(!scratch2().is(reg)); | 1069 |
| 1100 ASSERT(!scratch3().is(reg)); | 1070 __ Push(receiver()); |
| 1101 ASSERT(!scratch4().is(reg)); | 1071 |
| 1102 __ push(receiver()); | |
| 1103 if (heap()->InNewSpace(callback->data())) { | 1072 if (heap()->InNewSpace(callback->data())) { |
| 1104 __ Move(scratch3(), callback); | 1073 __ Mov(scratch3(), Operand(callback)); |
| 1105 __ ldr(scratch3(), FieldMemOperand(scratch3(), | 1074 __ Ldr(scratch3(), FieldMemOperand(scratch3(), |
| 1106 ExecutableAccessorInfo::kDataOffset)); | 1075 ExecutableAccessorInfo::kDataOffset)); |
| 1107 } else { | 1076 } else { |
| 1108 __ Move(scratch3(), Handle<Object>(callback->data(), isolate())); | 1077 __ Mov(scratch3(), Operand(Handle<Object>(callback->data(), isolate()))); |
| 1109 } | 1078 } |
| 1110 __ push(scratch3()); | 1079 // TODO(jbramley): Find another scratch register and combine the pushes |
| 1111 __ LoadRoot(scratch3(), Heap::kUndefinedValueRootIndex); | 1080 // together. Can we use scratch1() here? |
| 1112 __ mov(scratch4(), scratch3()); | 1081 __ LoadRoot(scratch4(), Heap::kUndefinedValueRootIndex); |
| 1113 __ Push(scratch3(), scratch4()); | 1082 __ Push(scratch3(), scratch4()); |
| 1114 __ mov(scratch4(), | 1083 __ Mov(scratch3(), Operand(ExternalReference::isolate_address(isolate()))); |
| 1115 Operand(ExternalReference::isolate_address(isolate()))); | 1084 __ Push(scratch4(), scratch3(), reg, name()); |
| 1116 __ Push(scratch4(), reg); | |
| 1117 __ mov(scratch2(), sp); // scratch2 = PropertyAccessorInfo::args_ | |
| 1118 __ push(name()); | |
| 1119 | 1085 |
| 1120 // Abi for CallApiGetter | 1086 Register args_addr = scratch2(); |
| 1121 Register getter_address_reg = r2; | 1087 __ Add(args_addr, __ StackPointer(), kPointerSize); |
| 1122 | 1088 |
| 1089 // Stack at this point: |
| 1090 // sp[40] callback data |
| 1091 // sp[32] undefined |
| 1092 // sp[24] undefined |
| 1093 // sp[16] isolate |
| 1094 // args_addr -> sp[8] reg |
| 1095 // sp[0] name |
| 1096 |
| 1097 // Abi for CallApiGetter. |
| 1098 Register getter_address_reg = x2; |
| 1099 |
| 1100 // Set up the call. |
| 1123 Address getter_address = v8::ToCData<Address>(callback->getter()); | 1101 Address getter_address = v8::ToCData<Address>(callback->getter()); |
| 1124 ApiFunction fun(getter_address); | 1102 ApiFunction fun(getter_address); |
| 1125 ExternalReference::Type type = ExternalReference::DIRECT_GETTER_CALL; | 1103 ExternalReference::Type type = ExternalReference::DIRECT_GETTER_CALL; |
| 1126 ExternalReference ref = ExternalReference(&fun, type, isolate()); | 1104 ExternalReference ref = ExternalReference(&fun, type, isolate()); |
| 1127 __ mov(getter_address_reg, Operand(ref)); | 1105 __ Mov(getter_address_reg, Operand(ref)); |
| 1128 | 1106 |
| 1129 CallApiGetterStub stub; | 1107 CallApiGetterStub stub; |
| 1130 __ TailCallStub(&stub); | 1108 __ TailCallStub(&stub); |
| 1131 } | 1109 } |
| 1132 | 1110 |
| 1133 | 1111 |
| 1134 void LoadStubCompiler::GenerateLoadInterceptor( | 1112 void LoadStubCompiler::GenerateLoadInterceptor( |
| 1135 Register holder_reg, | 1113 Register holder_reg, |
| 1136 Handle<Object> object, | 1114 Handle<Object> object, |
| 1137 Handle<JSObject> interceptor_holder, | 1115 Handle<JSObject> interceptor_holder, |
| 1138 LookupResult* lookup, | 1116 LookupResult* lookup, |
| 1139 Handle<Name> name) { | 1117 Handle<Name> name) { |
| 1118 ASSERT(!AreAliased(receiver(), this->name(), |
| 1119 scratch1(), scratch2(), scratch3())); |
| 1140 ASSERT(interceptor_holder->HasNamedInterceptor()); | 1120 ASSERT(interceptor_holder->HasNamedInterceptor()); |
| 1141 ASSERT(!interceptor_holder->GetNamedInterceptor()->getter()->IsUndefined()); | 1121 ASSERT(!interceptor_holder->GetNamedInterceptor()->getter()->IsUndefined()); |
| 1142 | 1122 |
| 1143 // So far the most popular follow ups for interceptor loads are FIELD | 1123 // So far the most popular follow ups for interceptor loads are FIELD |
| 1144 // and CALLBACKS, so inline only them, other cases may be added | 1124 // and CALLBACKS, so inline only them, other cases may be added later. |
| 1145 // later. | |
| 1146 bool compile_followup_inline = false; | 1125 bool compile_followup_inline = false; |
| 1147 if (lookup->IsFound() && lookup->IsCacheable()) { | 1126 if (lookup->IsFound() && lookup->IsCacheable()) { |
| 1148 if (lookup->IsField()) { | 1127 if (lookup->IsField()) { |
| 1149 compile_followup_inline = true; | 1128 compile_followup_inline = true; |
| 1150 } else if (lookup->type() == CALLBACKS && | 1129 } else if (lookup->type() == CALLBACKS && |
| 1151 lookup->GetCallbackObject()->IsExecutableAccessorInfo()) { | 1130 lookup->GetCallbackObject()->IsExecutableAccessorInfo()) { |
| 1152 ExecutableAccessorInfo* callback = | 1131 ExecutableAccessorInfo* callback = |
| 1153 ExecutableAccessorInfo::cast(lookup->GetCallbackObject()); | 1132 ExecutableAccessorInfo::cast(lookup->GetCallbackObject()); |
| 1154 compile_followup_inline = callback->getter() != NULL && | 1133 compile_followup_inline = callback->getter() != NULL && |
| 1155 callback->IsCompatibleReceiver(*object); | 1134 callback->IsCompatibleReceiver(*object); |
| 1156 } | 1135 } |
| 1157 } | 1136 } |
| 1158 | 1137 |
| 1159 if (compile_followup_inline) { | 1138 if (compile_followup_inline) { |
| 1160 // Compile the interceptor call, followed by inline code to load the | 1139 // Compile the interceptor call, followed by inline code to load the |
| 1161 // property from further up the prototype chain if the call fails. | 1140 // property from further up the prototype chain if the call fails. |
| 1162 // Check that the maps haven't changed. | 1141 // Check that the maps haven't changed. |
| 1163 ASSERT(holder_reg.is(receiver()) || holder_reg.is(scratch1())); | 1142 ASSERT(holder_reg.is(receiver()) || holder_reg.is(scratch1())); |
| 1164 | 1143 |
| 1165 // Preserve the receiver register explicitly whenever it is different from | 1144 // Preserve the receiver register explicitly whenever it is different from |
| 1166 // the holder and it is needed should the interceptor return without any | 1145 // the holder and it is needed should the interceptor return without any |
| 1167 // result. The CALLBACKS case needs the receiver to be passed into C++ code, | 1146 // result. The CALLBACKS case needs the receiver to be passed into C++ code, |
| 1168 // the FIELD case might cause a miss during the prototype check. | 1147 // the FIELD case might cause a miss during the prototype check. |
| 1169 bool must_perfrom_prototype_check = *interceptor_holder != lookup->holder(); | 1148 bool must_perfrom_prototype_check = *interceptor_holder != lookup->holder(); |
| 1170 bool must_preserve_receiver_reg = !receiver().is(holder_reg) && | 1149 bool must_preserve_receiver_reg = !receiver().Is(holder_reg) && |
| 1171 (lookup->type() == CALLBACKS || must_perfrom_prototype_check); | 1150 (lookup->type() == CALLBACKS || must_perfrom_prototype_check); |
| 1172 | 1151 |
| 1173 // Save necessary data before invoking an interceptor. | 1152 // Save necessary data before invoking an interceptor. |
| 1174 // Requires a frame to make GC aware of pushed pointers. | 1153 // Requires a frame to make GC aware of pushed pointers. |
| 1175 { | 1154 { |
| 1176 FrameScope frame_scope(masm(), StackFrame::INTERNAL); | 1155 FrameScope frame_scope(masm(), StackFrame::INTERNAL); |
| 1177 if (must_preserve_receiver_reg) { | 1156 if (must_preserve_receiver_reg) { |
| 1178 __ Push(receiver(), holder_reg, this->name()); | 1157 __ Push(receiver(), holder_reg, this->name()); |
| 1179 } else { | 1158 } else { |
| 1180 __ Push(holder_reg, this->name()); | 1159 __ Push(holder_reg, this->name()); |
| 1181 } | 1160 } |
| 1182 // Invoke an interceptor. Note: map checks from receiver to | 1161 // Invoke an interceptor. Note: map checks from receiver to |
| 1183 // interceptor's holder has been compiled before (see a caller | 1162 // interceptor's holder has been compiled before (see a caller |
| 1184 // of this method.) | 1163 // of this method.) |
| 1185 CompileCallLoadPropertyWithInterceptor( | 1164 CompileCallLoadPropertyWithInterceptor( |
| 1186 masm(), receiver(), holder_reg, this->name(), interceptor_holder, | 1165 masm(), receiver(), holder_reg, this->name(), interceptor_holder, |
| 1187 IC::kLoadPropertyWithInterceptorOnly); | 1166 IC::kLoadPropertyWithInterceptorOnly); |
| 1188 | 1167 |
| 1189 // Check if interceptor provided a value for property. If it's | 1168 // Check if interceptor provided a value for property. If it's |
| 1190 // the case, return immediately. | 1169 // the case, return immediately. |
| 1191 Label interceptor_failed; | 1170 Label interceptor_failed; |
| 1192 __ LoadRoot(scratch1(), Heap::kNoInterceptorResultSentinelRootIndex); | 1171 __ JumpIfRoot(x0, |
| 1193 __ cmp(r0, scratch1()); | 1172 Heap::kNoInterceptorResultSentinelRootIndex, |
| 1194 __ b(eq, &interceptor_failed); | 1173 &interceptor_failed); |
| 1195 frame_scope.GenerateLeaveFrame(); | 1174 frame_scope.GenerateLeaveFrame(); |
| 1196 __ Ret(); | 1175 __ Ret(); |
| 1197 | 1176 |
| 1198 __ bind(&interceptor_failed); | 1177 __ Bind(&interceptor_failed); |
| 1199 __ pop(this->name()); | |
| 1200 __ pop(holder_reg); | |
| 1201 if (must_preserve_receiver_reg) { | 1178 if (must_preserve_receiver_reg) { |
| 1202 __ pop(receiver()); | 1179 __ Pop(this->name(), holder_reg, receiver()); |
| 1180 } else { |
| 1181 __ Pop(this->name(), holder_reg); |
| 1203 } | 1182 } |
| 1204 // Leave the internal frame. | 1183 // Leave the internal frame. |
| 1205 } | 1184 } |
| 1206 | |
| 1207 GenerateLoadPostInterceptor(holder_reg, interceptor_holder, name, lookup); | 1185 GenerateLoadPostInterceptor(holder_reg, interceptor_holder, name, lookup); |
| 1208 } else { // !compile_followup_inline | 1186 } else { // !compile_followup_inline |
| 1209 // Call the runtime system to load the interceptor. | 1187 // Call the runtime system to load the interceptor. |
| 1210 // Check that the maps haven't changed. | 1188 // Check that the maps haven't changed. |
| 1211 PushInterceptorArguments(masm(), receiver(), holder_reg, | 1189 PushInterceptorArguments( |
| 1212 this->name(), interceptor_holder); | 1190 masm(), receiver(), holder_reg, this->name(), interceptor_holder); |
| 1213 | 1191 |
| 1214 ExternalReference ref = | 1192 ExternalReference ref = |
| 1215 ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorForLoad), | 1193 ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorForLoad), |
| 1216 isolate()); | 1194 isolate()); |
| 1217 __ TailCallExternalReference(ref, StubCache::kInterceptorArgsLength, 1); | 1195 __ TailCallExternalReference(ref, StubCache::kInterceptorArgsLength, 1); |
| 1218 } | 1196 } |
| 1219 } | 1197 } |
| 1220 | 1198 |
| 1221 | 1199 |
| 1222 void StubCompiler::GenerateBooleanCheck(Register object, Label* miss) { | 1200 void StubCompiler::GenerateBooleanCheck(Register object, Label* miss) { |
| 1223 Label success; | 1201 Label success; |
| 1224 // Check that the object is a boolean. | 1202 // Check that the object is a boolean. |
| 1225 __ LoadRoot(ip, Heap::kTrueValueRootIndex); | 1203 // TODO(all): Optimize this like LCodeGen::DoDeferredTaggedToI. |
| 1226 __ cmp(object, ip); | 1204 __ JumpIfRoot(object, Heap::kTrueValueRootIndex, &success); |
| 1227 __ b(eq, &success); | 1205 __ JumpIfNotRoot(object, Heap::kFalseValueRootIndex, miss); |
| 1228 __ LoadRoot(ip, Heap::kFalseValueRootIndex); | 1206 __ Bind(&success); |
| 1229 __ cmp(object, ip); | |
| 1230 __ b(ne, miss); | |
| 1231 __ bind(&success); | |
| 1232 } | 1207 } |
| 1233 | 1208 |
| 1234 | 1209 |
| 1235 Handle<Code> StoreStubCompiler::CompileStoreCallback( | 1210 Handle<Code> StoreStubCompiler::CompileStoreCallback( |
| 1236 Handle<JSObject> object, | 1211 Handle<JSObject> object, |
| 1237 Handle<JSObject> holder, | 1212 Handle<JSObject> holder, |
| 1238 Handle<Name> name, | 1213 Handle<Name> name, |
| 1239 Handle<ExecutableAccessorInfo> callback) { | 1214 Handle<ExecutableAccessorInfo> callback) { |
| 1215 ASM_LOCATION("StoreStubCompiler::CompileStoreCallback"); |
| 1240 Register holder_reg = HandlerFrontend( | 1216 Register holder_reg = HandlerFrontend( |
| 1241 IC::CurrentTypeOf(object, isolate()), receiver(), holder, name); | 1217 IC::CurrentTypeOf(object, isolate()), receiver(), holder, name); |
| 1242 | 1218 |
| 1243 // Stub never generated for non-global objects that require access checks. | 1219 // Stub never generated for non-global objects that require access checks. |
| 1244 ASSERT(holder->IsJSGlobalProxy() || !holder->IsAccessCheckNeeded()); | 1220 ASSERT(holder->IsJSGlobalProxy() || !holder->IsAccessCheckNeeded()); |
| 1245 | 1221 |
| 1246 __ push(receiver()); // receiver | 1222 // TODO(jbramley): Make Push take more than four arguments and combine these |
| 1247 __ push(holder_reg); | 1223 // two calls. |
| 1248 __ mov(ip, Operand(callback)); // callback info | 1224 __ Push(receiver(), holder_reg); |
| 1249 __ push(ip); | 1225 __ Mov(scratch1(), Operand(callback)); |
| 1250 __ mov(ip, Operand(name)); | 1226 __ Mov(scratch2(), Operand(name)); |
| 1251 __ Push(ip, value()); | 1227 __ Push(scratch1(), scratch2(), value()); |
| 1252 | 1228 |
| 1253 // Do tail-call to the runtime system. | 1229 // Do tail-call to the runtime system. |
| 1254 ExternalReference store_callback_property = | 1230 ExternalReference store_callback_property = |
| 1255 ExternalReference(IC_Utility(IC::kStoreCallbackProperty), isolate()); | 1231 ExternalReference(IC_Utility(IC::kStoreCallbackProperty), isolate()); |
| 1256 __ TailCallExternalReference(store_callback_property, 5, 1); | 1232 __ TailCallExternalReference(store_callback_property, 5, 1); |
| 1257 | 1233 |
| 1258 // Return the generated code. | 1234 // Return the generated code. |
| 1259 return GetCode(kind(), Code::FAST, name); | 1235 return GetCode(kind(), Code::FAST, name); |
| 1260 } | 1236 } |
| 1261 | 1237 |
| 1262 | 1238 |
| 1263 Handle<Code> StoreStubCompiler::CompileStoreCallback( | |
| 1264 Handle<JSObject> object, | |
| 1265 Handle<JSObject> holder, | |
| 1266 Handle<Name> name, | |
| 1267 const CallOptimization& call_optimization) { | |
| 1268 HandlerFrontend(IC::CurrentTypeOf(object, isolate()), | |
| 1269 receiver(), holder, name); | |
| 1270 | |
| 1271 Register values[] = { value() }; | |
| 1272 GenerateFastApiCall( | |
| 1273 masm(), call_optimization, handle(object->map()), | |
| 1274 receiver(), scratch3(), 1, values); | |
| 1275 | |
| 1276 // Return the generated code. | |
| 1277 return GetCode(kind(), Code::FAST, name); | |
| 1278 } | |
| 1279 | |
| 1280 | |
| 1281 #undef __ | 1239 #undef __ |
| 1282 #define __ ACCESS_MASM(masm) | 1240 #define __ ACCESS_MASM(masm) |
| 1283 | 1241 |
| 1284 | 1242 |
| 1285 void StoreStubCompiler::GenerateStoreViaSetter( | 1243 void StoreStubCompiler::GenerateStoreViaSetter( |
| 1286 MacroAssembler* masm, | 1244 MacroAssembler* masm, |
| 1287 Handle<HeapType> type, | 1245 Handle<HeapType> type, |
| 1288 Handle<JSFunction> setter) { | 1246 Handle<JSFunction> setter) { |
| 1289 // ----------- S t a t e ------------- | 1247 // ----------- S t a t e ------------- |
| 1290 // -- r0 : value | 1248 // -- x0 : value |
| 1291 // -- r1 : receiver | 1249 // -- x1 : receiver |
| 1292 // -- r2 : name | 1250 // -- x2 : name |
| 1293 // -- lr : return address | 1251 // -- lr : return address |
| 1294 // ----------------------------------- | 1252 // ----------------------------------- |
| 1253 Register value = x0; |
| 1254 Register receiver = x1; |
| 1255 Label miss; |
| 1256 |
| 1295 { | 1257 { |
| 1296 FrameScope scope(masm, StackFrame::INTERNAL); | 1258 FrameScope scope(masm, StackFrame::INTERNAL); |
| 1297 Register receiver = r1; | |
| 1298 Register value = r0; | |
| 1299 | 1259 |
| 1300 // Save value register, so we can restore it later. | 1260 // Save value register, so we can restore it later. |
| 1301 __ push(value); | 1261 __ Push(value); |
| 1302 | 1262 |
| 1303 if (!setter.is_null()) { | 1263 if (!setter.is_null()) { |
| 1304 // Call the JavaScript setter with receiver and value on the stack. | 1264 // Call the JavaScript setter with receiver and value on the stack. |
| 1305 if (IC::TypeToMap(*type, masm->isolate())->IsJSGlobalObjectMap()) { | 1265 if (IC::TypeToMap(*type, masm->isolate())->IsJSGlobalObjectMap()) { |
| 1306 // Swap in the global receiver. | 1266 // Swap in the global receiver. |
| 1307 __ ldr(receiver, | 1267 __ Ldr(receiver, |
| 1308 FieldMemOperand( | 1268 FieldMemOperand( |
| 1309 receiver, JSGlobalObject::kGlobalReceiverOffset)); | 1269 receiver, JSGlobalObject::kGlobalReceiverOffset)); |
| 1310 } | 1270 } |
| 1311 __ Push(receiver, value); | 1271 __ Push(receiver, value); |
| 1312 ParameterCount actual(1); | 1272 ParameterCount actual(1); |
| 1313 ParameterCount expected(setter); | 1273 ParameterCount expected(setter); |
| 1314 __ InvokeFunction(setter, expected, actual, | 1274 __ InvokeFunction(setter, expected, actual, |
| 1315 CALL_FUNCTION, NullCallWrapper()); | 1275 CALL_FUNCTION, NullCallWrapper()); |
| 1316 } else { | 1276 } else { |
| 1317 // If we generate a global code snippet for deoptimization only, remember | 1277 // If we generate a global code snippet for deoptimization only, remember |
| 1318 // the place to continue after deoptimization. | 1278 // the place to continue after deoptimization. |
| 1319 masm->isolate()->heap()->SetSetterStubDeoptPCOffset(masm->pc_offset()); | 1279 masm->isolate()->heap()->SetSetterStubDeoptPCOffset(masm->pc_offset()); |
| 1320 } | 1280 } |
| 1321 | 1281 |
| 1322 // We have to return the passed value, not the return value of the setter. | 1282 // We have to return the passed value, not the return value of the setter. |
| 1323 __ pop(r0); | 1283 __ Pop(value); |
| 1324 | 1284 |
| 1325 // Restore context register. | 1285 // Restore context register. |
| 1326 __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); | 1286 __ Ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); |
| 1327 } | 1287 } |
| 1328 __ Ret(); | 1288 __ Ret(); |
| 1329 } | 1289 } |
| 1330 | 1290 |
| 1331 | 1291 |
| 1332 #undef __ | 1292 #undef __ |
| 1333 #define __ ACCESS_MASM(masm()) | 1293 #define __ ACCESS_MASM(masm()) |
| 1334 | 1294 |
| 1335 | 1295 |
| 1336 Handle<Code> StoreStubCompiler::CompileStoreInterceptor( | 1296 Handle<Code> StoreStubCompiler::CompileStoreInterceptor( |
| 1337 Handle<JSObject> object, | 1297 Handle<JSObject> object, |
| 1338 Handle<Name> name) { | 1298 Handle<Name> name) { |
| 1339 Label miss; | 1299 Label miss; |
| 1340 | 1300 |
| 1301 ASM_LOCATION("StoreStubCompiler::CompileStoreInterceptor"); |
| 1302 |
| 1341 // Check that the map of the object hasn't changed. | 1303 // Check that the map of the object hasn't changed. |
| 1342 __ CheckMap(receiver(), scratch1(), Handle<Map>(object->map()), &miss, | 1304 __ CheckMap(receiver(), scratch1(), Handle<Map>(object->map()), &miss, |
| 1343 DO_SMI_CHECK); | 1305 DO_SMI_CHECK); |
| 1344 | 1306 |
| 1345 // Perform global security token check if needed. | 1307 // Perform global security token check if needed. |
| 1346 if (object->IsJSGlobalProxy()) { | 1308 if (object->IsJSGlobalProxy()) { |
| 1347 __ CheckAccessGlobalProxy(receiver(), scratch1(), &miss); | 1309 __ CheckAccessGlobalProxy(receiver(), scratch1(), &miss); |
| 1348 } | 1310 } |
| 1349 | 1311 |
| 1350 // Stub is never generated for non-global objects that require access | 1312 // Stub is never generated for non-global objects that require access checks. |
| 1351 // checks. | |
| 1352 ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded()); | 1313 ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded()); |
| 1353 | 1314 |
| 1354 __ Push(receiver(), this->name(), value()); | 1315 __ Push(receiver(), this->name(), value()); |
| 1355 | 1316 |
| 1356 // Do tail-call to the runtime system. | 1317 // Do tail-call to the runtime system. |
| 1357 ExternalReference store_ic_property = | 1318 ExternalReference store_ic_property = |
| 1358 ExternalReference(IC_Utility(IC::kStoreInterceptorProperty), isolate()); | 1319 ExternalReference(IC_Utility(IC::kStoreInterceptorProperty), isolate()); |
| 1359 __ TailCallExternalReference(store_ic_property, 3, 1); | 1320 __ TailCallExternalReference(store_ic_property, 3, 1); |
| 1360 | 1321 |
| 1361 // Handle store cache miss. | 1322 // Handle store cache miss. |
| 1362 __ bind(&miss); | 1323 __ Bind(&miss); |
| 1363 TailCallBuiltin(masm(), MissBuiltin(kind())); | 1324 TailCallBuiltin(masm(), MissBuiltin(kind())); |
| 1364 | 1325 |
| 1365 // Return the generated code. | 1326 // Return the generated code. |
| 1366 return GetCode(kind(), Code::FAST, name); | 1327 return GetCode(kind(), Code::FAST, name); |
| 1367 } | 1328 } |
| 1368 | 1329 |
| 1369 | 1330 |
| 1370 Handle<Code> LoadStubCompiler::CompileLoadNonexistent(Handle<HeapType> type, | 1331 Handle<Code> LoadStubCompiler::CompileLoadNonexistent(Handle<HeapType> type, |
| 1371 Handle<JSObject> last, | 1332 Handle<JSObject> last, |
| 1372 Handle<Name> name) { | 1333 Handle<Name> name) { |
| 1373 NonexistentHandlerFrontend(type, last, name); | 1334 NonexistentHandlerFrontend(type, last, name); |
| 1374 | 1335 |
| 1375 // Return undefined if maps of the full prototype chain are still the | 1336 // Return undefined if maps of the full prototype chain are still the |
| 1376 // same and no global property with this name contains a value. | 1337 // same and no global property with this name contains a value. |
| 1377 __ LoadRoot(r0, Heap::kUndefinedValueRootIndex); | 1338 __ LoadRoot(x0, Heap::kUndefinedValueRootIndex); |
| 1378 __ Ret(); | 1339 __ Ret(); |
| 1379 | 1340 |
| 1380 // Return the generated code. | 1341 // Return the generated code. |
| 1381 return GetCode(kind(), Code::FAST, name); | 1342 return GetCode(kind(), Code::FAST, name); |
| 1382 } | 1343 } |
| 1383 | 1344 |
| 1384 | 1345 |
| 1346 // TODO(all): The so-called scratch registers are significant in some cases. For |
| 1347 // example, KeyedStoreStubCompiler::registers()[3] (x3) is actually used for |
| 1348 // KeyedStoreCompiler::transition_map(). We should verify which registers are |
| 1349 // actually scratch registers, and which are important. For now, we use the same |
| 1350 // assignments as ARM to remain on the safe side. |
| 1351 |
| 1385 Register* LoadStubCompiler::registers() { | 1352 Register* LoadStubCompiler::registers() { |
| 1386 // receiver, name, scratch1, scratch2, scratch3, scratch4. | 1353 // receiver, name, scratch1, scratch2, scratch3, scratch4. |
| 1387 static Register registers[] = { r0, r2, r3, r1, r4, r5 }; | 1354 static Register registers[] = { x0, x2, x3, x1, x4, x5 }; |
| 1388 return registers; | 1355 return registers; |
| 1389 } | 1356 } |
| 1390 | 1357 |
| 1391 | 1358 |
| 1392 Register* KeyedLoadStubCompiler::registers() { | 1359 Register* KeyedLoadStubCompiler::registers() { |
| 1393 // receiver, name, scratch1, scratch2, scratch3, scratch4. | 1360 // receiver, name/key, scratch1, scratch2, scratch3, scratch4. |
| 1394 static Register registers[] = { r1, r0, r2, r3, r4, r5 }; | 1361 static Register registers[] = { x1, x0, x2, x3, x4, x5 }; |
| 1395 return registers; | 1362 return registers; |
| 1396 } | 1363 } |
| 1397 | 1364 |
| 1398 | 1365 |
| 1399 Register* StoreStubCompiler::registers() { | 1366 Register* StoreStubCompiler::registers() { |
| 1400 // receiver, name, value, scratch1, scratch2, scratch3. | 1367 // receiver, name, value, scratch1, scratch2, scratch3. |
| 1401 static Register registers[] = { r1, r2, r0, r3, r4, r5 }; | 1368 static Register registers[] = { x1, x2, x0, x3, x4, x5 }; |
| 1402 return registers; | 1369 return registers; |
| 1403 } | 1370 } |
| 1404 | 1371 |
| 1405 | 1372 |
| 1406 Register* KeyedStoreStubCompiler::registers() { | 1373 Register* KeyedStoreStubCompiler::registers() { |
| 1407 // receiver, name, value, scratch1, scratch2, scratch3. | 1374 // receiver, name, value, scratch1, scratch2, scratch3. |
| 1408 static Register registers[] = { r2, r1, r0, r3, r4, r5 }; | 1375 static Register registers[] = { x2, x1, x0, x3, x4, x5 }; |
| 1409 return registers; | 1376 return registers; |
| 1410 } | 1377 } |
| 1411 | 1378 |
| 1412 | 1379 |
| 1413 #undef __ | 1380 #undef __ |
| 1414 #define __ ACCESS_MASM(masm) | 1381 #define __ ACCESS_MASM(masm) |
| 1415 | 1382 |
| 1416 | |
| 1417 void LoadStubCompiler::GenerateLoadViaGetter(MacroAssembler* masm, | 1383 void LoadStubCompiler::GenerateLoadViaGetter(MacroAssembler* masm, |
| 1418 Handle<HeapType> type, | 1384 Handle<HeapType> type, |
| 1419 Register receiver, | 1385 Register receiver, |
| 1420 Handle<JSFunction> getter) { | 1386 Handle<JSFunction> getter) { |
| 1421 // ----------- S t a t e ------------- | |
| 1422 // -- r0 : receiver | |
| 1423 // -- r2 : name | |
| 1424 // -- lr : return address | |
| 1425 // ----------------------------------- | |
| 1426 { | 1387 { |
| 1427 FrameScope scope(masm, StackFrame::INTERNAL); | 1388 FrameScope scope(masm, StackFrame::INTERNAL); |
| 1428 | 1389 |
| 1429 if (!getter.is_null()) { | 1390 if (!getter.is_null()) { |
| 1430 // Call the JavaScript getter with the receiver on the stack. | 1391 // Call the JavaScript getter with the receiver on the stack. |
| 1431 if (IC::TypeToMap(*type, masm->isolate())->IsJSGlobalObjectMap()) { | 1392 if (IC::TypeToMap(*type, masm->isolate())->IsJSGlobalObjectMap()) { |
| 1432 // Swap in the global receiver. | 1393 // Swap in the global receiver. |
| 1433 __ ldr(receiver, | 1394 __ Ldr(receiver, |
| 1434 FieldMemOperand( | 1395 FieldMemOperand( |
| 1435 receiver, JSGlobalObject::kGlobalReceiverOffset)); | 1396 receiver, JSGlobalObject::kGlobalReceiverOffset)); |
| 1436 } | 1397 } |
| 1437 __ push(receiver); | 1398 __ Push(receiver); |
| 1438 ParameterCount actual(0); | 1399 ParameterCount actual(0); |
| 1439 ParameterCount expected(getter); | 1400 ParameterCount expected(getter); |
| 1440 __ InvokeFunction(getter, expected, actual, | 1401 __ InvokeFunction(getter, expected, actual, |
| 1441 CALL_FUNCTION, NullCallWrapper()); | 1402 CALL_FUNCTION, NullCallWrapper()); |
| 1442 } else { | 1403 } else { |
| 1443 // If we generate a global code snippet for deoptimization only, remember | 1404 // If we generate a global code snippet for deoptimization only, remember |
| 1444 // the place to continue after deoptimization. | 1405 // the place to continue after deoptimization. |
| 1445 masm->isolate()->heap()->SetGetterStubDeoptPCOffset(masm->pc_offset()); | 1406 masm->isolate()->heap()->SetGetterStubDeoptPCOffset(masm->pc_offset()); |
| 1446 } | 1407 } |
| 1447 | 1408 |
| 1448 // Restore context register. | 1409 // Restore context register. |
| 1449 __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); | 1410 __ Ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); |
| 1450 } | 1411 } |
| 1451 __ Ret(); | 1412 __ Ret(); |
| 1452 } | 1413 } |
| 1453 | 1414 |
| 1454 | 1415 |
| 1455 #undef __ | 1416 #undef __ |
| 1456 #define __ ACCESS_MASM(masm()) | 1417 #define __ ACCESS_MASM(masm()) |
| 1457 | 1418 |
| 1458 | 1419 |
| 1459 Handle<Code> LoadStubCompiler::CompileLoadGlobal( | 1420 Handle<Code> LoadStubCompiler::CompileLoadGlobal( |
| 1460 Handle<HeapType> type, | 1421 Handle<HeapType> type, |
| 1461 Handle<GlobalObject> global, | 1422 Handle<GlobalObject> global, |
| 1462 Handle<PropertyCell> cell, | 1423 Handle<PropertyCell> cell, |
| 1463 Handle<Name> name, | 1424 Handle<Name> name, |
| 1464 bool is_dont_delete) { | 1425 bool is_dont_delete) { |
| 1465 Label miss; | 1426 Label miss; |
| 1466 HandlerFrontendHeader(type, receiver(), global, name, &miss); | 1427 HandlerFrontendHeader(type, receiver(), global, name, &miss); |
| 1467 | 1428 |
| 1468 // Get the value from the cell. | 1429 // Get the value from the cell. |
| 1469 __ mov(r3, Operand(cell)); | 1430 __ Mov(x3, Operand(cell)); |
| 1470 __ ldr(r4, FieldMemOperand(r3, Cell::kValueOffset)); | 1431 __ Ldr(x4, FieldMemOperand(x3, Cell::kValueOffset)); |
| 1471 | 1432 |
| 1472 // Check for deleted property if property can actually be deleted. | 1433 // Check for deleted property if property can actually be deleted. |
| 1473 if (!is_dont_delete) { | 1434 if (!is_dont_delete) { |
| 1474 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); | 1435 __ JumpIfRoot(x4, Heap::kTheHoleValueRootIndex, &miss); |
| 1475 __ cmp(r4, ip); | |
| 1476 __ b(eq, &miss); | |
| 1477 } | 1436 } |
| 1478 | 1437 |
| 1479 Counters* counters = isolate()->counters(); | 1438 Counters* counters = isolate()->counters(); |
| 1480 __ IncrementCounter(counters->named_load_global_stub(), 1, r1, r3); | 1439 __ IncrementCounter(counters->named_load_global_stub(), 1, x1, x3); |
| 1481 __ mov(r0, r4); | 1440 __ Mov(x0, x4); |
| 1482 __ Ret(); | 1441 __ Ret(); |
| 1483 | 1442 |
| 1484 HandlerFrontendFooter(name, &miss); | 1443 HandlerFrontendFooter(name, &miss); |
| 1485 | 1444 |
| 1486 // Return the generated code. | 1445 // Return the generated code. |
| 1487 return GetCode(kind(), Code::NORMAL, name); | 1446 return GetCode(kind(), Code::NORMAL, name); |
| 1488 } | 1447 } |
| 1489 | 1448 |
| 1490 | 1449 |
| 1491 Handle<Code> BaseLoadStoreStubCompiler::CompilePolymorphicIC( | 1450 Handle<Code> BaseLoadStoreStubCompiler::CompilePolymorphicIC( |
| 1492 TypeHandleList* types, | 1451 TypeHandleList* types, |
| 1493 CodeHandleList* handlers, | 1452 CodeHandleList* handlers, |
| 1494 Handle<Name> name, | 1453 Handle<Name> name, |
| 1495 Code::StubType type, | 1454 Code::StubType type, |
| 1496 IcCheckType check) { | 1455 IcCheckType check) { |
| 1497 Label miss; | 1456 Label miss; |
| 1498 | 1457 |
| 1499 if (check == PROPERTY && | 1458 if (check == PROPERTY && |
| 1500 (kind() == Code::KEYED_LOAD_IC || kind() == Code::KEYED_STORE_IC)) { | 1459 (kind() == Code::KEYED_LOAD_IC || kind() == Code::KEYED_STORE_IC)) { |
| 1501 __ cmp(this->name(), Operand(name)); | 1460 __ CompareAndBranch(this->name(), Operand(name), ne, &miss); |
| 1502 __ b(ne, &miss); | |
| 1503 } | 1461 } |
| 1504 | 1462 |
| 1505 Label number_case; | 1463 Label number_case; |
| 1506 Label* smi_target = IncludesNumberType(types) ? &number_case : &miss; | 1464 Label* smi_target = IncludesNumberType(types) ? &number_case : &miss; |
| 1507 __ JumpIfSmi(receiver(), smi_target); | 1465 __ JumpIfSmi(receiver(), smi_target); |
| 1508 | 1466 |
| 1509 Register map_reg = scratch1(); | 1467 Register map_reg = scratch1(); |
| 1510 | 1468 __ Ldr(map_reg, FieldMemOperand(receiver(), HeapObject::kMapOffset)); |
| 1511 int receiver_count = types->length(); | 1469 int receiver_count = types->length(); |
| 1512 int number_of_handled_maps = 0; | 1470 int number_of_handled_maps = 0; |
| 1513 __ ldr(map_reg, FieldMemOperand(receiver(), HeapObject::kMapOffset)); | |
| 1514 for (int current = 0; current < receiver_count; ++current) { | 1471 for (int current = 0; current < receiver_count; ++current) { |
| 1515 Handle<HeapType> type = types->at(current); | 1472 Handle<HeapType> type = types->at(current); |
| 1516 Handle<Map> map = IC::TypeToMap(*type, isolate()); | 1473 Handle<Map> map = IC::TypeToMap(*type, isolate()); |
| 1517 if (!map->is_deprecated()) { | 1474 if (!map->is_deprecated()) { |
| 1518 number_of_handled_maps++; | 1475 number_of_handled_maps++; |
| 1519 __ mov(ip, Operand(map)); | 1476 Label try_next; |
| 1520 __ cmp(map_reg, ip); | 1477 __ Cmp(map_reg, Operand(map)); |
| 1478 __ B(ne, &try_next); |
| 1521 if (type->Is(HeapType::Number())) { | 1479 if (type->Is(HeapType::Number())) { |
| 1522 ASSERT(!number_case.is_unused()); | 1480 ASSERT(!number_case.is_unused()); |
| 1523 __ bind(&number_case); | 1481 __ Bind(&number_case); |
| 1524 } | 1482 } |
| 1525 __ Jump(handlers->at(current), RelocInfo::CODE_TARGET, eq); | 1483 __ Jump(handlers->at(current), RelocInfo::CODE_TARGET); |
| 1484 __ Bind(&try_next); |
| 1526 } | 1485 } |
| 1527 } | 1486 } |
| 1528 ASSERT(number_of_handled_maps != 0); | 1487 ASSERT(number_of_handled_maps != 0); |
| 1529 | 1488 |
| 1530 __ bind(&miss); | 1489 __ Bind(&miss); |
| 1531 TailCallBuiltin(masm(), MissBuiltin(kind())); | 1490 TailCallBuiltin(masm(), MissBuiltin(kind())); |
| 1532 | 1491 |
| 1533 // Return the generated code. | 1492 // Return the generated code. |
| 1534 InlineCacheState state = | 1493 InlineCacheState state = |
| 1535 number_of_handled_maps > 1 ? POLYMORPHIC : MONOMORPHIC; | 1494 (number_of_handled_maps > 1) ? POLYMORPHIC : MONOMORPHIC; |
| 1536 return GetICCode(kind(), type, name, state); | 1495 return GetICCode(kind(), type, name, state); |
| 1537 } | 1496 } |
| 1538 | 1497 |
| 1539 | 1498 |
| 1540 Handle<Code> KeyedStoreStubCompiler::CompileStorePolymorphic( | 1499 Handle<Code> KeyedStoreStubCompiler::CompileStorePolymorphic( |
| 1541 MapHandleList* receiver_maps, | 1500 MapHandleList* receiver_maps, |
| 1542 CodeHandleList* handler_stubs, | 1501 CodeHandleList* handler_stubs, |
| 1543 MapHandleList* transitioned_maps) { | 1502 MapHandleList* transitioned_maps) { |
| 1544 Label miss; | 1503 Label miss; |
| 1504 |
| 1505 ASM_LOCATION("KeyedStoreStubCompiler::CompileStorePolymorphic"); |
| 1506 |
| 1545 __ JumpIfSmi(receiver(), &miss); | 1507 __ JumpIfSmi(receiver(), &miss); |
| 1546 | 1508 |
| 1547 int receiver_count = receiver_maps->length(); | 1509 int receiver_count = receiver_maps->length(); |
| 1548 __ ldr(scratch1(), FieldMemOperand(receiver(), HeapObject::kMapOffset)); | 1510 __ Ldr(scratch1(), FieldMemOperand(receiver(), HeapObject::kMapOffset)); |
| 1549 for (int i = 0; i < receiver_count; ++i) { | 1511 for (int i = 0; i < receiver_count; i++) { |
| 1550 __ mov(ip, Operand(receiver_maps->at(i))); | 1512 __ Cmp(scratch1(), Operand(receiver_maps->at(i))); |
| 1551 __ cmp(scratch1(), ip); | 1513 |
| 1552 if (transitioned_maps->at(i).is_null()) { | 1514 Label skip; |
| 1553 __ Jump(handler_stubs->at(i), RelocInfo::CODE_TARGET, eq); | 1515 __ B(&skip, ne); |
| 1554 } else { | 1516 if (!transitioned_maps->at(i).is_null()) { |
| 1555 Label next_map; | 1517 // This argument is used by the handler stub. For example, see |
| 1556 __ b(ne, &next_map); | 1518 // ElementsTransitionGenerator::GenerateMapChangeElementsTransition. |
| 1557 __ mov(transition_map(), Operand(transitioned_maps->at(i))); | 1519 __ Mov(transition_map(), Operand(transitioned_maps->at(i))); |
| 1558 __ Jump(handler_stubs->at(i), RelocInfo::CODE_TARGET, al); | |
| 1559 __ bind(&next_map); | |
| 1560 } | 1520 } |
| 1521 __ Jump(handler_stubs->at(i), RelocInfo::CODE_TARGET); |
| 1522 __ Bind(&skip); |
| 1561 } | 1523 } |
| 1562 | 1524 |
| 1563 __ bind(&miss); | 1525 __ Bind(&miss); |
| 1564 TailCallBuiltin(masm(), MissBuiltin(kind())); | 1526 TailCallBuiltin(masm(), MissBuiltin(kind())); |
| 1565 | 1527 |
| 1566 // Return the generated code. | |
| 1567 return GetICCode( | 1528 return GetICCode( |
| 1568 kind(), Code::NORMAL, factory()->empty_string(), POLYMORPHIC); | 1529 kind(), Code::NORMAL, factory()->empty_string(), POLYMORPHIC); |
| 1569 } | 1530 } |
| 1570 | 1531 |
| 1571 | 1532 |
| 1533 Handle<Code> StoreStubCompiler::CompileStoreCallback( |
| 1534 Handle<JSObject> object, |
| 1535 Handle<JSObject> holder, |
| 1536 Handle<Name> name, |
| 1537 const CallOptimization& call_optimization) { |
| 1538 HandlerFrontend(IC::CurrentTypeOf(object, isolate()), |
| 1539 receiver(), holder, name); |
| 1540 |
| 1541 Register values[] = { value() }; |
| 1542 GenerateFastApiCall(masm(), call_optimization, handle(object->map()), |
| 1543 receiver(), scratch3(), 1, values); |
| 1544 |
| 1545 // Return the generated code. |
| 1546 return GetCode(kind(), Code::FAST, name); |
| 1547 } |
| 1548 |
| 1549 |
| 1572 #undef __ | 1550 #undef __ |
| 1573 #define __ ACCESS_MASM(masm) | 1551 #define __ ACCESS_MASM(masm) |
| 1574 | 1552 |
| 1575 | |
| 1576 void KeyedLoadStubCompiler::GenerateLoadDictionaryElement( | 1553 void KeyedLoadStubCompiler::GenerateLoadDictionaryElement( |
| 1577 MacroAssembler* masm) { | 1554 MacroAssembler* masm) { |
| 1578 // ---------- S t a t e -------------- | 1555 // ---------- S t a t e -------------- |
| 1579 // -- lr : return address | 1556 // -- lr : return address |
| 1580 // -- r0 : key | 1557 // -- x0 : key |
| 1581 // -- r1 : receiver | 1558 // -- x1 : receiver |
| 1582 // ----------------------------------- | 1559 // ----------------------------------- |
| 1583 Label slow, miss; | 1560 Label slow, miss; |
| 1584 | 1561 |
| 1585 Register key = r0; | 1562 Register result = x0; |
| 1586 Register receiver = r1; | 1563 Register key = x0; |
| 1564 Register receiver = x1; |
| 1587 | 1565 |
| 1588 __ UntagAndJumpIfNotSmi(r2, key, &miss); | 1566 __ JumpIfNotSmi(key, &miss); |
| 1589 __ ldr(r4, FieldMemOperand(receiver, JSObject::kElementsOffset)); | 1567 __ Ldr(x4, FieldMemOperand(receiver, JSObject::kElementsOffset)); |
| 1590 __ LoadFromNumberDictionary(&slow, r4, key, r0, r2, r3, r5); | 1568 __ LoadFromNumberDictionary(&slow, x4, key, result, x2, x3, x5, x6); |
| 1591 __ Ret(); | 1569 __ Ret(); |
| 1592 | 1570 |
| 1593 __ bind(&slow); | 1571 __ Bind(&slow); |
| 1594 __ IncrementCounter( | 1572 __ IncrementCounter( |
| 1595 masm->isolate()->counters()->keyed_load_external_array_slow(), | 1573 masm->isolate()->counters()->keyed_load_external_array_slow(), 1, x2, x3); |
| 1596 1, r2, r3); | |
| 1597 | |
| 1598 // ---------- S t a t e -------------- | |
| 1599 // -- lr : return address | |
| 1600 // -- r0 : key | |
| 1601 // -- r1 : receiver | |
| 1602 // ----------------------------------- | |
| 1603 TailCallBuiltin(masm, Builtins::kKeyedLoadIC_Slow); | 1574 TailCallBuiltin(masm, Builtins::kKeyedLoadIC_Slow); |
| 1604 | 1575 |
| 1605 // Miss case, call the runtime. | 1576 // Miss case, call the runtime. |
| 1606 __ bind(&miss); | 1577 __ Bind(&miss); |
| 1607 | |
| 1608 // ---------- S t a t e -------------- | |
| 1609 // -- lr : return address | |
| 1610 // -- r0 : key | |
| 1611 // -- r1 : receiver | |
| 1612 // ----------------------------------- | |
| 1613 TailCallBuiltin(masm, Builtins::kKeyedLoadIC_Miss); | 1578 TailCallBuiltin(masm, Builtins::kKeyedLoadIC_Miss); |
| 1614 } | 1579 } |
| 1615 | 1580 |
| 1616 | 1581 |
| 1617 #undef __ | |
| 1618 | |
| 1619 } } // namespace v8::internal | 1582 } } // namespace v8::internal |
| 1620 | 1583 |
| 1621 #endif // V8_TARGET_ARCH_ARM | 1584 #endif // V8_TARGET_ARCH_A64 |
| OLD | NEW |