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