Line data Source code
1 : // Copyright 2014 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 :
5 : #if V8_TARGET_ARCH_X64
6 :
7 : #include "src/ic/handler-compiler.h"
8 :
9 : #include "src/api-arguments.h"
10 : #include "src/field-type.h"
11 : #include "src/ic/call-optimization.h"
12 : #include "src/ic/ic.h"
13 : #include "src/isolate-inl.h"
14 :
15 : namespace v8 {
16 : namespace internal {
17 :
18 : #define __ ACCESS_MASM(masm)
19 :
20 245699 : void PropertyHandlerCompiler::PushVectorAndSlot(Register vector,
21 : Register slot) {
22 : MacroAssembler* masm = this->masm();
23 : STATIC_ASSERT(LoadWithVectorDescriptor::kSlot <
24 : LoadWithVectorDescriptor::kVector);
25 : STATIC_ASSERT(StoreWithVectorDescriptor::kSlot <
26 : StoreWithVectorDescriptor::kVector);
27 : STATIC_ASSERT(StoreTransitionDescriptor::kSlot <
28 : StoreTransitionDescriptor::kVector);
29 245699 : __ Push(slot);
30 245699 : __ Push(vector);
31 245699 : }
32 :
33 :
34 0 : void PropertyHandlerCompiler::PopVectorAndSlot(Register vector, Register slot) {
35 245683 : MacroAssembler* masm = this->masm();
36 245683 : __ Pop(vector);
37 245683 : __ Pop(slot);
38 0 : }
39 :
40 :
41 245699 : void PropertyHandlerCompiler::DiscardVectorAndSlot() {
42 : MacroAssembler* masm = this->masm();
43 : // Remove vector and slot.
44 245699 : __ addp(rsp, Immediate(2 * kPointerSize));
45 245699 : }
46 :
47 829 : void PropertyHandlerCompiler::GenerateDictionaryNegativeLookup(
48 : MacroAssembler* masm, Label* miss_label, Register receiver,
49 : Handle<Name> name, Register scratch0, Register scratch1) {
50 : DCHECK(name->IsUniqueName());
51 : DCHECK(receiver != scratch0);
52 829 : Counters* counters = masm->isolate()->counters();
53 829 : __ IncrementCounter(counters->negative_lookups(), 1);
54 829 : __ IncrementCounter(counters->negative_lookups_miss(), 1);
55 :
56 829 : __ movp(scratch0, FieldOperand(receiver, HeapObject::kMapOffset));
57 :
58 : const int kInterceptorOrAccessCheckNeededMask =
59 : (1 << Map::kHasNamedInterceptor) | (1 << Map::kIsAccessCheckNeeded);
60 :
61 : // Bail out if the receiver has a named interceptor or requires access checks.
62 : __ testb(FieldOperand(scratch0, Map::kBitFieldOffset),
63 829 : Immediate(kInterceptorOrAccessCheckNeededMask));
64 829 : __ j(not_zero, miss_label);
65 :
66 : // Check that receiver is a JSObject.
67 829 : __ CmpInstanceType(scratch0, FIRST_JS_RECEIVER_TYPE);
68 829 : __ j(below, miss_label);
69 :
70 : // Load properties array.
71 829 : Register properties = scratch0;
72 : __ movp(properties,
73 : FieldOperand(receiver, JSObject::kPropertiesOrHashOffset));
74 :
75 : // Check that the properties array is a dictionary.
76 : __ CompareRoot(FieldOperand(properties, HeapObject::kMapOffset),
77 829 : Heap::kHashTableMapRootIndex);
78 829 : __ j(not_equal, miss_label);
79 :
80 : Label done;
81 : NameDictionaryLookupStub::GenerateNegativeLookup(masm, miss_label, &done,
82 829 : properties, name, scratch1);
83 829 : __ bind(&done);
84 829 : __ DecrementCounter(counters->negative_lookups_miss(), 1);
85 829 : }
86 :
87 : // Generate call to api function.
88 3786 : void PropertyHandlerCompiler::GenerateApiAccessorCall(
89 : MacroAssembler* masm, const CallOptimization& optimization,
90 : Handle<Map> receiver_map, Register receiver, Register scratch,
91 : bool is_store, Register store_parameter, Register accessor_holder,
92 : int accessor_index) {
93 : DCHECK(accessor_holder != scratch);
94 : DCHECK(optimization.is_simple_api_call());
95 :
96 3786 : __ PopReturnAddressTo(scratch);
97 : // accessor_holder
98 3786 : __ Push(accessor_holder);
99 : // receiver
100 3786 : __ Push(receiver);
101 : // Write the arguments to stack frame.
102 3786 : if (is_store) {
103 : DCHECK(receiver != store_parameter);
104 : DCHECK(scratch != store_parameter);
105 274 : __ Push(store_parameter);
106 : }
107 : __ PushReturnAddressFrom(scratch);
108 : // Stack now matches JSFunction abi.
109 :
110 : // Abi for CallApiCallbackStub.
111 : Register callee = rdi;
112 : Register data = rbx;
113 : Register holder = rcx;
114 : Register api_function_address = rdx;
115 : scratch = no_reg;
116 :
117 : // Put callee in place.
118 : __ LoadAccessor(callee, accessor_holder, accessor_index,
119 3786 : is_store ? ACCESSOR_SETTER : ACCESSOR_GETTER);
120 :
121 : // Put holder in place.
122 : CallOptimization::HolderLookup holder_lookup;
123 3786 : optimization.LookupHolderOfExpectedType(receiver_map, &holder_lookup);
124 3786 : switch (holder_lookup) {
125 : case CallOptimization::kHolderIsReceiver:
126 3776 : __ Move(holder, receiver);
127 3776 : break;
128 : case CallOptimization::kHolderFound:
129 10 : __ movp(holder, FieldOperand(receiver, HeapObject::kMapOffset));
130 : __ movp(holder, FieldOperand(holder, Map::kPrototypeOffset));
131 : break;
132 : case CallOptimization::kHolderNotFound:
133 0 : UNREACHABLE();
134 : break;
135 : }
136 :
137 : Isolate* isolate = masm->isolate();
138 : Handle<CallHandlerInfo> api_call_info = optimization.api_call_info();
139 : // Put call data in place.
140 3786 : if (api_call_info->data()->IsUndefined(isolate)) {
141 3737 : __ LoadRoot(data, Heap::kUndefinedValueRootIndex);
142 : } else {
143 49 : if (optimization.is_constant_call()) {
144 : __ movp(data,
145 40 : FieldOperand(callee, JSFunction::kSharedFunctionInfoOffset));
146 : __ movp(data,
147 : FieldOperand(data, SharedFunctionInfo::kFunctionDataOffset));
148 : __ movp(data, FieldOperand(data, FunctionTemplateInfo::kCallCodeOffset));
149 : } else {
150 : __ movp(data,
151 9 : FieldOperand(callee, FunctionTemplateInfo::kCallCodeOffset));
152 : }
153 49 : __ movp(data, FieldOperand(data, CallHandlerInfo::kDataOffset));
154 : }
155 :
156 : // Put api_function_address in place.
157 : Address function_address = v8::ToCData<Address>(api_call_info->callback());
158 : __ Move(api_function_address, function_address,
159 : RelocInfo::EXTERNAL_REFERENCE);
160 :
161 : // Jump to stub.
162 3786 : CallApiCallbackStub stub(isolate, is_store, !optimization.is_constant_call());
163 3786 : __ TailCallStub(&stub);
164 3786 : }
165 :
166 :
167 91 : void PropertyHandlerCompiler::GenerateCheckPropertyCell(
168 : MacroAssembler* masm, Handle<JSGlobalObject> global, Handle<Name> name,
169 : Register scratch, Label* miss) {
170 : Handle<PropertyCell> cell = JSGlobalObject::EnsureEmptyPropertyCell(
171 91 : global, name, PropertyCellType::kInvalidated);
172 91 : Isolate* isolate = masm->isolate();
173 : DCHECK(cell->value()->IsTheHole(isolate));
174 91 : Handle<WeakCell> weak_cell = isolate->factory()->NewWeakCell(cell);
175 91 : __ LoadWeakValue(scratch, weak_cell, miss);
176 : __ Cmp(FieldOperand(scratch, PropertyCell::kValueOffset),
177 91 : isolate->factory()->the_hole_value());
178 91 : __ j(not_equal, miss);
179 91 : }
180 :
181 :
182 141348 : void NamedStoreHandlerCompiler::GenerateStoreViaSetter(
183 : MacroAssembler* masm, Handle<Map> map, Register receiver, Register holder,
184 : int accessor_index, int expected_arguments, Register scratch) {
185 : // ----------- S t a t e -------------
186 : // -- rsp[0] : return address
187 : // -----------------------------------
188 : {
189 141348 : FrameScope scope(masm, StackFrame::INTERNAL);
190 :
191 : // Save context register
192 141348 : __ pushq(rsi);
193 : // Save value register, so we can restore it later.
194 282696 : __ Push(value());
195 :
196 141348 : if (accessor_index >= 0) {
197 : DCHECK(holder != scratch);
198 : DCHECK(receiver != scratch);
199 : DCHECK(value() != scratch);
200 : // Call the JavaScript setter with receiver and value on the stack.
201 141317 : if (map->IsJSGlobalObjectMap()) {
202 : // Swap in the global receiver.
203 : __ movp(scratch,
204 : FieldOperand(receiver, JSGlobalObject::kGlobalProxyOffset));
205 18 : receiver = scratch;
206 : }
207 141317 : __ Push(receiver);
208 141317 : __ Push(value());
209 141317 : __ LoadAccessor(rdi, holder, accessor_index, ACCESSOR_SETTER);
210 141317 : __ Set(rax, 1);
211 : __ Call(masm->isolate()->builtins()->CallFunction(
212 : ConvertReceiverMode::kNotNullOrUndefined),
213 141317 : RelocInfo::CODE_TARGET);
214 : } else {
215 : // If we generate a global code snippet for deoptimization only, remember
216 : // the place to continue after deoptimization.
217 62 : masm->isolate()->heap()->SetSetterStubDeoptPCOffset(masm->pc_offset());
218 : }
219 :
220 : // We have to return the passed value, not the return value of the setter.
221 141348 : __ Pop(rax);
222 :
223 : // Restore context register.
224 141348 : __ popq(rsi);
225 : }
226 141348 : __ ret(0);
227 141348 : }
228 :
229 31 : void NamedLoadHandlerCompiler::GenerateLoadViaGetterForDeopt(
230 : MacroAssembler* masm) {
231 : {
232 31 : FrameScope scope(masm, StackFrame::INTERNAL);
233 : // Remember the place to continue after deoptimization.
234 62 : masm->isolate()->heap()->SetGetterStubDeoptPCOffset(masm->pc_offset());
235 : // Restore context register.
236 31 : __ popq(rsi);
237 : }
238 31 : __ ret(0);
239 31 : }
240 :
241 : #undef __
242 : #define __ ACCESS_MASM((masm()))
243 :
244 :
245 242171 : void NamedStoreHandlerCompiler::GenerateRestoreName(Label* label,
246 : Handle<Name> name) {
247 242171 : if (!label->is_unused()) {
248 484342 : __ bind(label);
249 242171 : __ Move(this->name(), name);
250 : }
251 242171 : }
252 :
253 73 : void PropertyHandlerCompiler::GenerateAccessCheck(
254 : Handle<WeakCell> native_context_cell, Register scratch1, Register scratch2,
255 : Label* miss, bool compare_native_contexts_only) {
256 : Label done;
257 : // Load current native context.
258 73 : __ movp(scratch1, NativeContextOperand());
259 : // Load expected native context.
260 73 : __ LoadWeakValue(scratch2, native_context_cell, miss);
261 73 : __ cmpp(scratch1, scratch2);
262 :
263 73 : if (!compare_native_contexts_only) {
264 73 : __ j(equal, &done);
265 :
266 : // Compare security tokens of current and expected native contexts.
267 73 : __ movp(scratch1, ContextOperand(scratch1, Context::SECURITY_TOKEN_INDEX));
268 73 : __ movp(scratch2, ContextOperand(scratch2, Context::SECURITY_TOKEN_INDEX));
269 73 : __ cmpp(scratch1, scratch2);
270 : }
271 73 : __ j(not_equal, miss);
272 :
273 73 : __ bind(&done);
274 73 : }
275 :
276 245699 : Register PropertyHandlerCompiler::CheckPrototypes(
277 : Register object_reg, Register holder_reg, Register scratch1,
278 : Register scratch2, Handle<Name> name, Label* miss) {
279 : Handle<Map> receiver_map = map();
280 :
281 : // Make sure there's no overlap between holder and object registers.
282 : DCHECK(scratch1 != object_reg && scratch1 != holder_reg);
283 : DCHECK(scratch2 != object_reg && scratch2 != holder_reg &&
284 : scratch2 != scratch1);
285 :
286 : Handle<Cell> validity_cell =
287 881632 : Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate());
288 245699 : if (!validity_cell.is_null()) {
289 : DCHECK_EQ(Smi::FromInt(Map::kPrototypeChainValid), validity_cell->value());
290 245683 : __ Move(scratch1, validity_cell);
291 : __ SmiCompare(FieldOperand(scratch1, Cell::kValueOffset),
292 245683 : Smi::FromInt(Map::kPrototypeChainValid));
293 245683 : __ j(not_equal, miss);
294 : }
295 :
296 : // Keep track of the current object in register reg. On the first
297 : // iteration, reg is an alias for object_reg, on later iterations,
298 : // it is an alias for holder_reg.
299 245699 : Register reg = object_reg;
300 : int depth = 0;
301 :
302 : Handle<JSObject> current = Handle<JSObject>::null();
303 245699 : if (receiver_map->IsJSGlobalObjectMap()) {
304 18 : current = isolate()->global_object();
305 : }
306 :
307 : Handle<Map> current_map(receiver_map->GetPrototypeChainRootMap(isolate()),
308 245699 : isolate());
309 : Handle<Map> holder_map(holder()->map());
310 : // Traverse the prototype chain and check the maps in the prototype chain for
311 : // fast and global objects or do negative lookup for normal objects.
312 778063 : while (!current_map.is_identical_to(holder_map)) {
313 286665 : ++depth;
314 :
315 286665 : if (current_map->IsJSGlobalObjectMap()) {
316 : GenerateCheckPropertyCell(masm(), Handle<JSGlobalObject>::cast(current),
317 91 : name, scratch2, miss);
318 286574 : } else if (current_map->is_dictionary_map()) {
319 : DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast.
320 : DCHECK(name->IsUniqueName());
321 : DCHECK(current.is_null() ||
322 : current->property_dictionary()->FindEntry(name) ==
323 : NameDictionary::kNotFound);
324 :
325 829 : if (depth > 1) {
326 : Handle<WeakCell> weak_cell =
327 27 : Map::GetOrCreatePrototypeWeakCell(current, isolate());
328 27 : __ LoadWeakValue(reg, weak_cell, miss);
329 : }
330 : GenerateDictionaryNegativeLookup(masm(), miss, reg, name, scratch1,
331 829 : scratch2);
332 : }
333 :
334 286665 : reg = holder_reg; // From now on the object will be in holder_reg.
335 : // Go to the next object in the prototype chain.
336 : current = handle(JSObject::cast(current_map->prototype()));
337 : current_map = handle(current->map());
338 : }
339 :
340 : DCHECK(!current_map->IsJSGlobalProxyMap());
341 :
342 : // Log the check depth.
343 245699 : LOG(isolate(), IntEvent("check-maps-depth", depth + 1));
344 :
345 245699 : if (depth != 0) {
346 : Handle<WeakCell> weak_cell =
347 144490 : Map::GetOrCreatePrototypeWeakCell(current, isolate());
348 144490 : __ LoadWeakValue(reg, weak_cell, miss);
349 : }
350 :
351 : // Return the register containing the holder.
352 245699 : return reg;
353 : }
354 :
355 :
356 3512 : void NamedLoadHandlerCompiler::FrontendFooter(Handle<Name> name, Label* miss) {
357 3512 : if (!miss->is_unused()) {
358 : Label success;
359 3512 : __ jmp(&success);
360 3512 : __ bind(miss);
361 3512 : PopVectorAndSlot();
362 3512 : TailCallBuiltin(masm(), Builtins::kLoadIC_Miss);
363 3512 : __ bind(&success);
364 : }
365 3512 : }
366 :
367 :
368 242187 : void NamedStoreHandlerCompiler::FrontendFooter(Handle<Name> name, Label* miss) {
369 242187 : if (!miss->is_unused()) {
370 : Label success;
371 242171 : __ jmp(&success);
372 242171 : GenerateRestoreName(miss, name);
373 242171 : PopVectorAndSlot();
374 242171 : TailCallBuiltin(masm(), Builtins::kStoreIC_Miss);
375 242171 : __ bind(&success);
376 : }
377 242187 : }
378 :
379 0 : void NamedStoreHandlerCompiler::ZapStackArgumentsRegisterAliases() {
380 : STATIC_ASSERT(!StoreWithVectorDescriptor::kPassLastArgsOnStack);
381 0 : }
382 :
383 100596 : Handle<Code> NamedStoreHandlerCompiler::CompileStoreCallback(
384 : Handle<JSObject> object, Handle<Name> name, Handle<AccessorInfo> callback,
385 : LanguageMode language_mode) {
386 100596 : Register holder_reg = Frontend(name);
387 :
388 402401 : __ PopReturnAddressTo(scratch1());
389 100596 : __ Push(receiver());
390 100596 : __ Push(holder_reg);
391 : // If the callback cannot leak, then push the callback directly,
392 : // otherwise wrap it in a weak cell.
393 100613 : if (callback->data()->IsUndefined(isolate()) || callback->data()->IsSmi()) {
394 100579 : __ Push(callback);
395 : } else {
396 17 : Handle<WeakCell> cell = isolate()->factory()->NewWeakCell(callback);
397 17 : __ Push(cell);
398 : }
399 100596 : __ Push(name);
400 100596 : __ Push(value());
401 100596 : __ Push(Smi::FromEnum(language_mode));
402 : __ PushReturnAddressFrom(scratch1());
403 :
404 : // Do tail-call to the runtime system.
405 100596 : __ TailCallRuntime(Runtime::kStoreCallbackProperty);
406 :
407 : // Return the generated code.
408 100596 : return GetCode(name);
409 : }
410 :
411 :
412 274 : Register NamedStoreHandlerCompiler::value() {
413 383535 : return StoreDescriptor::ValueRegister();
414 : }
415 :
416 :
417 : #undef __
418 : } // namespace internal
419 : } // namespace v8
420 :
421 : #endif // V8_TARGET_ARCH_X64
|