1 // Copyright 2012 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_X87
6
7 #include "src/base/bits.h"
8 #include "src/base/division-by-constant.h"
9 #include "src/bootstrapper.h"
10 #include "src/codegen.h"
11 #include "src/debug/debug.h"
12 #include "src/runtime/runtime.h"
13 #include "src/x87/frames-x87.h"
14 #include "src/x87/macro-assembler-x87.h"
15
16 namespace v8 {
17 namespace internal {
18
19 // -------------------------------------------------------------------------
20 // MacroAssembler implementation.
21
MacroAssembler(Isolate * arg_isolate,void * buffer,int size,CodeObjectRequired create_code_object)22 MacroAssembler::MacroAssembler(Isolate* arg_isolate, void* buffer, int size,
23 CodeObjectRequired create_code_object)
24 : Assembler(arg_isolate, buffer, size),
25 generating_stub_(false),
26 has_frame_(false) {
27 if (create_code_object == CodeObjectRequired::kYes) {
28 code_object_ =
29 Handle<Object>::New(isolate()->heap()->undefined_value(), isolate());
30 }
31 }
32
33
Load(Register dst,const Operand & src,Representation r)34 void MacroAssembler::Load(Register dst, const Operand& src, Representation r) {
35 DCHECK(!r.IsDouble());
36 if (r.IsInteger8()) {
37 movsx_b(dst, src);
38 } else if (r.IsUInteger8()) {
39 movzx_b(dst, src);
40 } else if (r.IsInteger16()) {
41 movsx_w(dst, src);
42 } else if (r.IsUInteger16()) {
43 movzx_w(dst, src);
44 } else {
45 mov(dst, src);
46 }
47 }
48
49
Store(Register src,const Operand & dst,Representation r)50 void MacroAssembler::Store(Register src, const Operand& dst, Representation r) {
51 DCHECK(!r.IsDouble());
52 if (r.IsInteger8() || r.IsUInteger8()) {
53 mov_b(dst, src);
54 } else if (r.IsInteger16() || r.IsUInteger16()) {
55 mov_w(dst, src);
56 } else {
57 if (r.IsHeapObject()) {
58 AssertNotSmi(src);
59 } else if (r.IsSmi()) {
60 AssertSmi(src);
61 }
62 mov(dst, src);
63 }
64 }
65
66
LoadRoot(Register destination,Heap::RootListIndex index)67 void MacroAssembler::LoadRoot(Register destination, Heap::RootListIndex index) {
68 if (isolate()->heap()->RootCanBeTreatedAsConstant(index)) {
69 mov(destination, isolate()->heap()->root_handle(index));
70 return;
71 }
72 ExternalReference roots_array_start =
73 ExternalReference::roots_array_start(isolate());
74 mov(destination, Immediate(index));
75 mov(destination, Operand::StaticArray(destination,
76 times_pointer_size,
77 roots_array_start));
78 }
79
80
StoreRoot(Register source,Register scratch,Heap::RootListIndex index)81 void MacroAssembler::StoreRoot(Register source,
82 Register scratch,
83 Heap::RootListIndex index) {
84 DCHECK(Heap::RootCanBeWrittenAfterInitialization(index));
85 ExternalReference roots_array_start =
86 ExternalReference::roots_array_start(isolate());
87 mov(scratch, Immediate(index));
88 mov(Operand::StaticArray(scratch, times_pointer_size, roots_array_start),
89 source);
90 }
91
92
CompareRoot(Register with,Register scratch,Heap::RootListIndex index)93 void MacroAssembler::CompareRoot(Register with,
94 Register scratch,
95 Heap::RootListIndex index) {
96 ExternalReference roots_array_start =
97 ExternalReference::roots_array_start(isolate());
98 mov(scratch, Immediate(index));
99 cmp(with, Operand::StaticArray(scratch,
100 times_pointer_size,
101 roots_array_start));
102 }
103
104
CompareRoot(Register with,Heap::RootListIndex index)105 void MacroAssembler::CompareRoot(Register with, Heap::RootListIndex index) {
106 DCHECK(isolate()->heap()->RootCanBeTreatedAsConstant(index));
107 cmp(with, isolate()->heap()->root_handle(index));
108 }
109
110
CompareRoot(const Operand & with,Heap::RootListIndex index)111 void MacroAssembler::CompareRoot(const Operand& with,
112 Heap::RootListIndex index) {
113 DCHECK(isolate()->heap()->RootCanBeTreatedAsConstant(index));
114 cmp(with, isolate()->heap()->root_handle(index));
115 }
116
117
PushRoot(Heap::RootListIndex index)118 void MacroAssembler::PushRoot(Heap::RootListIndex index) {
119 DCHECK(isolate()->heap()->RootCanBeTreatedAsConstant(index));
120 Push(isolate()->heap()->root_handle(index));
121 }
122
123 #define REG(Name) \
124 { Register::kCode_##Name }
125
126 static const Register saved_regs[] = {REG(eax), REG(ecx), REG(edx)};
127
128 #undef REG
129
130 static const int kNumberOfSavedRegs = sizeof(saved_regs) / sizeof(Register);
131
PushCallerSaved(SaveFPRegsMode fp_mode,Register exclusion1,Register exclusion2,Register exclusion3)132 void MacroAssembler::PushCallerSaved(SaveFPRegsMode fp_mode,
133 Register exclusion1, Register exclusion2,
134 Register exclusion3) {
135 // We don't allow a GC during a store buffer overflow so there is no need to
136 // store the registers in any particular way, but we do have to store and
137 // restore them.
138 for (int i = 0; i < kNumberOfSavedRegs; i++) {
139 Register reg = saved_regs[i];
140 if (!reg.is(exclusion1) && !reg.is(exclusion2) && !reg.is(exclusion3)) {
141 push(reg);
142 }
143 }
144 if (fp_mode == kSaveFPRegs) {
145 // Save FPU state in m108byte.
146 sub(esp, Immediate(108));
147 fnsave(Operand(esp, 0));
148 }
149 }
150
PopCallerSaved(SaveFPRegsMode fp_mode,Register exclusion1,Register exclusion2,Register exclusion3)151 void MacroAssembler::PopCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1,
152 Register exclusion2, Register exclusion3) {
153 if (fp_mode == kSaveFPRegs) {
154 // Restore FPU state in m108byte.
155 frstor(Operand(esp, 0));
156 add(esp, Immediate(108));
157 }
158
159 for (int i = kNumberOfSavedRegs - 1; i >= 0; i--) {
160 Register reg = saved_regs[i];
161 if (!reg.is(exclusion1) && !reg.is(exclusion2) && !reg.is(exclusion3)) {
162 pop(reg);
163 }
164 }
165 }
166
InNewSpace(Register object,Register scratch,Condition cc,Label * condition_met,Label::Distance distance)167 void MacroAssembler::InNewSpace(Register object, Register scratch, Condition cc,
168 Label* condition_met,
169 Label::Distance distance) {
170 CheckPageFlag(object, scratch, MemoryChunk::kIsInNewSpaceMask, cc,
171 condition_met, distance);
172 }
173
174
RememberedSetHelper(Register object,Register addr,Register scratch,SaveFPRegsMode save_fp,MacroAssembler::RememberedSetFinalAction and_then)175 void MacroAssembler::RememberedSetHelper(
176 Register object, // Only used for debug checks.
177 Register addr, Register scratch, SaveFPRegsMode save_fp,
178 MacroAssembler::RememberedSetFinalAction and_then) {
179 Label done;
180 if (emit_debug_code()) {
181 Label ok;
182 JumpIfNotInNewSpace(object, scratch, &ok, Label::kNear);
183 int3();
184 bind(&ok);
185 }
186 // Load store buffer top.
187 ExternalReference store_buffer =
188 ExternalReference::store_buffer_top(isolate());
189 mov(scratch, Operand::StaticVariable(store_buffer));
190 // Store pointer to buffer.
191 mov(Operand(scratch, 0), addr);
192 // Increment buffer top.
193 add(scratch, Immediate(kPointerSize));
194 // Write back new top of buffer.
195 mov(Operand::StaticVariable(store_buffer), scratch);
196 // Call stub on end of buffer.
197 // Check for end of buffer.
198 test(scratch, Immediate(StoreBuffer::kStoreBufferMask));
199 if (and_then == kReturnAtEnd) {
200 Label buffer_overflowed;
201 j(equal, &buffer_overflowed, Label::kNear);
202 ret(0);
203 bind(&buffer_overflowed);
204 } else {
205 DCHECK(and_then == kFallThroughAtEnd);
206 j(not_equal, &done, Label::kNear);
207 }
208 StoreBufferOverflowStub store_buffer_overflow(isolate(), save_fp);
209 CallStub(&store_buffer_overflow);
210 if (and_then == kReturnAtEnd) {
211 ret(0);
212 } else {
213 DCHECK(and_then == kFallThroughAtEnd);
214 bind(&done);
215 }
216 }
217
218
ClampTOSToUint8(Register result_reg)219 void MacroAssembler::ClampTOSToUint8(Register result_reg) {
220 Label done, conv_failure;
221 sub(esp, Immediate(kPointerSize));
222 fnclex();
223 fist_s(Operand(esp, 0));
224 pop(result_reg);
225 X87CheckIA();
226 j(equal, &conv_failure, Label::kNear);
227 test(result_reg, Immediate(0xFFFFFF00));
228 j(zero, &done, Label::kNear);
229 setcc(sign, result_reg);
230 sub(result_reg, Immediate(1));
231 and_(result_reg, Immediate(255));
232 jmp(&done, Label::kNear);
233 bind(&conv_failure);
234 fnclex();
235 fldz();
236 fld(1);
237 FCmp();
238 setcc(below, result_reg); // 1 if negative, 0 if positive.
239 dec_b(result_reg); // 0 if negative, 255 if positive.
240 bind(&done);
241 }
242
243
ClampUint8(Register reg)244 void MacroAssembler::ClampUint8(Register reg) {
245 Label done;
246 test(reg, Immediate(0xFFFFFF00));
247 j(zero, &done, Label::kNear);
248 setcc(negative, reg); // 1 if negative, 0 if positive.
249 dec_b(reg); // 0 if negative, 255 if positive.
250 bind(&done);
251 }
252
253
SlowTruncateToI(Register result_reg,Register input_reg,int offset)254 void MacroAssembler::SlowTruncateToI(Register result_reg,
255 Register input_reg,
256 int offset) {
257 DoubleToIStub stub(isolate(), input_reg, result_reg, offset, true);
258 call(stub.GetCode(), RelocInfo::CODE_TARGET);
259 }
260
261
TruncateX87TOSToI(Register result_reg)262 void MacroAssembler::TruncateX87TOSToI(Register result_reg) {
263 sub(esp, Immediate(kDoubleSize));
264 fst_d(MemOperand(esp, 0));
265 SlowTruncateToI(result_reg, esp, 0);
266 add(esp, Immediate(kDoubleSize));
267 }
268
269
X87TOSToI(Register result_reg,MinusZeroMode minus_zero_mode,Label * lost_precision,Label * is_nan,Label * minus_zero,Label::Distance dst)270 void MacroAssembler::X87TOSToI(Register result_reg,
271 MinusZeroMode minus_zero_mode,
272 Label* lost_precision, Label* is_nan,
273 Label* minus_zero, Label::Distance dst) {
274 Label done;
275 sub(esp, Immediate(kPointerSize));
276 fld(0);
277 fist_s(MemOperand(esp, 0));
278 fild_s(MemOperand(esp, 0));
279 pop(result_reg);
280 FCmp();
281 j(not_equal, lost_precision, dst);
282 j(parity_even, is_nan, dst);
283 if (minus_zero_mode == FAIL_ON_MINUS_ZERO) {
284 test(result_reg, Operand(result_reg));
285 j(not_zero, &done, Label::kNear);
286 // To check for minus zero, we load the value again as float, and check
287 // if that is still 0.
288 sub(esp, Immediate(kPointerSize));
289 fst_s(MemOperand(esp, 0));
290 pop(result_reg);
291 test(result_reg, Operand(result_reg));
292 j(not_zero, minus_zero, dst);
293 }
294 bind(&done);
295 }
296
297
TruncateHeapNumberToI(Register result_reg,Register input_reg)298 void MacroAssembler::TruncateHeapNumberToI(Register result_reg,
299 Register input_reg) {
300 Label done, slow_case;
301
302 SlowTruncateToI(result_reg, input_reg);
303 bind(&done);
304 }
305
306
LoadUint32NoSSE2(const Operand & src)307 void MacroAssembler::LoadUint32NoSSE2(const Operand& src) {
308 Label done;
309 push(src);
310 fild_s(Operand(esp, 0));
311 cmp(src, Immediate(0));
312 j(not_sign, &done, Label::kNear);
313 ExternalReference uint32_bias =
314 ExternalReference::address_of_uint32_bias();
315 fld_d(Operand::StaticVariable(uint32_bias));
316 faddp(1);
317 bind(&done);
318 add(esp, Immediate(kPointerSize));
319 }
320
321
RecordWriteArray(Register object,Register value,Register index,SaveFPRegsMode save_fp,RememberedSetAction remembered_set_action,SmiCheck smi_check,PointersToHereCheck pointers_to_here_check_for_value)322 void MacroAssembler::RecordWriteArray(
323 Register object, Register value, Register index, SaveFPRegsMode save_fp,
324 RememberedSetAction remembered_set_action, SmiCheck smi_check,
325 PointersToHereCheck pointers_to_here_check_for_value) {
326 // First, check if a write barrier is even needed. The tests below
327 // catch stores of Smis.
328 Label done;
329
330 // Skip barrier if writing a smi.
331 if (smi_check == INLINE_SMI_CHECK) {
332 DCHECK_EQ(0, kSmiTag);
333 test(value, Immediate(kSmiTagMask));
334 j(zero, &done);
335 }
336
337 // Array access: calculate the destination address in the same manner as
338 // KeyedStoreIC::GenerateGeneric. Multiply a smi by 2 to get an offset
339 // into an array of words.
340 Register dst = index;
341 lea(dst, Operand(object, index, times_half_pointer_size,
342 FixedArray::kHeaderSize - kHeapObjectTag));
343
344 RecordWrite(object, dst, value, save_fp, remembered_set_action,
345 OMIT_SMI_CHECK, pointers_to_here_check_for_value);
346
347 bind(&done);
348
349 // Clobber clobbered input registers when running with the debug-code flag
350 // turned on to provoke errors.
351 if (emit_debug_code()) {
352 mov(value, Immediate(bit_cast<int32_t>(kZapValue)));
353 mov(index, Immediate(bit_cast<int32_t>(kZapValue)));
354 }
355 }
356
357
RecordWriteField(Register object,int offset,Register value,Register dst,SaveFPRegsMode save_fp,RememberedSetAction remembered_set_action,SmiCheck smi_check,PointersToHereCheck pointers_to_here_check_for_value)358 void MacroAssembler::RecordWriteField(
359 Register object, int offset, Register value, Register dst,
360 SaveFPRegsMode save_fp, RememberedSetAction remembered_set_action,
361 SmiCheck smi_check, PointersToHereCheck pointers_to_here_check_for_value) {
362 // First, check if a write barrier is even needed. The tests below
363 // catch stores of Smis.
364 Label done;
365
366 // Skip barrier if writing a smi.
367 if (smi_check == INLINE_SMI_CHECK) {
368 JumpIfSmi(value, &done, Label::kNear);
369 }
370
371 // Although the object register is tagged, the offset is relative to the start
372 // of the object, so so offset must be a multiple of kPointerSize.
373 DCHECK(IsAligned(offset, kPointerSize));
374
375 lea(dst, FieldOperand(object, offset));
376 if (emit_debug_code()) {
377 Label ok;
378 test_b(dst, Immediate((1 << kPointerSizeLog2) - 1));
379 j(zero, &ok, Label::kNear);
380 int3();
381 bind(&ok);
382 }
383
384 RecordWrite(object, dst, value, save_fp, remembered_set_action,
385 OMIT_SMI_CHECK, pointers_to_here_check_for_value);
386
387 bind(&done);
388
389 // Clobber clobbered input registers when running with the debug-code flag
390 // turned on to provoke errors.
391 if (emit_debug_code()) {
392 mov(value, Immediate(bit_cast<int32_t>(kZapValue)));
393 mov(dst, Immediate(bit_cast<int32_t>(kZapValue)));
394 }
395 }
396
397
RecordWriteForMap(Register object,Handle<Map> map,Register scratch1,Register scratch2,SaveFPRegsMode save_fp)398 void MacroAssembler::RecordWriteForMap(Register object, Handle<Map> map,
399 Register scratch1, Register scratch2,
400 SaveFPRegsMode save_fp) {
401 Label done;
402
403 Register address = scratch1;
404 Register value = scratch2;
405 if (emit_debug_code()) {
406 Label ok;
407 lea(address, FieldOperand(object, HeapObject::kMapOffset));
408 test_b(address, Immediate((1 << kPointerSizeLog2) - 1));
409 j(zero, &ok, Label::kNear);
410 int3();
411 bind(&ok);
412 }
413
414 DCHECK(!object.is(value));
415 DCHECK(!object.is(address));
416 DCHECK(!value.is(address));
417 AssertNotSmi(object);
418
419 if (!FLAG_incremental_marking) {
420 return;
421 }
422
423 // Compute the address.
424 lea(address, FieldOperand(object, HeapObject::kMapOffset));
425
426 // A single check of the map's pages interesting flag suffices, since it is
427 // only set during incremental collection, and then it's also guaranteed that
428 // the from object's page's interesting flag is also set. This optimization
429 // relies on the fact that maps can never be in new space.
430 DCHECK(!isolate()->heap()->InNewSpace(*map));
431 CheckPageFlagForMap(map,
432 MemoryChunk::kPointersToHereAreInterestingMask,
433 zero,
434 &done,
435 Label::kNear);
436
437 RecordWriteStub stub(isolate(), object, value, address, OMIT_REMEMBERED_SET,
438 save_fp);
439 CallStub(&stub);
440
441 bind(&done);
442
443 // Count number of write barriers in generated code.
444 isolate()->counters()->write_barriers_static()->Increment();
445 IncrementCounter(isolate()->counters()->write_barriers_dynamic(), 1);
446
447 // Clobber clobbered input registers when running with the debug-code flag
448 // turned on to provoke errors.
449 if (emit_debug_code()) {
450 mov(value, Immediate(bit_cast<int32_t>(kZapValue)));
451 mov(scratch1, Immediate(bit_cast<int32_t>(kZapValue)));
452 mov(scratch2, Immediate(bit_cast<int32_t>(kZapValue)));
453 }
454 }
455
456
RecordWrite(Register object,Register address,Register value,SaveFPRegsMode fp_mode,RememberedSetAction remembered_set_action,SmiCheck smi_check,PointersToHereCheck pointers_to_here_check_for_value)457 void MacroAssembler::RecordWrite(
458 Register object, Register address, Register value, SaveFPRegsMode fp_mode,
459 RememberedSetAction remembered_set_action, SmiCheck smi_check,
460 PointersToHereCheck pointers_to_here_check_for_value) {
461 DCHECK(!object.is(value));
462 DCHECK(!object.is(address));
463 DCHECK(!value.is(address));
464 AssertNotSmi(object);
465
466 if (remembered_set_action == OMIT_REMEMBERED_SET &&
467 !FLAG_incremental_marking) {
468 return;
469 }
470
471 if (emit_debug_code()) {
472 Label ok;
473 cmp(value, Operand(address, 0));
474 j(equal, &ok, Label::kNear);
475 int3();
476 bind(&ok);
477 }
478
479 // First, check if a write barrier is even needed. The tests below
480 // catch stores of Smis and stores into young gen.
481 Label done;
482
483 if (smi_check == INLINE_SMI_CHECK) {
484 // Skip barrier if writing a smi.
485 JumpIfSmi(value, &done, Label::kNear);
486 }
487
488 if (pointers_to_here_check_for_value != kPointersToHereAreAlwaysInteresting) {
489 CheckPageFlag(value,
490 value, // Used as scratch.
491 MemoryChunk::kPointersToHereAreInterestingMask,
492 zero,
493 &done,
494 Label::kNear);
495 }
496 CheckPageFlag(object,
497 value, // Used as scratch.
498 MemoryChunk::kPointersFromHereAreInterestingMask,
499 zero,
500 &done,
501 Label::kNear);
502
503 RecordWriteStub stub(isolate(), object, value, address, remembered_set_action,
504 fp_mode);
505 CallStub(&stub);
506
507 bind(&done);
508
509 // Count number of write barriers in generated code.
510 isolate()->counters()->write_barriers_static()->Increment();
511 IncrementCounter(isolate()->counters()->write_barriers_dynamic(), 1);
512
513 // Clobber clobbered registers when running with the debug-code flag
514 // turned on to provoke errors.
515 if (emit_debug_code()) {
516 mov(address, Immediate(bit_cast<int32_t>(kZapValue)));
517 mov(value, Immediate(bit_cast<int32_t>(kZapValue)));
518 }
519 }
520
RecordWriteCodeEntryField(Register js_function,Register code_entry,Register scratch)521 void MacroAssembler::RecordWriteCodeEntryField(Register js_function,
522 Register code_entry,
523 Register scratch) {
524 const int offset = JSFunction::kCodeEntryOffset;
525
526 // Since a code entry (value) is always in old space, we don't need to update
527 // remembered set. If incremental marking is off, there is nothing for us to
528 // do.
529 if (!FLAG_incremental_marking) return;
530
531 DCHECK(!js_function.is(code_entry));
532 DCHECK(!js_function.is(scratch));
533 DCHECK(!code_entry.is(scratch));
534 AssertNotSmi(js_function);
535
536 if (emit_debug_code()) {
537 Label ok;
538 lea(scratch, FieldOperand(js_function, offset));
539 cmp(code_entry, Operand(scratch, 0));
540 j(equal, &ok, Label::kNear);
541 int3();
542 bind(&ok);
543 }
544
545 // First, check if a write barrier is even needed. The tests below
546 // catch stores of Smis and stores into young gen.
547 Label done;
548
549 CheckPageFlag(code_entry, scratch,
550 MemoryChunk::kPointersToHereAreInterestingMask, zero, &done,
551 Label::kNear);
552 CheckPageFlag(js_function, scratch,
553 MemoryChunk::kPointersFromHereAreInterestingMask, zero, &done,
554 Label::kNear);
555
556 // Save input registers.
557 push(js_function);
558 push(code_entry);
559
560 const Register dst = scratch;
561 lea(dst, FieldOperand(js_function, offset));
562
563 // Save caller-saved registers.
564 PushCallerSaved(kDontSaveFPRegs, js_function, code_entry);
565
566 int argument_count = 3;
567 PrepareCallCFunction(argument_count, code_entry);
568 mov(Operand(esp, 0 * kPointerSize), js_function);
569 mov(Operand(esp, 1 * kPointerSize), dst); // Slot.
570 mov(Operand(esp, 2 * kPointerSize),
571 Immediate(ExternalReference::isolate_address(isolate())));
572
573 {
574 AllowExternalCallThatCantCauseGC scope(this);
575 CallCFunction(
576 ExternalReference::incremental_marking_record_write_code_entry_function(
577 isolate()),
578 argument_count);
579 }
580
581 // Restore caller-saved registers.
582 PopCallerSaved(kDontSaveFPRegs, js_function, code_entry);
583
584 // Restore input registers.
585 pop(code_entry);
586 pop(js_function);
587
588 bind(&done);
589 }
590
DebugBreak()591 void MacroAssembler::DebugBreak() {
592 Move(eax, Immediate(0));
593 mov(ebx, Immediate(ExternalReference(Runtime::kHandleDebuggerStatement,
594 isolate())));
595 CEntryStub ces(isolate(), 1);
596 call(ces.GetCode(), RelocInfo::DEBUGGER_STATEMENT);
597 }
598
ShlPair(Register high,Register low,uint8_t shift)599 void MacroAssembler::ShlPair(Register high, Register low, uint8_t shift) {
600 if (shift >= 32) {
601 mov(high, low);
602 shl(high, shift - 32);
603 xor_(low, low);
604 } else {
605 shld(high, low, shift);
606 shl(low, shift);
607 }
608 }
609
ShlPair_cl(Register high,Register low)610 void MacroAssembler::ShlPair_cl(Register high, Register low) {
611 shld_cl(high, low);
612 shl_cl(low);
613 Label done;
614 test(ecx, Immediate(0x20));
615 j(equal, &done, Label::kNear);
616 mov(high, low);
617 xor_(low, low);
618 bind(&done);
619 }
620
ShrPair(Register high,Register low,uint8_t shift)621 void MacroAssembler::ShrPair(Register high, Register low, uint8_t shift) {
622 if (shift >= 32) {
623 mov(low, high);
624 shr(low, shift - 32);
625 xor_(high, high);
626 } else {
627 shrd(high, low, shift);
628 shr(high, shift);
629 }
630 }
631
ShrPair_cl(Register high,Register low)632 void MacroAssembler::ShrPair_cl(Register high, Register low) {
633 shrd_cl(low, high);
634 shr_cl(high);
635 Label done;
636 test(ecx, Immediate(0x20));
637 j(equal, &done, Label::kNear);
638 mov(low, high);
639 xor_(high, high);
640 bind(&done);
641 }
642
SarPair(Register high,Register low,uint8_t shift)643 void MacroAssembler::SarPair(Register high, Register low, uint8_t shift) {
644 if (shift >= 32) {
645 mov(low, high);
646 sar(low, shift - 32);
647 sar(high, 31);
648 } else {
649 shrd(high, low, shift);
650 sar(high, shift);
651 }
652 }
653
SarPair_cl(Register high,Register low)654 void MacroAssembler::SarPair_cl(Register high, Register low) {
655 shrd_cl(low, high);
656 sar_cl(high);
657 Label done;
658 test(ecx, Immediate(0x20));
659 j(equal, &done, Label::kNear);
660 mov(low, high);
661 sar(high, 31);
662 bind(&done);
663 }
664
IsUnsafeImmediate(const Immediate & x)665 bool MacroAssembler::IsUnsafeImmediate(const Immediate& x) {
666 static const int kMaxImmediateBits = 17;
667 if (!RelocInfo::IsNone(x.rmode_)) return false;
668 return !is_intn(x.x_, kMaxImmediateBits);
669 }
670
671
SafeMove(Register dst,const Immediate & x)672 void MacroAssembler::SafeMove(Register dst, const Immediate& x) {
673 if (IsUnsafeImmediate(x) && jit_cookie() != 0) {
674 Move(dst, Immediate(x.x_ ^ jit_cookie()));
675 xor_(dst, jit_cookie());
676 } else {
677 Move(dst, x);
678 }
679 }
680
681
SafePush(const Immediate & x)682 void MacroAssembler::SafePush(const Immediate& x) {
683 if (IsUnsafeImmediate(x) && jit_cookie() != 0) {
684 push(Immediate(x.x_ ^ jit_cookie()));
685 xor_(Operand(esp, 0), Immediate(jit_cookie()));
686 } else {
687 push(x);
688 }
689 }
690
691
CmpObjectType(Register heap_object,InstanceType type,Register map)692 void MacroAssembler::CmpObjectType(Register heap_object,
693 InstanceType type,
694 Register map) {
695 mov(map, FieldOperand(heap_object, HeapObject::kMapOffset));
696 CmpInstanceType(map, type);
697 }
698
699
CmpInstanceType(Register map,InstanceType type)700 void MacroAssembler::CmpInstanceType(Register map, InstanceType type) {
701 cmpb(FieldOperand(map, Map::kInstanceTypeOffset), Immediate(type));
702 }
703
CheckFastObjectElements(Register map,Label * fail,Label::Distance distance)704 void MacroAssembler::CheckFastObjectElements(Register map,
705 Label* fail,
706 Label::Distance distance) {
707 STATIC_ASSERT(FAST_SMI_ELEMENTS == 0);
708 STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1);
709 STATIC_ASSERT(FAST_ELEMENTS == 2);
710 STATIC_ASSERT(FAST_HOLEY_ELEMENTS == 3);
711 cmpb(FieldOperand(map, Map::kBitField2Offset),
712 Immediate(Map::kMaximumBitField2FastHoleySmiElementValue));
713 j(below_equal, fail, distance);
714 cmpb(FieldOperand(map, Map::kBitField2Offset),
715 Immediate(Map::kMaximumBitField2FastHoleyElementValue));
716 j(above, fail, distance);
717 }
718
719
CheckFastSmiElements(Register map,Label * fail,Label::Distance distance)720 void MacroAssembler::CheckFastSmiElements(Register map,
721 Label* fail,
722 Label::Distance distance) {
723 STATIC_ASSERT(FAST_SMI_ELEMENTS == 0);
724 STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1);
725 cmpb(FieldOperand(map, Map::kBitField2Offset),
726 Immediate(Map::kMaximumBitField2FastHoleySmiElementValue));
727 j(above, fail, distance);
728 }
729
730
StoreNumberToDoubleElements(Register maybe_number,Register elements,Register key,Register scratch,Label * fail,int elements_offset)731 void MacroAssembler::StoreNumberToDoubleElements(
732 Register maybe_number,
733 Register elements,
734 Register key,
735 Register scratch,
736 Label* fail,
737 int elements_offset) {
738 Label smi_value, done, maybe_nan, not_nan, is_nan, have_double_value;
739 JumpIfSmi(maybe_number, &smi_value, Label::kNear);
740
741 CheckMap(maybe_number,
742 isolate()->factory()->heap_number_map(),
743 fail,
744 DONT_DO_SMI_CHECK);
745
746 fld_d(FieldOperand(maybe_number, HeapNumber::kValueOffset));
747 jmp(&done, Label::kNear);
748
749 bind(&smi_value);
750 // Value is a smi. Convert to a double and store.
751 // Preserve original value.
752 mov(scratch, maybe_number);
753 SmiUntag(scratch);
754 push(scratch);
755 fild_s(Operand(esp, 0));
756 pop(scratch);
757 bind(&done);
758 fstp_d(FieldOperand(elements, key, times_4,
759 FixedDoubleArray::kHeaderSize - elements_offset));
760 }
761
762
CompareMap(Register obj,Handle<Map> map)763 void MacroAssembler::CompareMap(Register obj, Handle<Map> map) {
764 cmp(FieldOperand(obj, HeapObject::kMapOffset), map);
765 }
766
767
CheckMap(Register obj,Handle<Map> map,Label * fail,SmiCheckType smi_check_type)768 void MacroAssembler::CheckMap(Register obj,
769 Handle<Map> map,
770 Label* fail,
771 SmiCheckType smi_check_type) {
772 if (smi_check_type == DO_SMI_CHECK) {
773 JumpIfSmi(obj, fail);
774 }
775
776 CompareMap(obj, map);
777 j(not_equal, fail);
778 }
779
780
DispatchWeakMap(Register obj,Register scratch1,Register scratch2,Handle<WeakCell> cell,Handle<Code> success,SmiCheckType smi_check_type)781 void MacroAssembler::DispatchWeakMap(Register obj, Register scratch1,
782 Register scratch2, Handle<WeakCell> cell,
783 Handle<Code> success,
784 SmiCheckType smi_check_type) {
785 Label fail;
786 if (smi_check_type == DO_SMI_CHECK) {
787 JumpIfSmi(obj, &fail);
788 }
789 mov(scratch1, FieldOperand(obj, HeapObject::kMapOffset));
790 CmpWeakValue(scratch1, cell, scratch2);
791 j(equal, success);
792
793 bind(&fail);
794 }
795
796
IsObjectStringType(Register heap_object,Register map,Register instance_type)797 Condition MacroAssembler::IsObjectStringType(Register heap_object,
798 Register map,
799 Register instance_type) {
800 mov(map, FieldOperand(heap_object, HeapObject::kMapOffset));
801 movzx_b(instance_type, FieldOperand(map, Map::kInstanceTypeOffset));
802 STATIC_ASSERT(kNotStringTag != 0);
803 test(instance_type, Immediate(kIsNotStringMask));
804 return zero;
805 }
806
807
IsObjectNameType(Register heap_object,Register map,Register instance_type)808 Condition MacroAssembler::IsObjectNameType(Register heap_object,
809 Register map,
810 Register instance_type) {
811 mov(map, FieldOperand(heap_object, HeapObject::kMapOffset));
812 movzx_b(instance_type, FieldOperand(map, Map::kInstanceTypeOffset));
813 cmpb(instance_type, Immediate(LAST_NAME_TYPE));
814 return below_equal;
815 }
816
817
FCmp()818 void MacroAssembler::FCmp() {
819 fucompp();
820 push(eax);
821 fnstsw_ax();
822 sahf();
823 pop(eax);
824 }
825
826
FXamMinusZero()827 void MacroAssembler::FXamMinusZero() {
828 fxam();
829 push(eax);
830 fnstsw_ax();
831 and_(eax, Immediate(0x4700));
832 // For minus zero, C3 == 1 && C1 == 1.
833 cmp(eax, Immediate(0x4200));
834 pop(eax);
835 fstp(0);
836 }
837
838
FXamSign()839 void MacroAssembler::FXamSign() {
840 fxam();
841 push(eax);
842 fnstsw_ax();
843 // For negative value (including -0.0), C1 == 1.
844 and_(eax, Immediate(0x0200));
845 pop(eax);
846 fstp(0);
847 }
848
849
X87CheckIA()850 void MacroAssembler::X87CheckIA() {
851 push(eax);
852 fnstsw_ax();
853 // For #IA, IE == 1 && SF == 0.
854 and_(eax, Immediate(0x0041));
855 cmp(eax, Immediate(0x0001));
856 pop(eax);
857 }
858
859
860 // rc=00B, round to nearest.
861 // rc=01B, round down.
862 // rc=10B, round up.
863 // rc=11B, round toward zero.
X87SetRC(int rc)864 void MacroAssembler::X87SetRC(int rc) {
865 sub(esp, Immediate(kPointerSize));
866 fnstcw(MemOperand(esp, 0));
867 and_(MemOperand(esp, 0), Immediate(0xF3FF));
868 or_(MemOperand(esp, 0), Immediate(rc));
869 fldcw(MemOperand(esp, 0));
870 add(esp, Immediate(kPointerSize));
871 }
872
873
X87SetFPUCW(int cw)874 void MacroAssembler::X87SetFPUCW(int cw) {
875 RecordComment("-- X87SetFPUCW start --");
876 push(Immediate(cw));
877 fldcw(MemOperand(esp, 0));
878 add(esp, Immediate(kPointerSize));
879 RecordComment("-- X87SetFPUCW end--");
880 }
881
882
AssertNumber(Register object)883 void MacroAssembler::AssertNumber(Register object) {
884 if (emit_debug_code()) {
885 Label ok;
886 JumpIfSmi(object, &ok);
887 cmp(FieldOperand(object, HeapObject::kMapOffset),
888 isolate()->factory()->heap_number_map());
889 Check(equal, kOperandNotANumber);
890 bind(&ok);
891 }
892 }
893
AssertNotNumber(Register object)894 void MacroAssembler::AssertNotNumber(Register object) {
895 if (emit_debug_code()) {
896 test(object, Immediate(kSmiTagMask));
897 Check(not_equal, kOperandIsANumber);
898 cmp(FieldOperand(object, HeapObject::kMapOffset),
899 isolate()->factory()->heap_number_map());
900 Check(not_equal, kOperandIsANumber);
901 }
902 }
903
AssertSmi(Register object)904 void MacroAssembler::AssertSmi(Register object) {
905 if (emit_debug_code()) {
906 test(object, Immediate(kSmiTagMask));
907 Check(equal, kOperandIsNotASmi);
908 }
909 }
910
911
AssertString(Register object)912 void MacroAssembler::AssertString(Register object) {
913 if (emit_debug_code()) {
914 test(object, Immediate(kSmiTagMask));
915 Check(not_equal, kOperandIsASmiAndNotAString);
916 push(object);
917 mov(object, FieldOperand(object, HeapObject::kMapOffset));
918 CmpInstanceType(object, FIRST_NONSTRING_TYPE);
919 pop(object);
920 Check(below, kOperandIsNotAString);
921 }
922 }
923
924
AssertName(Register object)925 void MacroAssembler::AssertName(Register object) {
926 if (emit_debug_code()) {
927 test(object, Immediate(kSmiTagMask));
928 Check(not_equal, kOperandIsASmiAndNotAName);
929 push(object);
930 mov(object, FieldOperand(object, HeapObject::kMapOffset));
931 CmpInstanceType(object, LAST_NAME_TYPE);
932 pop(object);
933 Check(below_equal, kOperandIsNotAName);
934 }
935 }
936
937
AssertFunction(Register object)938 void MacroAssembler::AssertFunction(Register object) {
939 if (emit_debug_code()) {
940 test(object, Immediate(kSmiTagMask));
941 Check(not_equal, kOperandIsASmiAndNotAFunction);
942 Push(object);
943 CmpObjectType(object, JS_FUNCTION_TYPE, object);
944 Pop(object);
945 Check(equal, kOperandIsNotAFunction);
946 }
947 }
948
949
AssertBoundFunction(Register object)950 void MacroAssembler::AssertBoundFunction(Register object) {
951 if (emit_debug_code()) {
952 test(object, Immediate(kSmiTagMask));
953 Check(not_equal, kOperandIsASmiAndNotABoundFunction);
954 Push(object);
955 CmpObjectType(object, JS_BOUND_FUNCTION_TYPE, object);
956 Pop(object);
957 Check(equal, kOperandIsNotABoundFunction);
958 }
959 }
960
AssertGeneratorObject(Register object)961 void MacroAssembler::AssertGeneratorObject(Register object) {
962 if (emit_debug_code()) {
963 test(object, Immediate(kSmiTagMask));
964 Check(not_equal, kOperandIsASmiAndNotAGeneratorObject);
965 Push(object);
966 CmpObjectType(object, JS_GENERATOR_OBJECT_TYPE, object);
967 Pop(object);
968 Check(equal, kOperandIsNotAGeneratorObject);
969 }
970 }
971
AssertReceiver(Register object)972 void MacroAssembler::AssertReceiver(Register object) {
973 if (emit_debug_code()) {
974 test(object, Immediate(kSmiTagMask));
975 Check(not_equal, kOperandIsASmiAndNotAReceiver);
976 Push(object);
977 STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE);
978 CmpObjectType(object, FIRST_JS_RECEIVER_TYPE, object);
979 Pop(object);
980 Check(above_equal, kOperandIsNotAReceiver);
981 }
982 }
983
AssertUndefinedOrAllocationSite(Register object)984 void MacroAssembler::AssertUndefinedOrAllocationSite(Register object) {
985 if (emit_debug_code()) {
986 Label done_checking;
987 AssertNotSmi(object);
988 cmp(object, isolate()->factory()->undefined_value());
989 j(equal, &done_checking);
990 cmp(FieldOperand(object, 0),
991 Immediate(isolate()->factory()->allocation_site_map()));
992 Assert(equal, kExpectedUndefinedOrCell);
993 bind(&done_checking);
994 }
995 }
996
997
AssertNotSmi(Register object)998 void MacroAssembler::AssertNotSmi(Register object) {
999 if (emit_debug_code()) {
1000 test(object, Immediate(kSmiTagMask));
1001 Check(not_equal, kOperandIsASmi);
1002 }
1003 }
1004
StubPrologue(StackFrame::Type type)1005 void MacroAssembler::StubPrologue(StackFrame::Type type) {
1006 push(ebp); // Caller's frame pointer.
1007 mov(ebp, esp);
1008 push(Immediate(Smi::FromInt(type)));
1009 }
1010
1011
Prologue(bool code_pre_aging)1012 void MacroAssembler::Prologue(bool code_pre_aging) {
1013 PredictableCodeSizeScope predictible_code_size_scope(this,
1014 kNoCodeAgeSequenceLength);
1015 if (code_pre_aging) {
1016 // Pre-age the code.
1017 call(isolate()->builtins()->MarkCodeAsExecutedOnce(),
1018 RelocInfo::CODE_AGE_SEQUENCE);
1019 Nop(kNoCodeAgeSequenceLength - Assembler::kCallInstructionLength);
1020 } else {
1021 push(ebp); // Caller's frame pointer.
1022 mov(ebp, esp);
1023 push(esi); // Callee's context.
1024 push(edi); // Callee's JS function.
1025 }
1026 }
1027
1028
EmitLoadTypeFeedbackVector(Register vector)1029 void MacroAssembler::EmitLoadTypeFeedbackVector(Register vector) {
1030 mov(vector, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
1031 mov(vector, FieldOperand(vector, JSFunction::kLiteralsOffset));
1032 mov(vector, FieldOperand(vector, LiteralsArray::kFeedbackVectorOffset));
1033 }
1034
1035
EnterFrame(StackFrame::Type type,bool load_constant_pool_pointer_reg)1036 void MacroAssembler::EnterFrame(StackFrame::Type type,
1037 bool load_constant_pool_pointer_reg) {
1038 // Out-of-line constant pool not implemented on x87.
1039 UNREACHABLE();
1040 }
1041
1042
EnterFrame(StackFrame::Type type)1043 void MacroAssembler::EnterFrame(StackFrame::Type type) {
1044 push(ebp);
1045 mov(ebp, esp);
1046 push(Immediate(Smi::FromInt(type)));
1047 if (type == StackFrame::INTERNAL) {
1048 push(Immediate(CodeObject()));
1049 }
1050 if (emit_debug_code()) {
1051 cmp(Operand(esp, 0), Immediate(isolate()->factory()->undefined_value()));
1052 Check(not_equal, kCodeObjectNotProperlyPatched);
1053 }
1054 }
1055
1056
LeaveFrame(StackFrame::Type type)1057 void MacroAssembler::LeaveFrame(StackFrame::Type type) {
1058 if (emit_debug_code()) {
1059 cmp(Operand(ebp, CommonFrameConstants::kContextOrFrameTypeOffset),
1060 Immediate(Smi::FromInt(type)));
1061 Check(equal, kStackFrameTypesMustMatch);
1062 }
1063 leave();
1064 }
1065
EnterBuiltinFrame(Register context,Register target,Register argc)1066 void MacroAssembler::EnterBuiltinFrame(Register context, Register target,
1067 Register argc) {
1068 Push(ebp);
1069 Move(ebp, esp);
1070 Push(context);
1071 Push(target);
1072 Push(argc);
1073 }
1074
LeaveBuiltinFrame(Register context,Register target,Register argc)1075 void MacroAssembler::LeaveBuiltinFrame(Register context, Register target,
1076 Register argc) {
1077 Pop(argc);
1078 Pop(target);
1079 Pop(context);
1080 leave();
1081 }
1082
EnterExitFramePrologue(StackFrame::Type frame_type)1083 void MacroAssembler::EnterExitFramePrologue(StackFrame::Type frame_type) {
1084 DCHECK(frame_type == StackFrame::EXIT ||
1085 frame_type == StackFrame::BUILTIN_EXIT);
1086
1087 // Set up the frame structure on the stack.
1088 DCHECK_EQ(+2 * kPointerSize, ExitFrameConstants::kCallerSPDisplacement);
1089 DCHECK_EQ(+1 * kPointerSize, ExitFrameConstants::kCallerPCOffset);
1090 DCHECK_EQ(0 * kPointerSize, ExitFrameConstants::kCallerFPOffset);
1091 push(ebp);
1092 mov(ebp, esp);
1093
1094 // Reserve room for entry stack pointer and push the code object.
1095 push(Immediate(Smi::FromInt(frame_type)));
1096 DCHECK_EQ(-2 * kPointerSize, ExitFrameConstants::kSPOffset);
1097 push(Immediate(0)); // Saved entry sp, patched before call.
1098 DCHECK_EQ(-3 * kPointerSize, ExitFrameConstants::kCodeOffset);
1099 push(Immediate(CodeObject())); // Accessed from ExitFrame::code_slot.
1100
1101 // Save the frame pointer and the context in top.
1102 ExternalReference c_entry_fp_address(Isolate::kCEntryFPAddress, isolate());
1103 ExternalReference context_address(Isolate::kContextAddress, isolate());
1104 ExternalReference c_function_address(Isolate::kCFunctionAddress, isolate());
1105 mov(Operand::StaticVariable(c_entry_fp_address), ebp);
1106 mov(Operand::StaticVariable(context_address), esi);
1107 mov(Operand::StaticVariable(c_function_address), ebx);
1108 }
1109
1110
EnterExitFrameEpilogue(int argc,bool save_doubles)1111 void MacroAssembler::EnterExitFrameEpilogue(int argc, bool save_doubles) {
1112 // Optionally save FPU state.
1113 if (save_doubles) {
1114 // Store FPU state to m108byte.
1115 int space = 108 + argc * kPointerSize;
1116 sub(esp, Immediate(space));
1117 const int offset = -ExitFrameConstants::kFixedFrameSizeFromFp;
1118 fnsave(MemOperand(ebp, offset - 108));
1119 } else {
1120 sub(esp, Immediate(argc * kPointerSize));
1121 }
1122
1123 // Get the required frame alignment for the OS.
1124 const int kFrameAlignment = base::OS::ActivationFrameAlignment();
1125 if (kFrameAlignment > 0) {
1126 DCHECK(base::bits::IsPowerOfTwo32(kFrameAlignment));
1127 and_(esp, -kFrameAlignment);
1128 }
1129
1130 // Patch the saved entry sp.
1131 mov(Operand(ebp, ExitFrameConstants::kSPOffset), esp);
1132 }
1133
EnterExitFrame(int argc,bool save_doubles,StackFrame::Type frame_type)1134 void MacroAssembler::EnterExitFrame(int argc, bool save_doubles,
1135 StackFrame::Type frame_type) {
1136 EnterExitFramePrologue(frame_type);
1137
1138 // Set up argc and argv in callee-saved registers.
1139 int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize;
1140 mov(edi, eax);
1141 lea(esi, Operand(ebp, eax, times_4, offset));
1142
1143 // Reserve space for argc, argv and isolate.
1144 EnterExitFrameEpilogue(argc, save_doubles);
1145 }
1146
1147
EnterApiExitFrame(int argc)1148 void MacroAssembler::EnterApiExitFrame(int argc) {
1149 EnterExitFramePrologue(StackFrame::EXIT);
1150 EnterExitFrameEpilogue(argc, false);
1151 }
1152
1153
LeaveExitFrame(bool save_doubles,bool pop_arguments)1154 void MacroAssembler::LeaveExitFrame(bool save_doubles, bool pop_arguments) {
1155 // Optionally restore FPU state.
1156 if (save_doubles) {
1157 const int offset = -ExitFrameConstants::kFixedFrameSizeFromFp;
1158 frstor(MemOperand(ebp, offset - 108));
1159 }
1160
1161 if (pop_arguments) {
1162 // Get the return address from the stack and restore the frame pointer.
1163 mov(ecx, Operand(ebp, 1 * kPointerSize));
1164 mov(ebp, Operand(ebp, 0 * kPointerSize));
1165
1166 // Pop the arguments and the receiver from the caller stack.
1167 lea(esp, Operand(esi, 1 * kPointerSize));
1168
1169 // Push the return address to get ready to return.
1170 push(ecx);
1171 } else {
1172 // Otherwise just leave the exit frame.
1173 leave();
1174 }
1175
1176 LeaveExitFrameEpilogue(true);
1177 }
1178
1179
LeaveExitFrameEpilogue(bool restore_context)1180 void MacroAssembler::LeaveExitFrameEpilogue(bool restore_context) {
1181 // Restore current context from top and clear it in debug mode.
1182 ExternalReference context_address(Isolate::kContextAddress, isolate());
1183 if (restore_context) {
1184 mov(esi, Operand::StaticVariable(context_address));
1185 }
1186 #ifdef DEBUG
1187 mov(Operand::StaticVariable(context_address), Immediate(0));
1188 #endif
1189
1190 // Clear the top frame.
1191 ExternalReference c_entry_fp_address(Isolate::kCEntryFPAddress,
1192 isolate());
1193 mov(Operand::StaticVariable(c_entry_fp_address), Immediate(0));
1194 }
1195
1196
LeaveApiExitFrame(bool restore_context)1197 void MacroAssembler::LeaveApiExitFrame(bool restore_context) {
1198 mov(esp, ebp);
1199 pop(ebp);
1200
1201 LeaveExitFrameEpilogue(restore_context);
1202 }
1203
1204
PushStackHandler()1205 void MacroAssembler::PushStackHandler() {
1206 // Adjust this code if not the case.
1207 STATIC_ASSERT(StackHandlerConstants::kSize == 1 * kPointerSize);
1208 STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
1209
1210 // Link the current handler as the next handler.
1211 ExternalReference handler_address(Isolate::kHandlerAddress, isolate());
1212 push(Operand::StaticVariable(handler_address));
1213
1214 // Set this new handler as the current one.
1215 mov(Operand::StaticVariable(handler_address), esp);
1216 }
1217
1218
PopStackHandler()1219 void MacroAssembler::PopStackHandler() {
1220 STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
1221 ExternalReference handler_address(Isolate::kHandlerAddress, isolate());
1222 pop(Operand::StaticVariable(handler_address));
1223 add(esp, Immediate(StackHandlerConstants::kSize - kPointerSize));
1224 }
1225
1226
1227 // Compute the hash code from the untagged key. This must be kept in sync with
1228 // ComputeIntegerHash in utils.h and KeyedLoadGenericStub in
1229 // code-stub-hydrogen.cc
1230 //
1231 // Note: r0 will contain hash code
GetNumberHash(Register r0,Register scratch)1232 void MacroAssembler::GetNumberHash(Register r0, Register scratch) {
1233 // Xor original key with a seed.
1234 if (serializer_enabled()) {
1235 ExternalReference roots_array_start =
1236 ExternalReference::roots_array_start(isolate());
1237 mov(scratch, Immediate(Heap::kHashSeedRootIndex));
1238 mov(scratch,
1239 Operand::StaticArray(scratch, times_pointer_size, roots_array_start));
1240 SmiUntag(scratch);
1241 xor_(r0, scratch);
1242 } else {
1243 int32_t seed = isolate()->heap()->HashSeed();
1244 xor_(r0, Immediate(seed));
1245 }
1246
1247 // hash = ~hash + (hash << 15);
1248 mov(scratch, r0);
1249 not_(r0);
1250 shl(scratch, 15);
1251 add(r0, scratch);
1252 // hash = hash ^ (hash >> 12);
1253 mov(scratch, r0);
1254 shr(scratch, 12);
1255 xor_(r0, scratch);
1256 // hash = hash + (hash << 2);
1257 lea(r0, Operand(r0, r0, times_4, 0));
1258 // hash = hash ^ (hash >> 4);
1259 mov(scratch, r0);
1260 shr(scratch, 4);
1261 xor_(r0, scratch);
1262 // hash = hash * 2057;
1263 imul(r0, r0, 2057);
1264 // hash = hash ^ (hash >> 16);
1265 mov(scratch, r0);
1266 shr(scratch, 16);
1267 xor_(r0, scratch);
1268 and_(r0, 0x3fffffff);
1269 }
1270
LoadAllocationTopHelper(Register result,Register scratch,AllocationFlags flags)1271 void MacroAssembler::LoadAllocationTopHelper(Register result,
1272 Register scratch,
1273 AllocationFlags flags) {
1274 ExternalReference allocation_top =
1275 AllocationUtils::GetAllocationTopReference(isolate(), flags);
1276
1277 // Just return if allocation top is already known.
1278 if ((flags & RESULT_CONTAINS_TOP) != 0) {
1279 // No use of scratch if allocation top is provided.
1280 DCHECK(scratch.is(no_reg));
1281 #ifdef DEBUG
1282 // Assert that result actually contains top on entry.
1283 cmp(result, Operand::StaticVariable(allocation_top));
1284 Check(equal, kUnexpectedAllocationTop);
1285 #endif
1286 return;
1287 }
1288
1289 // Move address of new object to result. Use scratch register if available.
1290 if (scratch.is(no_reg)) {
1291 mov(result, Operand::StaticVariable(allocation_top));
1292 } else {
1293 mov(scratch, Immediate(allocation_top));
1294 mov(result, Operand(scratch, 0));
1295 }
1296 }
1297
1298
UpdateAllocationTopHelper(Register result_end,Register scratch,AllocationFlags flags)1299 void MacroAssembler::UpdateAllocationTopHelper(Register result_end,
1300 Register scratch,
1301 AllocationFlags flags) {
1302 if (emit_debug_code()) {
1303 test(result_end, Immediate(kObjectAlignmentMask));
1304 Check(zero, kUnalignedAllocationInNewSpace);
1305 }
1306
1307 ExternalReference allocation_top =
1308 AllocationUtils::GetAllocationTopReference(isolate(), flags);
1309
1310 // Update new top. Use scratch if available.
1311 if (scratch.is(no_reg)) {
1312 mov(Operand::StaticVariable(allocation_top), result_end);
1313 } else {
1314 mov(Operand(scratch, 0), result_end);
1315 }
1316 }
1317
1318
Allocate(int object_size,Register result,Register result_end,Register scratch,Label * gc_required,AllocationFlags flags)1319 void MacroAssembler::Allocate(int object_size,
1320 Register result,
1321 Register result_end,
1322 Register scratch,
1323 Label* gc_required,
1324 AllocationFlags flags) {
1325 DCHECK((flags & (RESULT_CONTAINS_TOP | SIZE_IN_WORDS)) == 0);
1326 DCHECK(object_size <= kMaxRegularHeapObjectSize);
1327 DCHECK((flags & ALLOCATION_FOLDED) == 0);
1328 if (!FLAG_inline_new) {
1329 if (emit_debug_code()) {
1330 // Trash the registers to simulate an allocation failure.
1331 mov(result, Immediate(0x7091));
1332 if (result_end.is_valid()) {
1333 mov(result_end, Immediate(0x7191));
1334 }
1335 if (scratch.is_valid()) {
1336 mov(scratch, Immediate(0x7291));
1337 }
1338 }
1339 jmp(gc_required);
1340 return;
1341 }
1342 DCHECK(!result.is(result_end));
1343
1344 // Load address of new object into result.
1345 LoadAllocationTopHelper(result, scratch, flags);
1346
1347 ExternalReference allocation_limit =
1348 AllocationUtils::GetAllocationLimitReference(isolate(), flags);
1349
1350 // Align the next allocation. Storing the filler map without checking top is
1351 // safe in new-space because the limit of the heap is aligned there.
1352 if ((flags & DOUBLE_ALIGNMENT) != 0) {
1353 DCHECK(kPointerAlignment * 2 == kDoubleAlignment);
1354 Label aligned;
1355 test(result, Immediate(kDoubleAlignmentMask));
1356 j(zero, &aligned, Label::kNear);
1357 if ((flags & PRETENURE) != 0) {
1358 cmp(result, Operand::StaticVariable(allocation_limit));
1359 j(above_equal, gc_required);
1360 }
1361 mov(Operand(result, 0),
1362 Immediate(isolate()->factory()->one_pointer_filler_map()));
1363 add(result, Immediate(kDoubleSize / 2));
1364 bind(&aligned);
1365 }
1366
1367 // Calculate new top and bail out if space is exhausted.
1368 Register top_reg = result_end.is_valid() ? result_end : result;
1369
1370 if (!top_reg.is(result)) {
1371 mov(top_reg, result);
1372 }
1373 add(top_reg, Immediate(object_size));
1374 cmp(top_reg, Operand::StaticVariable(allocation_limit));
1375 j(above, gc_required);
1376
1377 if ((flags & ALLOCATION_FOLDING_DOMINATOR) == 0) {
1378 // The top pointer is not updated for allocation folding dominators.
1379 UpdateAllocationTopHelper(top_reg, scratch, flags);
1380 }
1381
1382 if (top_reg.is(result)) {
1383 sub(result, Immediate(object_size - kHeapObjectTag));
1384 } else {
1385 // Tag the result.
1386 DCHECK(kHeapObjectTag == 1);
1387 inc(result);
1388 }
1389 }
1390
1391
Allocate(int header_size,ScaleFactor element_size,Register element_count,RegisterValueType element_count_type,Register result,Register result_end,Register scratch,Label * gc_required,AllocationFlags flags)1392 void MacroAssembler::Allocate(int header_size,
1393 ScaleFactor element_size,
1394 Register element_count,
1395 RegisterValueType element_count_type,
1396 Register result,
1397 Register result_end,
1398 Register scratch,
1399 Label* gc_required,
1400 AllocationFlags flags) {
1401 DCHECK((flags & SIZE_IN_WORDS) == 0);
1402 DCHECK((flags & ALLOCATION_FOLDING_DOMINATOR) == 0);
1403 DCHECK((flags & ALLOCATION_FOLDED) == 0);
1404 if (!FLAG_inline_new) {
1405 if (emit_debug_code()) {
1406 // Trash the registers to simulate an allocation failure.
1407 mov(result, Immediate(0x7091));
1408 mov(result_end, Immediate(0x7191));
1409 if (scratch.is_valid()) {
1410 mov(scratch, Immediate(0x7291));
1411 }
1412 // Register element_count is not modified by the function.
1413 }
1414 jmp(gc_required);
1415 return;
1416 }
1417 DCHECK(!result.is(result_end));
1418
1419 // Load address of new object into result.
1420 LoadAllocationTopHelper(result, scratch, flags);
1421
1422 ExternalReference allocation_limit =
1423 AllocationUtils::GetAllocationLimitReference(isolate(), flags);
1424
1425 // Align the next allocation. Storing the filler map without checking top is
1426 // safe in new-space because the limit of the heap is aligned there.
1427 if ((flags & DOUBLE_ALIGNMENT) != 0) {
1428 DCHECK(kPointerAlignment * 2 == kDoubleAlignment);
1429 Label aligned;
1430 test(result, Immediate(kDoubleAlignmentMask));
1431 j(zero, &aligned, Label::kNear);
1432 if ((flags & PRETENURE) != 0) {
1433 cmp(result, Operand::StaticVariable(allocation_limit));
1434 j(above_equal, gc_required);
1435 }
1436 mov(Operand(result, 0),
1437 Immediate(isolate()->factory()->one_pointer_filler_map()));
1438 add(result, Immediate(kDoubleSize / 2));
1439 bind(&aligned);
1440 }
1441
1442 // Calculate new top and bail out if space is exhausted.
1443 // We assume that element_count*element_size + header_size does not
1444 // overflow.
1445 if (element_count_type == REGISTER_VALUE_IS_SMI) {
1446 STATIC_ASSERT(static_cast<ScaleFactor>(times_2 - 1) == times_1);
1447 STATIC_ASSERT(static_cast<ScaleFactor>(times_4 - 1) == times_2);
1448 STATIC_ASSERT(static_cast<ScaleFactor>(times_8 - 1) == times_4);
1449 DCHECK(element_size >= times_2);
1450 DCHECK(kSmiTagSize == 1);
1451 element_size = static_cast<ScaleFactor>(element_size - 1);
1452 } else {
1453 DCHECK(element_count_type == REGISTER_VALUE_IS_INT32);
1454 }
1455 lea(result_end, Operand(element_count, element_size, header_size));
1456 add(result_end, result);
1457 j(carry, gc_required);
1458 cmp(result_end, Operand::StaticVariable(allocation_limit));
1459 j(above, gc_required);
1460
1461 // Tag result.
1462 DCHECK(kHeapObjectTag == 1);
1463 inc(result);
1464
1465 // Update allocation top.
1466 UpdateAllocationTopHelper(result_end, scratch, flags);
1467 }
1468
Allocate(Register object_size,Register result,Register result_end,Register scratch,Label * gc_required,AllocationFlags flags)1469 void MacroAssembler::Allocate(Register object_size,
1470 Register result,
1471 Register result_end,
1472 Register scratch,
1473 Label* gc_required,
1474 AllocationFlags flags) {
1475 DCHECK((flags & (RESULT_CONTAINS_TOP | SIZE_IN_WORDS)) == 0);
1476 DCHECK((flags & ALLOCATION_FOLDED) == 0);
1477 if (!FLAG_inline_new) {
1478 if (emit_debug_code()) {
1479 // Trash the registers to simulate an allocation failure.
1480 mov(result, Immediate(0x7091));
1481 mov(result_end, Immediate(0x7191));
1482 if (scratch.is_valid()) {
1483 mov(scratch, Immediate(0x7291));
1484 }
1485 // object_size is left unchanged by this function.
1486 }
1487 jmp(gc_required);
1488 return;
1489 }
1490 DCHECK(!result.is(result_end));
1491
1492 // Load address of new object into result.
1493 LoadAllocationTopHelper(result, scratch, flags);
1494
1495 ExternalReference allocation_limit =
1496 AllocationUtils::GetAllocationLimitReference(isolate(), flags);
1497
1498 // Align the next allocation. Storing the filler map without checking top is
1499 // safe in new-space because the limit of the heap is aligned there.
1500 if ((flags & DOUBLE_ALIGNMENT) != 0) {
1501 DCHECK(kPointerAlignment * 2 == kDoubleAlignment);
1502 Label aligned;
1503 test(result, Immediate(kDoubleAlignmentMask));
1504 j(zero, &aligned, Label::kNear);
1505 if ((flags & PRETENURE) != 0) {
1506 cmp(result, Operand::StaticVariable(allocation_limit));
1507 j(above_equal, gc_required);
1508 }
1509 mov(Operand(result, 0),
1510 Immediate(isolate()->factory()->one_pointer_filler_map()));
1511 add(result, Immediate(kDoubleSize / 2));
1512 bind(&aligned);
1513 }
1514
1515 // Calculate new top and bail out if space is exhausted.
1516 if (!object_size.is(result_end)) {
1517 mov(result_end, object_size);
1518 }
1519 add(result_end, result);
1520 cmp(result_end, Operand::StaticVariable(allocation_limit));
1521 j(above, gc_required);
1522
1523 // Tag result.
1524 DCHECK(kHeapObjectTag == 1);
1525 inc(result);
1526
1527 if ((flags & ALLOCATION_FOLDING_DOMINATOR) == 0) {
1528 // The top pointer is not updated for allocation folding dominators.
1529 UpdateAllocationTopHelper(result_end, scratch, flags);
1530 }
1531 }
1532
FastAllocate(int object_size,Register result,Register result_end,AllocationFlags flags)1533 void MacroAssembler::FastAllocate(int object_size, Register result,
1534 Register result_end, AllocationFlags flags) {
1535 DCHECK(!result.is(result_end));
1536 // Load address of new object into result.
1537 LoadAllocationTopHelper(result, no_reg, flags);
1538
1539 if ((flags & DOUBLE_ALIGNMENT) != 0) {
1540 DCHECK(kPointerAlignment * 2 == kDoubleAlignment);
1541 Label aligned;
1542 test(result, Immediate(kDoubleAlignmentMask));
1543 j(zero, &aligned, Label::kNear);
1544 mov(Operand(result, 0),
1545 Immediate(isolate()->factory()->one_pointer_filler_map()));
1546 add(result, Immediate(kDoubleSize / 2));
1547 bind(&aligned);
1548 }
1549
1550 lea(result_end, Operand(result, object_size));
1551 UpdateAllocationTopHelper(result_end, no_reg, flags);
1552
1553 DCHECK(kHeapObjectTag == 1);
1554 inc(result);
1555 }
1556
FastAllocate(Register object_size,Register result,Register result_end,AllocationFlags flags)1557 void MacroAssembler::FastAllocate(Register object_size, Register result,
1558 Register result_end, AllocationFlags flags) {
1559 DCHECK(!result.is(result_end));
1560 // Load address of new object into result.
1561 LoadAllocationTopHelper(result, no_reg, flags);
1562
1563 if ((flags & DOUBLE_ALIGNMENT) != 0) {
1564 DCHECK(kPointerAlignment * 2 == kDoubleAlignment);
1565 Label aligned;
1566 test(result, Immediate(kDoubleAlignmentMask));
1567 j(zero, &aligned, Label::kNear);
1568 mov(Operand(result, 0),
1569 Immediate(isolate()->factory()->one_pointer_filler_map()));
1570 add(result, Immediate(kDoubleSize / 2));
1571 bind(&aligned);
1572 }
1573
1574 lea(result_end, Operand(result, object_size, times_1, 0));
1575 UpdateAllocationTopHelper(result_end, no_reg, flags);
1576
1577 DCHECK(kHeapObjectTag == 1);
1578 inc(result);
1579 }
1580
AllocateHeapNumber(Register result,Register scratch1,Register scratch2,Label * gc_required,MutableMode mode)1581 void MacroAssembler::AllocateHeapNumber(Register result,
1582 Register scratch1,
1583 Register scratch2,
1584 Label* gc_required,
1585 MutableMode mode) {
1586 // Allocate heap number in new space.
1587 Allocate(HeapNumber::kSize, result, scratch1, scratch2, gc_required,
1588 NO_ALLOCATION_FLAGS);
1589
1590 Handle<Map> map = mode == MUTABLE
1591 ? isolate()->factory()->mutable_heap_number_map()
1592 : isolate()->factory()->heap_number_map();
1593
1594 // Set the map.
1595 mov(FieldOperand(result, HeapObject::kMapOffset), Immediate(map));
1596 }
1597
1598
AllocateTwoByteString(Register result,Register length,Register scratch1,Register scratch2,Register scratch3,Label * gc_required)1599 void MacroAssembler::AllocateTwoByteString(Register result,
1600 Register length,
1601 Register scratch1,
1602 Register scratch2,
1603 Register scratch3,
1604 Label* gc_required) {
1605 // Calculate the number of bytes needed for the characters in the string while
1606 // observing object alignment.
1607 DCHECK((SeqTwoByteString::kHeaderSize & kObjectAlignmentMask) == 0);
1608 DCHECK(kShortSize == 2);
1609 // scratch1 = length * 2 + kObjectAlignmentMask.
1610 lea(scratch1, Operand(length, length, times_1, kObjectAlignmentMask));
1611 and_(scratch1, Immediate(~kObjectAlignmentMask));
1612
1613 // Allocate two byte string in new space.
1614 Allocate(SeqTwoByteString::kHeaderSize, times_1, scratch1,
1615 REGISTER_VALUE_IS_INT32, result, scratch2, scratch3, gc_required,
1616 NO_ALLOCATION_FLAGS);
1617
1618 // Set the map, length and hash field.
1619 mov(FieldOperand(result, HeapObject::kMapOffset),
1620 Immediate(isolate()->factory()->string_map()));
1621 mov(scratch1, length);
1622 SmiTag(scratch1);
1623 mov(FieldOperand(result, String::kLengthOffset), scratch1);
1624 mov(FieldOperand(result, String::kHashFieldOffset),
1625 Immediate(String::kEmptyHashField));
1626 }
1627
1628
AllocateOneByteString(Register result,Register length,Register scratch1,Register scratch2,Register scratch3,Label * gc_required)1629 void MacroAssembler::AllocateOneByteString(Register result, Register length,
1630 Register scratch1, Register scratch2,
1631 Register scratch3,
1632 Label* gc_required) {
1633 // Calculate the number of bytes needed for the characters in the string while
1634 // observing object alignment.
1635 DCHECK((SeqOneByteString::kHeaderSize & kObjectAlignmentMask) == 0);
1636 mov(scratch1, length);
1637 DCHECK(kCharSize == 1);
1638 add(scratch1, Immediate(kObjectAlignmentMask));
1639 and_(scratch1, Immediate(~kObjectAlignmentMask));
1640
1641 // Allocate one-byte string in new space.
1642 Allocate(SeqOneByteString::kHeaderSize, times_1, scratch1,
1643 REGISTER_VALUE_IS_INT32, result, scratch2, scratch3, gc_required,
1644 NO_ALLOCATION_FLAGS);
1645
1646 // Set the map, length and hash field.
1647 mov(FieldOperand(result, HeapObject::kMapOffset),
1648 Immediate(isolate()->factory()->one_byte_string_map()));
1649 mov(scratch1, length);
1650 SmiTag(scratch1);
1651 mov(FieldOperand(result, String::kLengthOffset), scratch1);
1652 mov(FieldOperand(result, String::kHashFieldOffset),
1653 Immediate(String::kEmptyHashField));
1654 }
1655
1656
AllocateOneByteString(Register result,int length,Register scratch1,Register scratch2,Label * gc_required)1657 void MacroAssembler::AllocateOneByteString(Register result, int length,
1658 Register scratch1, Register scratch2,
1659 Label* gc_required) {
1660 DCHECK(length > 0);
1661
1662 // Allocate one-byte string in new space.
1663 Allocate(SeqOneByteString::SizeFor(length), result, scratch1, scratch2,
1664 gc_required, NO_ALLOCATION_FLAGS);
1665
1666 // Set the map, length and hash field.
1667 mov(FieldOperand(result, HeapObject::kMapOffset),
1668 Immediate(isolate()->factory()->one_byte_string_map()));
1669 mov(FieldOperand(result, String::kLengthOffset),
1670 Immediate(Smi::FromInt(length)));
1671 mov(FieldOperand(result, String::kHashFieldOffset),
1672 Immediate(String::kEmptyHashField));
1673 }
1674
1675
AllocateTwoByteConsString(Register result,Register scratch1,Register scratch2,Label * gc_required)1676 void MacroAssembler::AllocateTwoByteConsString(Register result,
1677 Register scratch1,
1678 Register scratch2,
1679 Label* gc_required) {
1680 // Allocate heap number in new space.
1681 Allocate(ConsString::kSize, result, scratch1, scratch2, gc_required,
1682 NO_ALLOCATION_FLAGS);
1683
1684 // Set the map. The other fields are left uninitialized.
1685 mov(FieldOperand(result, HeapObject::kMapOffset),
1686 Immediate(isolate()->factory()->cons_string_map()));
1687 }
1688
1689
AllocateOneByteConsString(Register result,Register scratch1,Register scratch2,Label * gc_required)1690 void MacroAssembler::AllocateOneByteConsString(Register result,
1691 Register scratch1,
1692 Register scratch2,
1693 Label* gc_required) {
1694 Allocate(ConsString::kSize, result, scratch1, scratch2, gc_required,
1695 NO_ALLOCATION_FLAGS);
1696
1697 // Set the map. The other fields are left uninitialized.
1698 mov(FieldOperand(result, HeapObject::kMapOffset),
1699 Immediate(isolate()->factory()->cons_one_byte_string_map()));
1700 }
1701
1702
AllocateTwoByteSlicedString(Register result,Register scratch1,Register scratch2,Label * gc_required)1703 void MacroAssembler::AllocateTwoByteSlicedString(Register result,
1704 Register scratch1,
1705 Register scratch2,
1706 Label* gc_required) {
1707 // Allocate heap number in new space.
1708 Allocate(SlicedString::kSize, result, scratch1, scratch2, gc_required,
1709 NO_ALLOCATION_FLAGS);
1710
1711 // Set the map. The other fields are left uninitialized.
1712 mov(FieldOperand(result, HeapObject::kMapOffset),
1713 Immediate(isolate()->factory()->sliced_string_map()));
1714 }
1715
1716
AllocateOneByteSlicedString(Register result,Register scratch1,Register scratch2,Label * gc_required)1717 void MacroAssembler::AllocateOneByteSlicedString(Register result,
1718 Register scratch1,
1719 Register scratch2,
1720 Label* gc_required) {
1721 // Allocate heap number in new space.
1722 Allocate(SlicedString::kSize, result, scratch1, scratch2, gc_required,
1723 NO_ALLOCATION_FLAGS);
1724
1725 // Set the map. The other fields are left uninitialized.
1726 mov(FieldOperand(result, HeapObject::kMapOffset),
1727 Immediate(isolate()->factory()->sliced_one_byte_string_map()));
1728 }
1729
1730
AllocateJSValue(Register result,Register constructor,Register value,Register scratch,Label * gc_required)1731 void MacroAssembler::AllocateJSValue(Register result, Register constructor,
1732 Register value, Register scratch,
1733 Label* gc_required) {
1734 DCHECK(!result.is(constructor));
1735 DCHECK(!result.is(scratch));
1736 DCHECK(!result.is(value));
1737
1738 // Allocate JSValue in new space.
1739 Allocate(JSValue::kSize, result, scratch, no_reg, gc_required,
1740 NO_ALLOCATION_FLAGS);
1741
1742 // Initialize the JSValue.
1743 LoadGlobalFunctionInitialMap(constructor, scratch);
1744 mov(FieldOperand(result, HeapObject::kMapOffset), scratch);
1745 LoadRoot(scratch, Heap::kEmptyFixedArrayRootIndex);
1746 mov(FieldOperand(result, JSObject::kPropertiesOffset), scratch);
1747 mov(FieldOperand(result, JSObject::kElementsOffset), scratch);
1748 mov(FieldOperand(result, JSValue::kValueOffset), value);
1749 STATIC_ASSERT(JSValue::kSize == 4 * kPointerSize);
1750 }
1751
InitializeFieldsWithFiller(Register current_address,Register end_address,Register filler)1752 void MacroAssembler::InitializeFieldsWithFiller(Register current_address,
1753 Register end_address,
1754 Register filler) {
1755 Label loop, entry;
1756 jmp(&entry, Label::kNear);
1757 bind(&loop);
1758 mov(Operand(current_address, 0), filler);
1759 add(current_address, Immediate(kPointerSize));
1760 bind(&entry);
1761 cmp(current_address, end_address);
1762 j(below, &loop, Label::kNear);
1763 }
1764
1765
BooleanBitTest(Register object,int field_offset,int bit_index)1766 void MacroAssembler::BooleanBitTest(Register object,
1767 int field_offset,
1768 int bit_index) {
1769 bit_index += kSmiTagSize + kSmiShiftSize;
1770 DCHECK(base::bits::IsPowerOfTwo32(kBitsPerByte));
1771 int byte_index = bit_index / kBitsPerByte;
1772 int byte_bit_index = bit_index & (kBitsPerByte - 1);
1773 test_b(FieldOperand(object, field_offset + byte_index),
1774 Immediate(1 << byte_bit_index));
1775 }
1776
1777
1778
NegativeZeroTest(Register result,Register op,Label * then_label)1779 void MacroAssembler::NegativeZeroTest(Register result,
1780 Register op,
1781 Label* then_label) {
1782 Label ok;
1783 test(result, result);
1784 j(not_zero, &ok, Label::kNear);
1785 test(op, op);
1786 j(sign, then_label, Label::kNear);
1787 bind(&ok);
1788 }
1789
1790
NegativeZeroTest(Register result,Register op1,Register op2,Register scratch,Label * then_label)1791 void MacroAssembler::NegativeZeroTest(Register result,
1792 Register op1,
1793 Register op2,
1794 Register scratch,
1795 Label* then_label) {
1796 Label ok;
1797 test(result, result);
1798 j(not_zero, &ok, Label::kNear);
1799 mov(scratch, op1);
1800 or_(scratch, op2);
1801 j(sign, then_label, Label::kNear);
1802 bind(&ok);
1803 }
1804
1805
GetMapConstructor(Register result,Register map,Register temp)1806 void MacroAssembler::GetMapConstructor(Register result, Register map,
1807 Register temp) {
1808 Label done, loop;
1809 mov(result, FieldOperand(map, Map::kConstructorOrBackPointerOffset));
1810 bind(&loop);
1811 JumpIfSmi(result, &done, Label::kNear);
1812 CmpObjectType(result, MAP_TYPE, temp);
1813 j(not_equal, &done, Label::kNear);
1814 mov(result, FieldOperand(result, Map::kConstructorOrBackPointerOffset));
1815 jmp(&loop);
1816 bind(&done);
1817 }
1818
1819
TryGetFunctionPrototype(Register function,Register result,Register scratch,Label * miss)1820 void MacroAssembler::TryGetFunctionPrototype(Register function, Register result,
1821 Register scratch, Label* miss) {
1822 // Get the prototype or initial map from the function.
1823 mov(result,
1824 FieldOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
1825
1826 // If the prototype or initial map is the hole, don't return it and
1827 // simply miss the cache instead. This will allow us to allocate a
1828 // prototype object on-demand in the runtime system.
1829 cmp(result, Immediate(isolate()->factory()->the_hole_value()));
1830 j(equal, miss);
1831
1832 // If the function does not have an initial map, we're done.
1833 Label done;
1834 CmpObjectType(result, MAP_TYPE, scratch);
1835 j(not_equal, &done, Label::kNear);
1836
1837 // Get the prototype from the initial map.
1838 mov(result, FieldOperand(result, Map::kPrototypeOffset));
1839
1840 // All done.
1841 bind(&done);
1842 }
1843
1844
CallStub(CodeStub * stub,TypeFeedbackId ast_id)1845 void MacroAssembler::CallStub(CodeStub* stub, TypeFeedbackId ast_id) {
1846 DCHECK(AllowThisStubCall(stub)); // Calls are not allowed in some stubs.
1847 call(stub->GetCode(), RelocInfo::CODE_TARGET, ast_id);
1848 }
1849
1850
TailCallStub(CodeStub * stub)1851 void MacroAssembler::TailCallStub(CodeStub* stub) {
1852 jmp(stub->GetCode(), RelocInfo::CODE_TARGET);
1853 }
1854
1855
StubReturn(int argc)1856 void MacroAssembler::StubReturn(int argc) {
1857 DCHECK(argc >= 1 && generating_stub());
1858 ret((argc - 1) * kPointerSize);
1859 }
1860
1861
AllowThisStubCall(CodeStub * stub)1862 bool MacroAssembler::AllowThisStubCall(CodeStub* stub) {
1863 return has_frame_ || !stub->SometimesSetsUpAFrame();
1864 }
1865
CallRuntime(const Runtime::Function * f,int num_arguments,SaveFPRegsMode save_doubles)1866 void MacroAssembler::CallRuntime(const Runtime::Function* f, int num_arguments,
1867 SaveFPRegsMode save_doubles) {
1868 // If the expected number of arguments of the runtime function is
1869 // constant, we check that the actual number of arguments match the
1870 // expectation.
1871 CHECK(f->nargs < 0 || f->nargs == num_arguments);
1872
1873 // TODO(1236192): Most runtime routines don't need the number of
1874 // arguments passed in because it is constant. At some point we
1875 // should remove this need and make the runtime routine entry code
1876 // smarter.
1877 Move(eax, Immediate(num_arguments));
1878 mov(ebx, Immediate(ExternalReference(f, isolate())));
1879 CEntryStub ces(isolate(), 1, save_doubles);
1880 CallStub(&ces);
1881 }
1882
1883
CallExternalReference(ExternalReference ref,int num_arguments)1884 void MacroAssembler::CallExternalReference(ExternalReference ref,
1885 int num_arguments) {
1886 mov(eax, Immediate(num_arguments));
1887 mov(ebx, Immediate(ref));
1888
1889 CEntryStub stub(isolate(), 1);
1890 CallStub(&stub);
1891 }
1892
1893
TailCallRuntime(Runtime::FunctionId fid)1894 void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid) {
1895 // ----------- S t a t e -------------
1896 // -- esp[0] : return address
1897 // -- esp[8] : argument num_arguments - 1
1898 // ...
1899 // -- esp[8 * num_arguments] : argument 0 (receiver)
1900 //
1901 // For runtime functions with variable arguments:
1902 // -- eax : number of arguments
1903 // -----------------------------------
1904
1905 const Runtime::Function* function = Runtime::FunctionForId(fid);
1906 DCHECK_EQ(1, function->result_size);
1907 if (function->nargs >= 0) {
1908 // TODO(1236192): Most runtime routines don't need the number of
1909 // arguments passed in because it is constant. At some point we
1910 // should remove this need and make the runtime routine entry code
1911 // smarter.
1912 mov(eax, Immediate(function->nargs));
1913 }
1914 JumpToExternalReference(ExternalReference(fid, isolate()));
1915 }
1916
JumpToExternalReference(const ExternalReference & ext,bool builtin_exit_frame)1917 void MacroAssembler::JumpToExternalReference(const ExternalReference& ext,
1918 bool builtin_exit_frame) {
1919 // Set the entry point and jump to the C entry runtime stub.
1920 mov(ebx, Immediate(ext));
1921 CEntryStub ces(isolate(), 1, kDontSaveFPRegs, kArgvOnStack,
1922 builtin_exit_frame);
1923 jmp(ces.GetCode(), RelocInfo::CODE_TARGET);
1924 }
1925
PrepareForTailCall(const ParameterCount & callee_args_count,Register caller_args_count_reg,Register scratch0,Register scratch1,ReturnAddressState ra_state,int number_of_temp_values_after_return_address)1926 void MacroAssembler::PrepareForTailCall(
1927 const ParameterCount& callee_args_count, Register caller_args_count_reg,
1928 Register scratch0, Register scratch1, ReturnAddressState ra_state,
1929 int number_of_temp_values_after_return_address) {
1930 #if DEBUG
1931 if (callee_args_count.is_reg()) {
1932 DCHECK(!AreAliased(callee_args_count.reg(), caller_args_count_reg, scratch0,
1933 scratch1));
1934 } else {
1935 DCHECK(!AreAliased(caller_args_count_reg, scratch0, scratch1));
1936 }
1937 DCHECK(ra_state != ReturnAddressState::kNotOnStack ||
1938 number_of_temp_values_after_return_address == 0);
1939 #endif
1940
1941 // Calculate the destination address where we will put the return address
1942 // after we drop current frame.
1943 Register new_sp_reg = scratch0;
1944 if (callee_args_count.is_reg()) {
1945 sub(caller_args_count_reg, callee_args_count.reg());
1946 lea(new_sp_reg,
1947 Operand(ebp, caller_args_count_reg, times_pointer_size,
1948 StandardFrameConstants::kCallerPCOffset -
1949 number_of_temp_values_after_return_address * kPointerSize));
1950 } else {
1951 lea(new_sp_reg, Operand(ebp, caller_args_count_reg, times_pointer_size,
1952 StandardFrameConstants::kCallerPCOffset -
1953 (callee_args_count.immediate() +
1954 number_of_temp_values_after_return_address) *
1955 kPointerSize));
1956 }
1957
1958 if (FLAG_debug_code) {
1959 cmp(esp, new_sp_reg);
1960 Check(below, kStackAccessBelowStackPointer);
1961 }
1962
1963 // Copy return address from caller's frame to current frame's return address
1964 // to avoid its trashing and let the following loop copy it to the right
1965 // place.
1966 Register tmp_reg = scratch1;
1967 if (ra_state == ReturnAddressState::kOnStack) {
1968 mov(tmp_reg, Operand(ebp, StandardFrameConstants::kCallerPCOffset));
1969 mov(Operand(esp, number_of_temp_values_after_return_address * kPointerSize),
1970 tmp_reg);
1971 } else {
1972 DCHECK(ReturnAddressState::kNotOnStack == ra_state);
1973 DCHECK_EQ(0, number_of_temp_values_after_return_address);
1974 Push(Operand(ebp, StandardFrameConstants::kCallerPCOffset));
1975 }
1976
1977 // Restore caller's frame pointer now as it could be overwritten by
1978 // the copying loop.
1979 mov(ebp, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
1980
1981 // +2 here is to copy both receiver and return address.
1982 Register count_reg = caller_args_count_reg;
1983 if (callee_args_count.is_reg()) {
1984 lea(count_reg, Operand(callee_args_count.reg(),
1985 2 + number_of_temp_values_after_return_address));
1986 } else {
1987 mov(count_reg, Immediate(callee_args_count.immediate() + 2 +
1988 number_of_temp_values_after_return_address));
1989 // TODO(ishell): Unroll copying loop for small immediate values.
1990 }
1991
1992 // Now copy callee arguments to the caller frame going backwards to avoid
1993 // callee arguments corruption (source and destination areas could overlap).
1994 Label loop, entry;
1995 jmp(&entry, Label::kNear);
1996 bind(&loop);
1997 dec(count_reg);
1998 mov(tmp_reg, Operand(esp, count_reg, times_pointer_size, 0));
1999 mov(Operand(new_sp_reg, count_reg, times_pointer_size, 0), tmp_reg);
2000 bind(&entry);
2001 cmp(count_reg, Immediate(0));
2002 j(not_equal, &loop, Label::kNear);
2003
2004 // Leave current frame.
2005 mov(esp, new_sp_reg);
2006 }
2007
InvokePrologue(const ParameterCount & expected,const ParameterCount & actual,Label * done,bool * definitely_mismatches,InvokeFlag flag,Label::Distance done_near,const CallWrapper & call_wrapper)2008 void MacroAssembler::InvokePrologue(const ParameterCount& expected,
2009 const ParameterCount& actual,
2010 Label* done,
2011 bool* definitely_mismatches,
2012 InvokeFlag flag,
2013 Label::Distance done_near,
2014 const CallWrapper& call_wrapper) {
2015 bool definitely_matches = false;
2016 *definitely_mismatches = false;
2017 Label invoke;
2018 if (expected.is_immediate()) {
2019 DCHECK(actual.is_immediate());
2020 mov(eax, actual.immediate());
2021 if (expected.immediate() == actual.immediate()) {
2022 definitely_matches = true;
2023 } else {
2024 const int sentinel = SharedFunctionInfo::kDontAdaptArgumentsSentinel;
2025 if (expected.immediate() == sentinel) {
2026 // Don't worry about adapting arguments for builtins that
2027 // don't want that done. Skip adaption code by making it look
2028 // like we have a match between expected and actual number of
2029 // arguments.
2030 definitely_matches = true;
2031 } else {
2032 *definitely_mismatches = true;
2033 mov(ebx, expected.immediate());
2034 }
2035 }
2036 } else {
2037 if (actual.is_immediate()) {
2038 // Expected is in register, actual is immediate. This is the
2039 // case when we invoke function values without going through the
2040 // IC mechanism.
2041 mov(eax, actual.immediate());
2042 cmp(expected.reg(), actual.immediate());
2043 j(equal, &invoke);
2044 DCHECK(expected.reg().is(ebx));
2045 } else if (!expected.reg().is(actual.reg())) {
2046 // Both expected and actual are in (different) registers. This
2047 // is the case when we invoke functions using call and apply.
2048 cmp(expected.reg(), actual.reg());
2049 j(equal, &invoke);
2050 DCHECK(actual.reg().is(eax));
2051 DCHECK(expected.reg().is(ebx));
2052 } else {
2053 Move(eax, actual.reg());
2054 }
2055 }
2056
2057 if (!definitely_matches) {
2058 Handle<Code> adaptor =
2059 isolate()->builtins()->ArgumentsAdaptorTrampoline();
2060 if (flag == CALL_FUNCTION) {
2061 call_wrapper.BeforeCall(CallSize(adaptor, RelocInfo::CODE_TARGET));
2062 call(adaptor, RelocInfo::CODE_TARGET);
2063 call_wrapper.AfterCall();
2064 if (!*definitely_mismatches) {
2065 jmp(done, done_near);
2066 }
2067 } else {
2068 jmp(adaptor, RelocInfo::CODE_TARGET);
2069 }
2070 bind(&invoke);
2071 }
2072 }
2073
2074
FloodFunctionIfStepping(Register fun,Register new_target,const ParameterCount & expected,const ParameterCount & actual)2075 void MacroAssembler::FloodFunctionIfStepping(Register fun, Register new_target,
2076 const ParameterCount& expected,
2077 const ParameterCount& actual) {
2078 Label skip_flooding;
2079 ExternalReference last_step_action =
2080 ExternalReference::debug_last_step_action_address(isolate());
2081 STATIC_ASSERT(StepFrame > StepIn);
2082 cmpb(Operand::StaticVariable(last_step_action), Immediate(StepIn));
2083 j(less, &skip_flooding);
2084 {
2085 FrameScope frame(this,
2086 has_frame() ? StackFrame::NONE : StackFrame::INTERNAL);
2087 if (expected.is_reg()) {
2088 SmiTag(expected.reg());
2089 Push(expected.reg());
2090 }
2091 if (actual.is_reg()) {
2092 SmiTag(actual.reg());
2093 Push(actual.reg());
2094 }
2095 if (new_target.is_valid()) {
2096 Push(new_target);
2097 }
2098 Push(fun);
2099 Push(fun);
2100 CallRuntime(Runtime::kDebugPrepareStepInIfStepping);
2101 Pop(fun);
2102 if (new_target.is_valid()) {
2103 Pop(new_target);
2104 }
2105 if (actual.is_reg()) {
2106 Pop(actual.reg());
2107 SmiUntag(actual.reg());
2108 }
2109 if (expected.is_reg()) {
2110 Pop(expected.reg());
2111 SmiUntag(expected.reg());
2112 }
2113 }
2114 bind(&skip_flooding);
2115 }
2116
2117
InvokeFunctionCode(Register function,Register new_target,const ParameterCount & expected,const ParameterCount & actual,InvokeFlag flag,const CallWrapper & call_wrapper)2118 void MacroAssembler::InvokeFunctionCode(Register function, Register new_target,
2119 const ParameterCount& expected,
2120 const ParameterCount& actual,
2121 InvokeFlag flag,
2122 const CallWrapper& call_wrapper) {
2123 // You can't call a function without a valid frame.
2124 DCHECK(flag == JUMP_FUNCTION || has_frame());
2125 DCHECK(function.is(edi));
2126 DCHECK_IMPLIES(new_target.is_valid(), new_target.is(edx));
2127
2128 if (call_wrapper.NeedsDebugStepCheck()) {
2129 FloodFunctionIfStepping(function, new_target, expected, actual);
2130 }
2131
2132 // Clear the new.target register if not given.
2133 if (!new_target.is_valid()) {
2134 mov(edx, isolate()->factory()->undefined_value());
2135 }
2136
2137 Label done;
2138 bool definitely_mismatches = false;
2139 InvokePrologue(expected, actual, &done, &definitely_mismatches, flag,
2140 Label::kNear, call_wrapper);
2141 if (!definitely_mismatches) {
2142 // We call indirectly through the code field in the function to
2143 // allow recompilation to take effect without changing any of the
2144 // call sites.
2145 Operand code = FieldOperand(function, JSFunction::kCodeEntryOffset);
2146 if (flag == CALL_FUNCTION) {
2147 call_wrapper.BeforeCall(CallSize(code));
2148 call(code);
2149 call_wrapper.AfterCall();
2150 } else {
2151 DCHECK(flag == JUMP_FUNCTION);
2152 jmp(code);
2153 }
2154 bind(&done);
2155 }
2156 }
2157
2158
InvokeFunction(Register fun,Register new_target,const ParameterCount & actual,InvokeFlag flag,const CallWrapper & call_wrapper)2159 void MacroAssembler::InvokeFunction(Register fun, Register new_target,
2160 const ParameterCount& actual,
2161 InvokeFlag flag,
2162 const CallWrapper& call_wrapper) {
2163 // You can't call a function without a valid frame.
2164 DCHECK(flag == JUMP_FUNCTION || has_frame());
2165
2166 DCHECK(fun.is(edi));
2167 mov(ebx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
2168 mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
2169 mov(ebx, FieldOperand(ebx, SharedFunctionInfo::kFormalParameterCountOffset));
2170 SmiUntag(ebx);
2171
2172 ParameterCount expected(ebx);
2173 InvokeFunctionCode(edi, new_target, expected, actual, flag, call_wrapper);
2174 }
2175
2176
InvokeFunction(Register fun,const ParameterCount & expected,const ParameterCount & actual,InvokeFlag flag,const CallWrapper & call_wrapper)2177 void MacroAssembler::InvokeFunction(Register fun,
2178 const ParameterCount& expected,
2179 const ParameterCount& actual,
2180 InvokeFlag flag,
2181 const CallWrapper& call_wrapper) {
2182 // You can't call a function without a valid frame.
2183 DCHECK(flag == JUMP_FUNCTION || has_frame());
2184
2185 DCHECK(fun.is(edi));
2186 mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
2187
2188 InvokeFunctionCode(edi, no_reg, expected, actual, flag, call_wrapper);
2189 }
2190
2191
InvokeFunction(Handle<JSFunction> function,const ParameterCount & expected,const ParameterCount & actual,InvokeFlag flag,const CallWrapper & call_wrapper)2192 void MacroAssembler::InvokeFunction(Handle<JSFunction> function,
2193 const ParameterCount& expected,
2194 const ParameterCount& actual,
2195 InvokeFlag flag,
2196 const CallWrapper& call_wrapper) {
2197 LoadHeapObject(edi, function);
2198 InvokeFunction(edi, expected, actual, flag, call_wrapper);
2199 }
2200
2201
LoadContext(Register dst,int context_chain_length)2202 void MacroAssembler::LoadContext(Register dst, int context_chain_length) {
2203 if (context_chain_length > 0) {
2204 // Move up the chain of contexts to the context containing the slot.
2205 mov(dst, Operand(esi, Context::SlotOffset(Context::PREVIOUS_INDEX)));
2206 for (int i = 1; i < context_chain_length; i++) {
2207 mov(dst, Operand(dst, Context::SlotOffset(Context::PREVIOUS_INDEX)));
2208 }
2209 } else {
2210 // Slot is in the current function context. Move it into the
2211 // destination register in case we store into it (the write barrier
2212 // cannot be allowed to destroy the context in esi).
2213 mov(dst, esi);
2214 }
2215
2216 // We should not have found a with context by walking the context chain
2217 // (i.e., the static scope chain and runtime context chain do not agree).
2218 // A variable occurring in such a scope should have slot type LOOKUP and
2219 // not CONTEXT.
2220 if (emit_debug_code()) {
2221 cmp(FieldOperand(dst, HeapObject::kMapOffset),
2222 isolate()->factory()->with_context_map());
2223 Check(not_equal, kVariableResolvedToWithContext);
2224 }
2225 }
2226
2227
LoadGlobalProxy(Register dst)2228 void MacroAssembler::LoadGlobalProxy(Register dst) {
2229 mov(dst, NativeContextOperand());
2230 mov(dst, ContextOperand(dst, Context::GLOBAL_PROXY_INDEX));
2231 }
2232
2233
LoadTransitionedArrayMapConditional(ElementsKind expected_kind,ElementsKind transitioned_kind,Register map_in_out,Register scratch,Label * no_map_match)2234 void MacroAssembler::LoadTransitionedArrayMapConditional(
2235 ElementsKind expected_kind,
2236 ElementsKind transitioned_kind,
2237 Register map_in_out,
2238 Register scratch,
2239 Label* no_map_match) {
2240 DCHECK(IsFastElementsKind(expected_kind));
2241 DCHECK(IsFastElementsKind(transitioned_kind));
2242
2243 // Check that the function's map is the same as the expected cached map.
2244 mov(scratch, NativeContextOperand());
2245 cmp(map_in_out,
2246 ContextOperand(scratch, Context::ArrayMapIndex(expected_kind)));
2247 j(not_equal, no_map_match);
2248
2249 // Use the transitioned cached map.
2250 mov(map_in_out,
2251 ContextOperand(scratch, Context::ArrayMapIndex(transitioned_kind)));
2252 }
2253
2254
LoadGlobalFunction(int index,Register function)2255 void MacroAssembler::LoadGlobalFunction(int index, Register function) {
2256 // Load the native context from the current context.
2257 mov(function, NativeContextOperand());
2258 // Load the function from the native context.
2259 mov(function, ContextOperand(function, index));
2260 }
2261
2262
LoadGlobalFunctionInitialMap(Register function,Register map)2263 void MacroAssembler::LoadGlobalFunctionInitialMap(Register function,
2264 Register map) {
2265 // Load the initial map. The global functions all have initial maps.
2266 mov(map, FieldOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
2267 if (emit_debug_code()) {
2268 Label ok, fail;
2269 CheckMap(map, isolate()->factory()->meta_map(), &fail, DO_SMI_CHECK);
2270 jmp(&ok);
2271 bind(&fail);
2272 Abort(kGlobalFunctionsMustHaveInitialMap);
2273 bind(&ok);
2274 }
2275 }
2276
2277
2278 // Store the value in register src in the safepoint register stack
2279 // slot for register dst.
StoreToSafepointRegisterSlot(Register dst,Register src)2280 void MacroAssembler::StoreToSafepointRegisterSlot(Register dst, Register src) {
2281 mov(SafepointRegisterSlot(dst), src);
2282 }
2283
2284
StoreToSafepointRegisterSlot(Register dst,Immediate src)2285 void MacroAssembler::StoreToSafepointRegisterSlot(Register dst, Immediate src) {
2286 mov(SafepointRegisterSlot(dst), src);
2287 }
2288
2289
LoadFromSafepointRegisterSlot(Register dst,Register src)2290 void MacroAssembler::LoadFromSafepointRegisterSlot(Register dst, Register src) {
2291 mov(dst, SafepointRegisterSlot(src));
2292 }
2293
2294
SafepointRegisterSlot(Register reg)2295 Operand MacroAssembler::SafepointRegisterSlot(Register reg) {
2296 return Operand(esp, SafepointRegisterStackIndex(reg.code()) * kPointerSize);
2297 }
2298
2299
SafepointRegisterStackIndex(int reg_code)2300 int MacroAssembler::SafepointRegisterStackIndex(int reg_code) {
2301 // The registers are pushed starting with the lowest encoding,
2302 // which means that lowest encodings are furthest away from
2303 // the stack pointer.
2304 DCHECK(reg_code >= 0 && reg_code < kNumSafepointRegisters);
2305 return kNumSafepointRegisters - reg_code - 1;
2306 }
2307
2308
LoadHeapObject(Register result,Handle<HeapObject> object)2309 void MacroAssembler::LoadHeapObject(Register result,
2310 Handle<HeapObject> object) {
2311 mov(result, object);
2312 }
2313
2314
CmpHeapObject(Register reg,Handle<HeapObject> object)2315 void MacroAssembler::CmpHeapObject(Register reg, Handle<HeapObject> object) {
2316 cmp(reg, object);
2317 }
2318
PushHeapObject(Handle<HeapObject> object)2319 void MacroAssembler::PushHeapObject(Handle<HeapObject> object) { Push(object); }
2320
CmpWeakValue(Register value,Handle<WeakCell> cell,Register scratch)2321 void MacroAssembler::CmpWeakValue(Register value, Handle<WeakCell> cell,
2322 Register scratch) {
2323 mov(scratch, cell);
2324 cmp(value, FieldOperand(scratch, WeakCell::kValueOffset));
2325 }
2326
2327
GetWeakValue(Register value,Handle<WeakCell> cell)2328 void MacroAssembler::GetWeakValue(Register value, Handle<WeakCell> cell) {
2329 mov(value, cell);
2330 mov(value, FieldOperand(value, WeakCell::kValueOffset));
2331 }
2332
2333
LoadWeakValue(Register value,Handle<WeakCell> cell,Label * miss)2334 void MacroAssembler::LoadWeakValue(Register value, Handle<WeakCell> cell,
2335 Label* miss) {
2336 GetWeakValue(value, cell);
2337 JumpIfSmi(value, miss);
2338 }
2339
2340
Ret()2341 void MacroAssembler::Ret() {
2342 ret(0);
2343 }
2344
2345
Ret(int bytes_dropped,Register scratch)2346 void MacroAssembler::Ret(int bytes_dropped, Register scratch) {
2347 if (is_uint16(bytes_dropped)) {
2348 ret(bytes_dropped);
2349 } else {
2350 pop(scratch);
2351 add(esp, Immediate(bytes_dropped));
2352 push(scratch);
2353 ret(0);
2354 }
2355 }
2356
2357
VerifyX87StackDepth(uint32_t depth)2358 void MacroAssembler::VerifyX87StackDepth(uint32_t depth) {
2359 // Turn off the stack depth check when serializer is enabled to reduce the
2360 // code size.
2361 if (serializer_enabled()) return;
2362 // Make sure the floating point stack is either empty or has depth items.
2363 DCHECK(depth <= 7);
2364 // This is very expensive.
2365 DCHECK(FLAG_debug_code && FLAG_enable_slow_asserts);
2366
2367 // The top-of-stack (tos) is 7 if there is one item pushed.
2368 int tos = (8 - depth) % 8;
2369 const int kTopMask = 0x3800;
2370 push(eax);
2371 fwait();
2372 fnstsw_ax();
2373 and_(eax, kTopMask);
2374 shr(eax, 11);
2375 cmp(eax, Immediate(tos));
2376 Check(equal, kUnexpectedFPUStackDepthAfterInstruction);
2377 fnclex();
2378 pop(eax);
2379 }
2380
2381
Drop(int stack_elements)2382 void MacroAssembler::Drop(int stack_elements) {
2383 if (stack_elements > 0) {
2384 add(esp, Immediate(stack_elements * kPointerSize));
2385 }
2386 }
2387
2388
Move(Register dst,Register src)2389 void MacroAssembler::Move(Register dst, Register src) {
2390 if (!dst.is(src)) {
2391 mov(dst, src);
2392 }
2393 }
2394
2395
Move(Register dst,const Immediate & x)2396 void MacroAssembler::Move(Register dst, const Immediate& x) {
2397 if (x.is_zero() && RelocInfo::IsNone(x.rmode_)) {
2398 xor_(dst, dst); // Shorter than mov of 32-bit immediate 0.
2399 } else {
2400 mov(dst, x);
2401 }
2402 }
2403
2404
Move(const Operand & dst,const Immediate & x)2405 void MacroAssembler::Move(const Operand& dst, const Immediate& x) {
2406 mov(dst, x);
2407 }
2408
2409
Lzcnt(Register dst,const Operand & src)2410 void MacroAssembler::Lzcnt(Register dst, const Operand& src) {
2411 // TODO(intel): Add support for LZCNT (with ABM/BMI1).
2412 Label not_zero_src;
2413 bsr(dst, src);
2414 j(not_zero, ¬_zero_src, Label::kNear);
2415 Move(dst, Immediate(63)); // 63^31 == 32
2416 bind(¬_zero_src);
2417 xor_(dst, Immediate(31)); // for x in [0..31], 31^x == 31-x.
2418 }
2419
2420
Tzcnt(Register dst,const Operand & src)2421 void MacroAssembler::Tzcnt(Register dst, const Operand& src) {
2422 // TODO(intel): Add support for TZCNT (with ABM/BMI1).
2423 Label not_zero_src;
2424 bsf(dst, src);
2425 j(not_zero, ¬_zero_src, Label::kNear);
2426 Move(dst, Immediate(32)); // The result of tzcnt is 32 if src = 0.
2427 bind(¬_zero_src);
2428 }
2429
2430
Popcnt(Register dst,const Operand & src)2431 void MacroAssembler::Popcnt(Register dst, const Operand& src) {
2432 // TODO(intel): Add support for POPCNT (with POPCNT)
2433 // if (CpuFeatures::IsSupported(POPCNT)) {
2434 // CpuFeatureScope scope(this, POPCNT);
2435 // popcnt(dst, src);
2436 // return;
2437 // }
2438 UNREACHABLE();
2439 }
2440
2441
SetCounter(StatsCounter * counter,int value)2442 void MacroAssembler::SetCounter(StatsCounter* counter, int value) {
2443 if (FLAG_native_code_counters && counter->Enabled()) {
2444 mov(Operand::StaticVariable(ExternalReference(counter)), Immediate(value));
2445 }
2446 }
2447
2448
IncrementCounter(StatsCounter * counter,int value)2449 void MacroAssembler::IncrementCounter(StatsCounter* counter, int value) {
2450 DCHECK(value > 0);
2451 if (FLAG_native_code_counters && counter->Enabled()) {
2452 Operand operand = Operand::StaticVariable(ExternalReference(counter));
2453 if (value == 1) {
2454 inc(operand);
2455 } else {
2456 add(operand, Immediate(value));
2457 }
2458 }
2459 }
2460
2461
DecrementCounter(StatsCounter * counter,int value)2462 void MacroAssembler::DecrementCounter(StatsCounter* counter, int value) {
2463 DCHECK(value > 0);
2464 if (FLAG_native_code_counters && counter->Enabled()) {
2465 Operand operand = Operand::StaticVariable(ExternalReference(counter));
2466 if (value == 1) {
2467 dec(operand);
2468 } else {
2469 sub(operand, Immediate(value));
2470 }
2471 }
2472 }
2473
2474
IncrementCounter(Condition cc,StatsCounter * counter,int value)2475 void MacroAssembler::IncrementCounter(Condition cc,
2476 StatsCounter* counter,
2477 int value) {
2478 DCHECK(value > 0);
2479 if (FLAG_native_code_counters && counter->Enabled()) {
2480 Label skip;
2481 j(NegateCondition(cc), &skip);
2482 pushfd();
2483 IncrementCounter(counter, value);
2484 popfd();
2485 bind(&skip);
2486 }
2487 }
2488
2489
DecrementCounter(Condition cc,StatsCounter * counter,int value)2490 void MacroAssembler::DecrementCounter(Condition cc,
2491 StatsCounter* counter,
2492 int value) {
2493 DCHECK(value > 0);
2494 if (FLAG_native_code_counters && counter->Enabled()) {
2495 Label skip;
2496 j(NegateCondition(cc), &skip);
2497 pushfd();
2498 DecrementCounter(counter, value);
2499 popfd();
2500 bind(&skip);
2501 }
2502 }
2503
2504
Assert(Condition cc,BailoutReason reason)2505 void MacroAssembler::Assert(Condition cc, BailoutReason reason) {
2506 if (emit_debug_code()) Check(cc, reason);
2507 }
2508
2509
AssertFastElements(Register elements)2510 void MacroAssembler::AssertFastElements(Register elements) {
2511 if (emit_debug_code()) {
2512 Factory* factory = isolate()->factory();
2513 Label ok;
2514 cmp(FieldOperand(elements, HeapObject::kMapOffset),
2515 Immediate(factory->fixed_array_map()));
2516 j(equal, &ok);
2517 cmp(FieldOperand(elements, HeapObject::kMapOffset),
2518 Immediate(factory->fixed_double_array_map()));
2519 j(equal, &ok);
2520 cmp(FieldOperand(elements, HeapObject::kMapOffset),
2521 Immediate(factory->fixed_cow_array_map()));
2522 j(equal, &ok);
2523 Abort(kJSObjectWithFastElementsMapHasSlowElements);
2524 bind(&ok);
2525 }
2526 }
2527
2528
Check(Condition cc,BailoutReason reason)2529 void MacroAssembler::Check(Condition cc, BailoutReason reason) {
2530 Label L;
2531 j(cc, &L);
2532 Abort(reason);
2533 // will not return here
2534 bind(&L);
2535 }
2536
2537
CheckStackAlignment()2538 void MacroAssembler::CheckStackAlignment() {
2539 int frame_alignment = base::OS::ActivationFrameAlignment();
2540 int frame_alignment_mask = frame_alignment - 1;
2541 if (frame_alignment > kPointerSize) {
2542 DCHECK(base::bits::IsPowerOfTwo32(frame_alignment));
2543 Label alignment_as_expected;
2544 test(esp, Immediate(frame_alignment_mask));
2545 j(zero, &alignment_as_expected);
2546 // Abort if stack is not aligned.
2547 int3();
2548 bind(&alignment_as_expected);
2549 }
2550 }
2551
2552
Abort(BailoutReason reason)2553 void MacroAssembler::Abort(BailoutReason reason) {
2554 #ifdef DEBUG
2555 const char* msg = GetBailoutReason(reason);
2556 if (msg != NULL) {
2557 RecordComment("Abort message: ");
2558 RecordComment(msg);
2559 }
2560
2561 if (FLAG_trap_on_abort) {
2562 int3();
2563 return;
2564 }
2565 #endif
2566
2567 // Check if Abort() has already been initialized.
2568 DCHECK(isolate()->builtins()->Abort()->IsHeapObject());
2569
2570 Move(edx, Smi::FromInt(static_cast<int>(reason)));
2571
2572 // Disable stub call restrictions to always allow calls to abort.
2573 if (!has_frame_) {
2574 // We don't actually want to generate a pile of code for this, so just
2575 // claim there is a stack frame, without generating one.
2576 FrameScope scope(this, StackFrame::NONE);
2577 Call(isolate()->builtins()->Abort(), RelocInfo::CODE_TARGET);
2578 } else {
2579 Call(isolate()->builtins()->Abort(), RelocInfo::CODE_TARGET);
2580 }
2581 // will not return here
2582 int3();
2583 }
2584
2585
LoadInstanceDescriptors(Register map,Register descriptors)2586 void MacroAssembler::LoadInstanceDescriptors(Register map,
2587 Register descriptors) {
2588 mov(descriptors, FieldOperand(map, Map::kDescriptorsOffset));
2589 }
2590
2591
NumberOfOwnDescriptors(Register dst,Register map)2592 void MacroAssembler::NumberOfOwnDescriptors(Register dst, Register map) {
2593 mov(dst, FieldOperand(map, Map::kBitField3Offset));
2594 DecodeField<Map::NumberOfOwnDescriptorsBits>(dst);
2595 }
2596
2597
LoadAccessor(Register dst,Register holder,int accessor_index,AccessorComponent accessor)2598 void MacroAssembler::LoadAccessor(Register dst, Register holder,
2599 int accessor_index,
2600 AccessorComponent accessor) {
2601 mov(dst, FieldOperand(holder, HeapObject::kMapOffset));
2602 LoadInstanceDescriptors(dst, dst);
2603 mov(dst, FieldOperand(dst, DescriptorArray::GetValueOffset(accessor_index)));
2604 int offset = accessor == ACCESSOR_GETTER ? AccessorPair::kGetterOffset
2605 : AccessorPair::kSetterOffset;
2606 mov(dst, FieldOperand(dst, offset));
2607 }
2608
2609
JumpIfInstanceTypeIsNotSequentialOneByte(Register instance_type,Register scratch,Label * failure)2610 void MacroAssembler::JumpIfInstanceTypeIsNotSequentialOneByte(
2611 Register instance_type, Register scratch, Label* failure) {
2612 if (!scratch.is(instance_type)) {
2613 mov(scratch, instance_type);
2614 }
2615 and_(scratch,
2616 kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask);
2617 cmp(scratch, kStringTag | kSeqStringTag | kOneByteStringTag);
2618 j(not_equal, failure);
2619 }
2620
2621
JumpIfNotBothSequentialOneByteStrings(Register object1,Register object2,Register scratch1,Register scratch2,Label * failure)2622 void MacroAssembler::JumpIfNotBothSequentialOneByteStrings(Register object1,
2623 Register object2,
2624 Register scratch1,
2625 Register scratch2,
2626 Label* failure) {
2627 // Check that both objects are not smis.
2628 STATIC_ASSERT(kSmiTag == 0);
2629 mov(scratch1, object1);
2630 and_(scratch1, object2);
2631 JumpIfSmi(scratch1, failure);
2632
2633 // Load instance type for both strings.
2634 mov(scratch1, FieldOperand(object1, HeapObject::kMapOffset));
2635 mov(scratch2, FieldOperand(object2, HeapObject::kMapOffset));
2636 movzx_b(scratch1, FieldOperand(scratch1, Map::kInstanceTypeOffset));
2637 movzx_b(scratch2, FieldOperand(scratch2, Map::kInstanceTypeOffset));
2638
2639 // Check that both are flat one-byte strings.
2640 const int kFlatOneByteStringMask =
2641 kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask;
2642 const int kFlatOneByteStringTag =
2643 kStringTag | kOneByteStringTag | kSeqStringTag;
2644 // Interleave bits from both instance types and compare them in one check.
2645 DCHECK_EQ(0, kFlatOneByteStringMask & (kFlatOneByteStringMask << 3));
2646 and_(scratch1, kFlatOneByteStringMask);
2647 and_(scratch2, kFlatOneByteStringMask);
2648 lea(scratch1, Operand(scratch1, scratch2, times_8, 0));
2649 cmp(scratch1, kFlatOneByteStringTag | (kFlatOneByteStringTag << 3));
2650 j(not_equal, failure);
2651 }
2652
2653
JumpIfNotUniqueNameInstanceType(Operand operand,Label * not_unique_name,Label::Distance distance)2654 void MacroAssembler::JumpIfNotUniqueNameInstanceType(Operand operand,
2655 Label* not_unique_name,
2656 Label::Distance distance) {
2657 STATIC_ASSERT(kInternalizedTag == 0 && kStringTag == 0);
2658 Label succeed;
2659 test(operand, Immediate(kIsNotStringMask | kIsNotInternalizedMask));
2660 j(zero, &succeed);
2661 cmpb(operand, Immediate(SYMBOL_TYPE));
2662 j(not_equal, not_unique_name, distance);
2663
2664 bind(&succeed);
2665 }
2666
2667
EmitSeqStringSetCharCheck(Register string,Register index,Register value,uint32_t encoding_mask)2668 void MacroAssembler::EmitSeqStringSetCharCheck(Register string,
2669 Register index,
2670 Register value,
2671 uint32_t encoding_mask) {
2672 Label is_object;
2673 JumpIfNotSmi(string, &is_object, Label::kNear);
2674 Abort(kNonObject);
2675 bind(&is_object);
2676
2677 push(value);
2678 mov(value, FieldOperand(string, HeapObject::kMapOffset));
2679 movzx_b(value, FieldOperand(value, Map::kInstanceTypeOffset));
2680
2681 and_(value, Immediate(kStringRepresentationMask | kStringEncodingMask));
2682 cmp(value, Immediate(encoding_mask));
2683 pop(value);
2684 Check(equal, kUnexpectedStringType);
2685
2686 // The index is assumed to be untagged coming in, tag it to compare with the
2687 // string length without using a temp register, it is restored at the end of
2688 // this function.
2689 SmiTag(index);
2690 Check(no_overflow, kIndexIsTooLarge);
2691
2692 cmp(index, FieldOperand(string, String::kLengthOffset));
2693 Check(less, kIndexIsTooLarge);
2694
2695 cmp(index, Immediate(Smi::kZero));
2696 Check(greater_equal, kIndexIsNegative);
2697
2698 // Restore the index
2699 SmiUntag(index);
2700 }
2701
2702
PrepareCallCFunction(int num_arguments,Register scratch)2703 void MacroAssembler::PrepareCallCFunction(int num_arguments, Register scratch) {
2704 int frame_alignment = base::OS::ActivationFrameAlignment();
2705 if (frame_alignment != 0) {
2706 // Make stack end at alignment and make room for num_arguments words
2707 // and the original value of esp.
2708 mov(scratch, esp);
2709 sub(esp, Immediate((num_arguments + 1) * kPointerSize));
2710 DCHECK(base::bits::IsPowerOfTwo32(frame_alignment));
2711 and_(esp, -frame_alignment);
2712 mov(Operand(esp, num_arguments * kPointerSize), scratch);
2713 } else {
2714 sub(esp, Immediate(num_arguments * kPointerSize));
2715 }
2716 }
2717
2718
CallCFunction(ExternalReference function,int num_arguments)2719 void MacroAssembler::CallCFunction(ExternalReference function,
2720 int num_arguments) {
2721 // Trashing eax is ok as it will be the return value.
2722 mov(eax, Immediate(function));
2723 CallCFunction(eax, num_arguments);
2724 }
2725
2726
CallCFunction(Register function,int num_arguments)2727 void MacroAssembler::CallCFunction(Register function,
2728 int num_arguments) {
2729 DCHECK(has_frame());
2730 // Check stack alignment.
2731 if (emit_debug_code()) {
2732 CheckStackAlignment();
2733 }
2734
2735 call(function);
2736 if (base::OS::ActivationFrameAlignment() != 0) {
2737 mov(esp, Operand(esp, num_arguments * kPointerSize));
2738 } else {
2739 add(esp, Immediate(num_arguments * kPointerSize));
2740 }
2741 }
2742
2743
2744 #ifdef DEBUG
AreAliased(Register reg1,Register reg2,Register reg3,Register reg4,Register reg5,Register reg6,Register reg7,Register reg8)2745 bool AreAliased(Register reg1,
2746 Register reg2,
2747 Register reg3,
2748 Register reg4,
2749 Register reg5,
2750 Register reg6,
2751 Register reg7,
2752 Register reg8) {
2753 int n_of_valid_regs = reg1.is_valid() + reg2.is_valid() +
2754 reg3.is_valid() + reg4.is_valid() + reg5.is_valid() + reg6.is_valid() +
2755 reg7.is_valid() + reg8.is_valid();
2756
2757 RegList regs = 0;
2758 if (reg1.is_valid()) regs |= reg1.bit();
2759 if (reg2.is_valid()) regs |= reg2.bit();
2760 if (reg3.is_valid()) regs |= reg3.bit();
2761 if (reg4.is_valid()) regs |= reg4.bit();
2762 if (reg5.is_valid()) regs |= reg5.bit();
2763 if (reg6.is_valid()) regs |= reg6.bit();
2764 if (reg7.is_valid()) regs |= reg7.bit();
2765 if (reg8.is_valid()) regs |= reg8.bit();
2766 int n_of_non_aliasing_regs = NumRegs(regs);
2767
2768 return n_of_valid_regs != n_of_non_aliasing_regs;
2769 }
2770 #endif
2771
2772
CodePatcher(Isolate * isolate,byte * address,int size)2773 CodePatcher::CodePatcher(Isolate* isolate, byte* address, int size)
2774 : address_(address),
2775 size_(size),
2776 masm_(isolate, address, size + Assembler::kGap, CodeObjectRequired::kNo) {
2777 // Create a new macro assembler pointing to the address of the code to patch.
2778 // The size is adjusted with kGap on order for the assembler to generate size
2779 // bytes of instructions without failing with buffer size constraints.
2780 DCHECK(masm_.reloc_info_writer.pos() == address_ + size_ + Assembler::kGap);
2781 }
2782
2783
~CodePatcher()2784 CodePatcher::~CodePatcher() {
2785 // Indicate that code has changed.
2786 Assembler::FlushICache(masm_.isolate(), address_, size_);
2787
2788 // Check that the code was patched as expected.
2789 DCHECK(masm_.pc_ == address_ + size_);
2790 DCHECK(masm_.reloc_info_writer.pos() == address_ + size_ + Assembler::kGap);
2791 }
2792
2793
CheckPageFlag(Register object,Register scratch,int mask,Condition cc,Label * condition_met,Label::Distance condition_met_distance)2794 void MacroAssembler::CheckPageFlag(
2795 Register object,
2796 Register scratch,
2797 int mask,
2798 Condition cc,
2799 Label* condition_met,
2800 Label::Distance condition_met_distance) {
2801 DCHECK(cc == zero || cc == not_zero);
2802 if (scratch.is(object)) {
2803 and_(scratch, Immediate(~Page::kPageAlignmentMask));
2804 } else {
2805 mov(scratch, Immediate(~Page::kPageAlignmentMask));
2806 and_(scratch, object);
2807 }
2808 if (mask < (1 << kBitsPerByte)) {
2809 test_b(Operand(scratch, MemoryChunk::kFlagsOffset), Immediate(mask));
2810 } else {
2811 test(Operand(scratch, MemoryChunk::kFlagsOffset), Immediate(mask));
2812 }
2813 j(cc, condition_met, condition_met_distance);
2814 }
2815
2816
CheckPageFlagForMap(Handle<Map> map,int mask,Condition cc,Label * condition_met,Label::Distance condition_met_distance)2817 void MacroAssembler::CheckPageFlagForMap(
2818 Handle<Map> map,
2819 int mask,
2820 Condition cc,
2821 Label* condition_met,
2822 Label::Distance condition_met_distance) {
2823 DCHECK(cc == zero || cc == not_zero);
2824 Page* page = Page::FromAddress(map->address());
2825 DCHECK(!serializer_enabled()); // Serializer cannot match page_flags.
2826 ExternalReference reference(ExternalReference::page_flags(page));
2827 // The inlined static address check of the page's flags relies
2828 // on maps never being compacted.
2829 DCHECK(!isolate()->heap()->mark_compact_collector()->
2830 IsOnEvacuationCandidate(*map));
2831 if (mask < (1 << kBitsPerByte)) {
2832 test_b(Operand::StaticVariable(reference), Immediate(mask));
2833 } else {
2834 test(Operand::StaticVariable(reference), Immediate(mask));
2835 }
2836 j(cc, condition_met, condition_met_distance);
2837 }
2838
2839
JumpIfBlack(Register object,Register scratch0,Register scratch1,Label * on_black,Label::Distance on_black_near)2840 void MacroAssembler::JumpIfBlack(Register object,
2841 Register scratch0,
2842 Register scratch1,
2843 Label* on_black,
2844 Label::Distance on_black_near) {
2845 HasColor(object, scratch0, scratch1, on_black, on_black_near, 1,
2846 1); // kBlackBitPattern.
2847 DCHECK(strcmp(Marking::kBlackBitPattern, "11") == 0);
2848 }
2849
2850
HasColor(Register object,Register bitmap_scratch,Register mask_scratch,Label * has_color,Label::Distance has_color_distance,int first_bit,int second_bit)2851 void MacroAssembler::HasColor(Register object,
2852 Register bitmap_scratch,
2853 Register mask_scratch,
2854 Label* has_color,
2855 Label::Distance has_color_distance,
2856 int first_bit,
2857 int second_bit) {
2858 DCHECK(!AreAliased(object, bitmap_scratch, mask_scratch, ecx));
2859
2860 GetMarkBits(object, bitmap_scratch, mask_scratch);
2861
2862 Label other_color, word_boundary;
2863 test(mask_scratch, Operand(bitmap_scratch, MemoryChunk::kHeaderSize));
2864 j(first_bit == 1 ? zero : not_zero, &other_color, Label::kNear);
2865 add(mask_scratch, mask_scratch); // Shift left 1 by adding.
2866 j(zero, &word_boundary, Label::kNear);
2867 test(mask_scratch, Operand(bitmap_scratch, MemoryChunk::kHeaderSize));
2868 j(second_bit == 1 ? not_zero : zero, has_color, has_color_distance);
2869 jmp(&other_color, Label::kNear);
2870
2871 bind(&word_boundary);
2872 test_b(Operand(bitmap_scratch, MemoryChunk::kHeaderSize + kPointerSize),
2873 Immediate(1));
2874
2875 j(second_bit == 1 ? not_zero : zero, has_color, has_color_distance);
2876 bind(&other_color);
2877 }
2878
2879
GetMarkBits(Register addr_reg,Register bitmap_reg,Register mask_reg)2880 void MacroAssembler::GetMarkBits(Register addr_reg,
2881 Register bitmap_reg,
2882 Register mask_reg) {
2883 DCHECK(!AreAliased(addr_reg, mask_reg, bitmap_reg, ecx));
2884 mov(bitmap_reg, Immediate(~Page::kPageAlignmentMask));
2885 and_(bitmap_reg, addr_reg);
2886 mov(ecx, addr_reg);
2887 int shift =
2888 Bitmap::kBitsPerCellLog2 + kPointerSizeLog2 - Bitmap::kBytesPerCellLog2;
2889 shr(ecx, shift);
2890 and_(ecx,
2891 (Page::kPageAlignmentMask >> shift) & ~(Bitmap::kBytesPerCell - 1));
2892
2893 add(bitmap_reg, ecx);
2894 mov(ecx, addr_reg);
2895 shr(ecx, kPointerSizeLog2);
2896 and_(ecx, (1 << Bitmap::kBitsPerCellLog2) - 1);
2897 mov(mask_reg, Immediate(1));
2898 shl_cl(mask_reg);
2899 }
2900
2901
JumpIfWhite(Register value,Register bitmap_scratch,Register mask_scratch,Label * value_is_white,Label::Distance distance)2902 void MacroAssembler::JumpIfWhite(Register value, Register bitmap_scratch,
2903 Register mask_scratch, Label* value_is_white,
2904 Label::Distance distance) {
2905 DCHECK(!AreAliased(value, bitmap_scratch, mask_scratch, ecx));
2906 GetMarkBits(value, bitmap_scratch, mask_scratch);
2907
2908 // If the value is black or grey we don't need to do anything.
2909 DCHECK(strcmp(Marking::kWhiteBitPattern, "00") == 0);
2910 DCHECK(strcmp(Marking::kBlackBitPattern, "11") == 0);
2911 DCHECK(strcmp(Marking::kGreyBitPattern, "10") == 0);
2912 DCHECK(strcmp(Marking::kImpossibleBitPattern, "01") == 0);
2913
2914 // Since both black and grey have a 1 in the first position and white does
2915 // not have a 1 there we only need to check one bit.
2916 test(mask_scratch, Operand(bitmap_scratch, MemoryChunk::kHeaderSize));
2917 j(zero, value_is_white, Label::kNear);
2918 }
2919
2920
EnumLength(Register dst,Register map)2921 void MacroAssembler::EnumLength(Register dst, Register map) {
2922 STATIC_ASSERT(Map::EnumLengthBits::kShift == 0);
2923 mov(dst, FieldOperand(map, Map::kBitField3Offset));
2924 and_(dst, Immediate(Map::EnumLengthBits::kMask));
2925 SmiTag(dst);
2926 }
2927
2928
CheckEnumCache(Label * call_runtime)2929 void MacroAssembler::CheckEnumCache(Label* call_runtime) {
2930 Label next, start;
2931 mov(ecx, eax);
2932
2933 // Check if the enum length field is properly initialized, indicating that
2934 // there is an enum cache.
2935 mov(ebx, FieldOperand(ecx, HeapObject::kMapOffset));
2936
2937 EnumLength(edx, ebx);
2938 cmp(edx, Immediate(Smi::FromInt(kInvalidEnumCacheSentinel)));
2939 j(equal, call_runtime);
2940
2941 jmp(&start);
2942
2943 bind(&next);
2944 mov(ebx, FieldOperand(ecx, HeapObject::kMapOffset));
2945
2946 // For all objects but the receiver, check that the cache is empty.
2947 EnumLength(edx, ebx);
2948 cmp(edx, Immediate(Smi::kZero));
2949 j(not_equal, call_runtime);
2950
2951 bind(&start);
2952
2953 // Check that there are no elements. Register rcx contains the current JS
2954 // object we've reached through the prototype chain.
2955 Label no_elements;
2956 mov(ecx, FieldOperand(ecx, JSObject::kElementsOffset));
2957 cmp(ecx, isolate()->factory()->empty_fixed_array());
2958 j(equal, &no_elements);
2959
2960 // Second chance, the object may be using the empty slow element dictionary.
2961 cmp(ecx, isolate()->factory()->empty_slow_element_dictionary());
2962 j(not_equal, call_runtime);
2963
2964 bind(&no_elements);
2965 mov(ecx, FieldOperand(ebx, Map::kPrototypeOffset));
2966 cmp(ecx, isolate()->factory()->null_value());
2967 j(not_equal, &next);
2968 }
2969
2970
TestJSArrayForAllocationMemento(Register receiver_reg,Register scratch_reg,Label * no_memento_found)2971 void MacroAssembler::TestJSArrayForAllocationMemento(
2972 Register receiver_reg,
2973 Register scratch_reg,
2974 Label* no_memento_found) {
2975 Label map_check;
2976 Label top_check;
2977 ExternalReference new_space_allocation_top =
2978 ExternalReference::new_space_allocation_top_address(isolate());
2979 const int kMementoMapOffset = JSArray::kSize - kHeapObjectTag;
2980 const int kMementoLastWordOffset =
2981 kMementoMapOffset + AllocationMemento::kSize - kPointerSize;
2982
2983 // Bail out if the object is not in new space.
2984 JumpIfNotInNewSpace(receiver_reg, scratch_reg, no_memento_found);
2985 // If the object is in new space, we need to check whether it is on the same
2986 // page as the current top.
2987 lea(scratch_reg, Operand(receiver_reg, kMementoLastWordOffset));
2988 xor_(scratch_reg, Operand::StaticVariable(new_space_allocation_top));
2989 test(scratch_reg, Immediate(~Page::kPageAlignmentMask));
2990 j(zero, &top_check);
2991 // The object is on a different page than allocation top. Bail out if the
2992 // object sits on the page boundary as no memento can follow and we cannot
2993 // touch the memory following it.
2994 lea(scratch_reg, Operand(receiver_reg, kMementoLastWordOffset));
2995 xor_(scratch_reg, receiver_reg);
2996 test(scratch_reg, Immediate(~Page::kPageAlignmentMask));
2997 j(not_zero, no_memento_found);
2998 // Continue with the actual map check.
2999 jmp(&map_check);
3000 // If top is on the same page as the current object, we need to check whether
3001 // we are below top.
3002 bind(&top_check);
3003 lea(scratch_reg, Operand(receiver_reg, kMementoLastWordOffset));
3004 cmp(scratch_reg, Operand::StaticVariable(new_space_allocation_top));
3005 j(greater_equal, no_memento_found);
3006 // Memento map check.
3007 bind(&map_check);
3008 mov(scratch_reg, Operand(receiver_reg, kMementoMapOffset));
3009 cmp(scratch_reg, Immediate(isolate()->factory()->allocation_memento_map()));
3010 }
3011
3012
JumpIfDictionaryInPrototypeChain(Register object,Register scratch0,Register scratch1,Label * found)3013 void MacroAssembler::JumpIfDictionaryInPrototypeChain(
3014 Register object,
3015 Register scratch0,
3016 Register scratch1,
3017 Label* found) {
3018 DCHECK(!scratch1.is(scratch0));
3019 Factory* factory = isolate()->factory();
3020 Register current = scratch0;
3021 Label loop_again, end;
3022
3023 // scratch contained elements pointer.
3024 mov(current, object);
3025 mov(current, FieldOperand(current, HeapObject::kMapOffset));
3026 mov(current, FieldOperand(current, Map::kPrototypeOffset));
3027 cmp(current, Immediate(factory->null_value()));
3028 j(equal, &end);
3029
3030 // Loop based on the map going up the prototype chain.
3031 bind(&loop_again);
3032 mov(current, FieldOperand(current, HeapObject::kMapOffset));
3033 STATIC_ASSERT(JS_PROXY_TYPE < JS_OBJECT_TYPE);
3034 STATIC_ASSERT(JS_VALUE_TYPE < JS_OBJECT_TYPE);
3035 CmpInstanceType(current, JS_OBJECT_TYPE);
3036 j(below, found);
3037 mov(scratch1, FieldOperand(current, Map::kBitField2Offset));
3038 DecodeField<Map::ElementsKindBits>(scratch1);
3039 cmp(scratch1, Immediate(DICTIONARY_ELEMENTS));
3040 j(equal, found);
3041 mov(current, FieldOperand(current, Map::kPrototypeOffset));
3042 cmp(current, Immediate(factory->null_value()));
3043 j(not_equal, &loop_again);
3044
3045 bind(&end);
3046 }
3047
3048
TruncatingDiv(Register dividend,int32_t divisor)3049 void MacroAssembler::TruncatingDiv(Register dividend, int32_t divisor) {
3050 DCHECK(!dividend.is(eax));
3051 DCHECK(!dividend.is(edx));
3052 base::MagicNumbersForDivision<uint32_t> mag =
3053 base::SignedDivisionByConstant(static_cast<uint32_t>(divisor));
3054 mov(eax, Immediate(mag.multiplier));
3055 imul(dividend);
3056 bool neg = (mag.multiplier & (static_cast<uint32_t>(1) << 31)) != 0;
3057 if (divisor > 0 && neg) add(edx, dividend);
3058 if (divisor < 0 && !neg && mag.multiplier > 0) sub(edx, dividend);
3059 if (mag.shift > 0) sar(edx, mag.shift);
3060 mov(eax, dividend);
3061 shr(eax, 31);
3062 add(edx, eax);
3063 }
3064
3065
3066 } // namespace internal
3067 } // namespace v8
3068
3069 #endif // V8_TARGET_ARCH_X87
3070