1 // Copyright 2013 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef V8_ARM64_ASSEMBLER_ARM64_INL_H_
6 #define V8_ARM64_ASSEMBLER_ARM64_INL_H_
7
8 #include "src/arm64/assembler-arm64.h"
9 #include "src/cpu.h"
10 #include "src/debug.h"
11
12
13 namespace v8 {
14 namespace internal {
15
16
SupportsCrankshaft()17 bool CpuFeatures::SupportsCrankshaft() { return true; }
18
19
apply(intptr_t delta,ICacheFlushMode icache_flush_mode)20 void RelocInfo::apply(intptr_t delta, ICacheFlushMode icache_flush_mode) {
21 UNIMPLEMENTED();
22 }
23
24
set_target_address(Address target,WriteBarrierMode write_barrier_mode,ICacheFlushMode icache_flush_mode)25 void RelocInfo::set_target_address(Address target,
26 WriteBarrierMode write_barrier_mode,
27 ICacheFlushMode icache_flush_mode) {
28 ASSERT(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_));
29 Assembler::set_target_address_at(pc_, host_, target, icache_flush_mode);
30 if (write_barrier_mode == UPDATE_WRITE_BARRIER && host() != NULL &&
31 IsCodeTarget(rmode_)) {
32 Object* target_code = Code::GetCodeFromTargetAddress(target);
33 host()->GetHeap()->incremental_marking()->RecordWriteIntoCode(
34 host(), this, HeapObject::cast(target_code));
35 }
36 }
37
38
code()39 inline unsigned CPURegister::code() const {
40 ASSERT(IsValid());
41 return reg_code;
42 }
43
44
type()45 inline CPURegister::RegisterType CPURegister::type() const {
46 ASSERT(IsValidOrNone());
47 return reg_type;
48 }
49
50
Bit()51 inline RegList CPURegister::Bit() const {
52 ASSERT(reg_code < (sizeof(RegList) * kBitsPerByte));
53 return IsValid() ? 1UL << reg_code : 0;
54 }
55
56
SizeInBits()57 inline unsigned CPURegister::SizeInBits() const {
58 ASSERT(IsValid());
59 return reg_size;
60 }
61
62
SizeInBytes()63 inline int CPURegister::SizeInBytes() const {
64 ASSERT(IsValid());
65 ASSERT(SizeInBits() % 8 == 0);
66 return reg_size / 8;
67 }
68
69
Is32Bits()70 inline bool CPURegister::Is32Bits() const {
71 ASSERT(IsValid());
72 return reg_size == 32;
73 }
74
75
Is64Bits()76 inline bool CPURegister::Is64Bits() const {
77 ASSERT(IsValid());
78 return reg_size == 64;
79 }
80
81
IsValid()82 inline bool CPURegister::IsValid() const {
83 if (IsValidRegister() || IsValidFPRegister()) {
84 ASSERT(!IsNone());
85 return true;
86 } else {
87 ASSERT(IsNone());
88 return false;
89 }
90 }
91
92
IsValidRegister()93 inline bool CPURegister::IsValidRegister() const {
94 return IsRegister() &&
95 ((reg_size == kWRegSizeInBits) || (reg_size == kXRegSizeInBits)) &&
96 ((reg_code < kNumberOfRegisters) || (reg_code == kSPRegInternalCode));
97 }
98
99
IsValidFPRegister()100 inline bool CPURegister::IsValidFPRegister() const {
101 return IsFPRegister() &&
102 ((reg_size == kSRegSizeInBits) || (reg_size == kDRegSizeInBits)) &&
103 (reg_code < kNumberOfFPRegisters);
104 }
105
106
IsNone()107 inline bool CPURegister::IsNone() const {
108 // kNoRegister types should always have size 0 and code 0.
109 ASSERT((reg_type != kNoRegister) || (reg_code == 0));
110 ASSERT((reg_type != kNoRegister) || (reg_size == 0));
111
112 return reg_type == kNoRegister;
113 }
114
115
Is(const CPURegister & other)116 inline bool CPURegister::Is(const CPURegister& other) const {
117 ASSERT(IsValidOrNone() && other.IsValidOrNone());
118 return Aliases(other) && (reg_size == other.reg_size);
119 }
120
121
Aliases(const CPURegister & other)122 inline bool CPURegister::Aliases(const CPURegister& other) const {
123 ASSERT(IsValidOrNone() && other.IsValidOrNone());
124 return (reg_code == other.reg_code) && (reg_type == other.reg_type);
125 }
126
127
IsRegister()128 inline bool CPURegister::IsRegister() const {
129 return reg_type == kRegister;
130 }
131
132
IsFPRegister()133 inline bool CPURegister::IsFPRegister() const {
134 return reg_type == kFPRegister;
135 }
136
137
IsSameSizeAndType(const CPURegister & other)138 inline bool CPURegister::IsSameSizeAndType(const CPURegister& other) const {
139 return (reg_size == other.reg_size) && (reg_type == other.reg_type);
140 }
141
142
IsValidOrNone()143 inline bool CPURegister::IsValidOrNone() const {
144 return IsValid() || IsNone();
145 }
146
147
IsZero()148 inline bool CPURegister::IsZero() const {
149 ASSERT(IsValid());
150 return IsRegister() && (reg_code == kZeroRegCode);
151 }
152
153
IsSP()154 inline bool CPURegister::IsSP() const {
155 ASSERT(IsValid());
156 return IsRegister() && (reg_code == kSPRegInternalCode);
157 }
158
159
Combine(const CPURegList & other)160 inline void CPURegList::Combine(const CPURegList& other) {
161 ASSERT(IsValid());
162 ASSERT(other.type() == type_);
163 ASSERT(other.RegisterSizeInBits() == size_);
164 list_ |= other.list();
165 }
166
167
Remove(const CPURegList & other)168 inline void CPURegList::Remove(const CPURegList& other) {
169 ASSERT(IsValid());
170 if (other.type() == type_) {
171 list_ &= ~other.list();
172 }
173 }
174
175
Combine(const CPURegister & other)176 inline void CPURegList::Combine(const CPURegister& other) {
177 ASSERT(other.type() == type_);
178 ASSERT(other.SizeInBits() == size_);
179 Combine(other.code());
180 }
181
182
Remove(const CPURegister & other1,const CPURegister & other2,const CPURegister & other3,const CPURegister & other4)183 inline void CPURegList::Remove(const CPURegister& other1,
184 const CPURegister& other2,
185 const CPURegister& other3,
186 const CPURegister& other4) {
187 if (!other1.IsNone() && (other1.type() == type_)) Remove(other1.code());
188 if (!other2.IsNone() && (other2.type() == type_)) Remove(other2.code());
189 if (!other3.IsNone() && (other3.type() == type_)) Remove(other3.code());
190 if (!other4.IsNone() && (other4.type() == type_)) Remove(other4.code());
191 }
192
193
Combine(int code)194 inline void CPURegList::Combine(int code) {
195 ASSERT(IsValid());
196 ASSERT(CPURegister::Create(code, size_, type_).IsValid());
197 list_ |= (1UL << code);
198 }
199
200
Remove(int code)201 inline void CPURegList::Remove(int code) {
202 ASSERT(IsValid());
203 ASSERT(CPURegister::Create(code, size_, type_).IsValid());
204 list_ &= ~(1UL << code);
205 }
206
207
XRegFromCode(unsigned code)208 inline Register Register::XRegFromCode(unsigned code) {
209 if (code == kSPRegInternalCode) {
210 return csp;
211 } else {
212 ASSERT(code < kNumberOfRegisters);
213 return Register::Create(code, kXRegSizeInBits);
214 }
215 }
216
217
WRegFromCode(unsigned code)218 inline Register Register::WRegFromCode(unsigned code) {
219 if (code == kSPRegInternalCode) {
220 return wcsp;
221 } else {
222 ASSERT(code < kNumberOfRegisters);
223 return Register::Create(code, kWRegSizeInBits);
224 }
225 }
226
227
SRegFromCode(unsigned code)228 inline FPRegister FPRegister::SRegFromCode(unsigned code) {
229 ASSERT(code < kNumberOfFPRegisters);
230 return FPRegister::Create(code, kSRegSizeInBits);
231 }
232
233
DRegFromCode(unsigned code)234 inline FPRegister FPRegister::DRegFromCode(unsigned code) {
235 ASSERT(code < kNumberOfFPRegisters);
236 return FPRegister::Create(code, kDRegSizeInBits);
237 }
238
239
W()240 inline Register CPURegister::W() const {
241 ASSERT(IsValidRegister());
242 return Register::WRegFromCode(reg_code);
243 }
244
245
X()246 inline Register CPURegister::X() const {
247 ASSERT(IsValidRegister());
248 return Register::XRegFromCode(reg_code);
249 }
250
251
S()252 inline FPRegister CPURegister::S() const {
253 ASSERT(IsValidFPRegister());
254 return FPRegister::SRegFromCode(reg_code);
255 }
256
257
D()258 inline FPRegister CPURegister::D() const {
259 ASSERT(IsValidFPRegister());
260 return FPRegister::DRegFromCode(reg_code);
261 }
262
263
264 // Immediate.
265 // Default initializer is for int types
266 template<typename T>
267 struct ImmediateInitializer {
268 static const bool kIsIntType = true;
rmode_forImmediateInitializer269 static inline RelocInfo::Mode rmode_for(T) {
270 return sizeof(T) == 8 ? RelocInfo::NONE64 : RelocInfo::NONE32;
271 }
immediate_forImmediateInitializer272 static inline int64_t immediate_for(T t) {
273 STATIC_ASSERT(sizeof(T) <= 8);
274 return t;
275 }
276 };
277
278
279 template<>
280 struct ImmediateInitializer<Smi*> {
281 static const bool kIsIntType = false;
282 static inline RelocInfo::Mode rmode_for(Smi* t) {
283 return RelocInfo::NONE64;
284 }
285 static inline int64_t immediate_for(Smi* t) {;
286 return reinterpret_cast<int64_t>(t);
287 }
288 };
289
290
291 template<>
292 struct ImmediateInitializer<ExternalReference> {
293 static const bool kIsIntType = false;
294 static inline RelocInfo::Mode rmode_for(ExternalReference t) {
295 return RelocInfo::EXTERNAL_REFERENCE;
296 }
297 static inline int64_t immediate_for(ExternalReference t) {;
298 return reinterpret_cast<int64_t>(t.address());
299 }
300 };
301
302
303 template<typename T>
304 Immediate::Immediate(Handle<T> value) {
305 InitializeHandle(value);
306 }
307
308
309 template<typename T>
310 Immediate::Immediate(T t)
311 : value_(ImmediateInitializer<T>::immediate_for(t)),
312 rmode_(ImmediateInitializer<T>::rmode_for(t)) {}
313
314
315 template<typename T>
316 Immediate::Immediate(T t, RelocInfo::Mode rmode)
317 : value_(ImmediateInitializer<T>::immediate_for(t)),
318 rmode_(rmode) {
319 STATIC_ASSERT(ImmediateInitializer<T>::kIsIntType);
320 }
321
322
323 // Operand.
324 template<typename T>
325 Operand::Operand(Handle<T> value) : immediate_(value), reg_(NoReg) {}
326
327
328 template<typename T>
329 Operand::Operand(T t) : immediate_(t), reg_(NoReg) {}
330
331
332 template<typename T>
333 Operand::Operand(T t, RelocInfo::Mode rmode)
334 : immediate_(t, rmode),
335 reg_(NoReg) {}
336
337
338 Operand::Operand(Register reg, Shift shift, unsigned shift_amount)
339 : immediate_(0),
340 reg_(reg),
341 shift_(shift),
342 extend_(NO_EXTEND),
343 shift_amount_(shift_amount) {
344 ASSERT(reg.Is64Bits() || (shift_amount < kWRegSizeInBits));
345 ASSERT(reg.Is32Bits() || (shift_amount < kXRegSizeInBits));
346 ASSERT(!reg.IsSP());
347 }
348
349
350 Operand::Operand(Register reg, Extend extend, unsigned shift_amount)
351 : immediate_(0),
352 reg_(reg),
353 shift_(NO_SHIFT),
354 extend_(extend),
355 shift_amount_(shift_amount) {
356 ASSERT(reg.IsValid());
357 ASSERT(shift_amount <= 4);
358 ASSERT(!reg.IsSP());
359
360 // Extend modes SXTX and UXTX require a 64-bit register.
361 ASSERT(reg.Is64Bits() || ((extend != SXTX) && (extend != UXTX)));
362 }
363
364
365 bool Operand::IsImmediate() const {
366 return reg_.Is(NoReg);
367 }
368
369
370 bool Operand::IsShiftedRegister() const {
371 return reg_.IsValid() && (shift_ != NO_SHIFT);
372 }
373
374
375 bool Operand::IsExtendedRegister() const {
376 return reg_.IsValid() && (extend_ != NO_EXTEND);
377 }
378
379
380 bool Operand::IsZero() const {
381 if (IsImmediate()) {
382 return ImmediateValue() == 0;
383 } else {
384 return reg().IsZero();
385 }
386 }
387
388
389 Operand Operand::ToExtendedRegister() const {
390 ASSERT(IsShiftedRegister());
391 ASSERT((shift_ == LSL) && (shift_amount_ <= 4));
392 return Operand(reg_, reg_.Is64Bits() ? UXTX : UXTW, shift_amount_);
393 }
394
395
396 Immediate Operand::immediate() const {
397 ASSERT(IsImmediate());
398 return immediate_;
399 }
400
401
402 int64_t Operand::ImmediateValue() const {
403 ASSERT(IsImmediate());
404 return immediate_.value();
405 }
406
407
408 Register Operand::reg() const {
409 ASSERT(IsShiftedRegister() || IsExtendedRegister());
410 return reg_;
411 }
412
413
414 Shift Operand::shift() const {
415 ASSERT(IsShiftedRegister());
416 return shift_;
417 }
418
419
420 Extend Operand::extend() const {
421 ASSERT(IsExtendedRegister());
422 return extend_;
423 }
424
425
426 unsigned Operand::shift_amount() const {
427 ASSERT(IsShiftedRegister() || IsExtendedRegister());
428 return shift_amount_;
429 }
430
431
432 Operand Operand::UntagSmi(Register smi) {
433 ASSERT(smi.Is64Bits());
434 return Operand(smi, ASR, kSmiShift);
435 }
436
437
438 Operand Operand::UntagSmiAndScale(Register smi, int scale) {
439 ASSERT(smi.Is64Bits());
440 ASSERT((scale >= 0) && (scale <= (64 - kSmiValueSize)));
441 if (scale > kSmiShift) {
442 return Operand(smi, LSL, scale - kSmiShift);
443 } else if (scale < kSmiShift) {
444 return Operand(smi, ASR, kSmiShift - scale);
445 }
446 return Operand(smi);
447 }
448
449
450 MemOperand::MemOperand()
451 : base_(NoReg), regoffset_(NoReg), offset_(0), addrmode_(Offset),
452 shift_(NO_SHIFT), extend_(NO_EXTEND), shift_amount_(0) {
453 }
454
455
456 MemOperand::MemOperand(Register base, ptrdiff_t offset, AddrMode addrmode)
457 : base_(base), regoffset_(NoReg), offset_(offset), addrmode_(addrmode),
458 shift_(NO_SHIFT), extend_(NO_EXTEND), shift_amount_(0) {
459 ASSERT(base.Is64Bits() && !base.IsZero());
460 }
461
462
463 MemOperand::MemOperand(Register base,
464 Register regoffset,
465 Extend extend,
466 unsigned shift_amount)
467 : base_(base), regoffset_(regoffset), offset_(0), addrmode_(Offset),
468 shift_(NO_SHIFT), extend_(extend), shift_amount_(shift_amount) {
469 ASSERT(base.Is64Bits() && !base.IsZero());
470 ASSERT(!regoffset.IsSP());
471 ASSERT((extend == UXTW) || (extend == SXTW) || (extend == SXTX));
472
473 // SXTX extend mode requires a 64-bit offset register.
474 ASSERT(regoffset.Is64Bits() || (extend != SXTX));
475 }
476
477
478 MemOperand::MemOperand(Register base,
479 Register regoffset,
480 Shift shift,
481 unsigned shift_amount)
482 : base_(base), regoffset_(regoffset), offset_(0), addrmode_(Offset),
483 shift_(shift), extend_(NO_EXTEND), shift_amount_(shift_amount) {
484 ASSERT(base.Is64Bits() && !base.IsZero());
485 ASSERT(regoffset.Is64Bits() && !regoffset.IsSP());
486 ASSERT(shift == LSL);
487 }
488
489
490 MemOperand::MemOperand(Register base, const Operand& offset, AddrMode addrmode)
491 : base_(base), addrmode_(addrmode) {
492 ASSERT(base.Is64Bits() && !base.IsZero());
493
494 if (offset.IsImmediate()) {
495 offset_ = offset.ImmediateValue();
496
497 regoffset_ = NoReg;
498 } else if (offset.IsShiftedRegister()) {
499 ASSERT(addrmode == Offset);
500
501 regoffset_ = offset.reg();
502 shift_= offset.shift();
503 shift_amount_ = offset.shift_amount();
504
505 extend_ = NO_EXTEND;
506 offset_ = 0;
507
508 // These assertions match those in the shifted-register constructor.
509 ASSERT(regoffset_.Is64Bits() && !regoffset_.IsSP());
510 ASSERT(shift_ == LSL);
511 } else {
512 ASSERT(offset.IsExtendedRegister());
513 ASSERT(addrmode == Offset);
514
515 regoffset_ = offset.reg();
516 extend_ = offset.extend();
517 shift_amount_ = offset.shift_amount();
518
519 shift_= NO_SHIFT;
520 offset_ = 0;
521
522 // These assertions match those in the extended-register constructor.
523 ASSERT(!regoffset_.IsSP());
524 ASSERT((extend_ == UXTW) || (extend_ == SXTW) || (extend_ == SXTX));
525 ASSERT((regoffset_.Is64Bits() || (extend_ != SXTX)));
526 }
527 }
528
529 bool MemOperand::IsImmediateOffset() const {
530 return (addrmode_ == Offset) && regoffset_.Is(NoReg);
531 }
532
533
534 bool MemOperand::IsRegisterOffset() const {
535 return (addrmode_ == Offset) && !regoffset_.Is(NoReg);
536 }
537
538
539 bool MemOperand::IsPreIndex() const {
540 return addrmode_ == PreIndex;
541 }
542
543
544 bool MemOperand::IsPostIndex() const {
545 return addrmode_ == PostIndex;
546 }
547
548 Operand MemOperand::OffsetAsOperand() const {
549 if (IsImmediateOffset()) {
550 return offset();
551 } else {
552 ASSERT(IsRegisterOffset());
553 if (extend() == NO_EXTEND) {
554 return Operand(regoffset(), shift(), shift_amount());
555 } else {
556 return Operand(regoffset(), extend(), shift_amount());
557 }
558 }
559 }
560
561
562 void Assembler::Unreachable() {
563 #ifdef USE_SIMULATOR
564 debug("UNREACHABLE", __LINE__, BREAK);
565 #else
566 // Crash by branching to 0. lr now points near the fault.
567 Emit(BLR | Rn(xzr));
568 #endif
569 }
570
571
572 Address Assembler::target_pointer_address_at(Address pc) {
573 Instruction* instr = reinterpret_cast<Instruction*>(pc);
574 ASSERT(instr->IsLdrLiteralX());
575 return reinterpret_cast<Address>(instr->ImmPCOffsetTarget());
576 }
577
578
579 // Read/Modify the code target address in the branch/call instruction at pc.
580 Address Assembler::target_address_at(Address pc,
581 ConstantPoolArray* constant_pool) {
582 return Memory::Address_at(target_pointer_address_at(pc));
583 }
584
585
586 Address Assembler::target_address_at(Address pc, Code* code) {
587 ConstantPoolArray* constant_pool = code ? code->constant_pool() : NULL;
588 return target_address_at(pc, constant_pool);
589 }
590
591
592 Address Assembler::target_address_from_return_address(Address pc) {
593 // Returns the address of the call target from the return address that will
594 // be returned to after a call.
595 // Call sequence on ARM64 is:
596 // ldr ip0, #... @ load from literal pool
597 // blr ip0
598 Address candidate = pc - 2 * kInstructionSize;
599 Instruction* instr = reinterpret_cast<Instruction*>(candidate);
600 USE(instr);
601 ASSERT(instr->IsLdrLiteralX());
602 return candidate;
603 }
604
605
606 Address Assembler::return_address_from_call_start(Address pc) {
607 // The call, generated by MacroAssembler::Call, is one of two possible
608 // sequences:
609 //
610 // Without relocation:
611 // movz temp, #(target & 0x000000000000ffff)
612 // movk temp, #(target & 0x00000000ffff0000)
613 // movk temp, #(target & 0x0000ffff00000000)
614 // blr temp
615 //
616 // With relocation:
617 // ldr temp, =target
618 // blr temp
619 //
620 // The return address is immediately after the blr instruction in both cases,
621 // so it can be found by adding the call size to the address at the start of
622 // the call sequence.
623 STATIC_ASSERT(Assembler::kCallSizeWithoutRelocation == 4 * kInstructionSize);
624 STATIC_ASSERT(Assembler::kCallSizeWithRelocation == 2 * kInstructionSize);
625
626 Instruction* instr = reinterpret_cast<Instruction*>(pc);
627 if (instr->IsMovz()) {
628 // Verify the instruction sequence.
629 ASSERT(instr->following(1)->IsMovk());
630 ASSERT(instr->following(2)->IsMovk());
631 ASSERT(instr->following(3)->IsBranchAndLinkToRegister());
632 return pc + Assembler::kCallSizeWithoutRelocation;
633 } else {
634 // Verify the instruction sequence.
635 ASSERT(instr->IsLdrLiteralX());
636 ASSERT(instr->following(1)->IsBranchAndLinkToRegister());
637 return pc + Assembler::kCallSizeWithRelocation;
638 }
639 }
640
641
642 void Assembler::deserialization_set_special_target_at(
643 Address constant_pool_entry, Code* code, Address target) {
644 Memory::Address_at(constant_pool_entry) = target;
645 }
646
647
648 void Assembler::set_target_address_at(Address pc,
649 ConstantPoolArray* constant_pool,
650 Address target,
651 ICacheFlushMode icache_flush_mode) {
652 Memory::Address_at(target_pointer_address_at(pc)) = target;
653 // Intuitively, we would think it is necessary to always flush the
654 // instruction cache after patching a target address in the code as follows:
655 // CPU::FlushICache(pc, sizeof(target));
656 // However, on ARM, an instruction is actually patched in the case of
657 // embedded constants of the form:
658 // ldr ip, [pc, #...]
659 // since the instruction accessing this address in the constant pool remains
660 // unchanged, a flush is not required.
661 }
662
663
664 void Assembler::set_target_address_at(Address pc,
665 Code* code,
666 Address target,
667 ICacheFlushMode icache_flush_mode) {
668 ConstantPoolArray* constant_pool = code ? code->constant_pool() : NULL;
669 set_target_address_at(pc, constant_pool, target, icache_flush_mode);
670 }
671
672
673 int RelocInfo::target_address_size() {
674 return kPointerSize;
675 }
676
677
678 Address RelocInfo::target_address() {
679 ASSERT(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_));
680 return Assembler::target_address_at(pc_, host_);
681 }
682
683
684 Address RelocInfo::target_address_address() {
685 ASSERT(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)
686 || rmode_ == EMBEDDED_OBJECT
687 || rmode_ == EXTERNAL_REFERENCE);
688 return Assembler::target_pointer_address_at(pc_);
689 }
690
691
692 Address RelocInfo::constant_pool_entry_address() {
693 ASSERT(IsInConstantPool());
694 return Assembler::target_pointer_address_at(pc_);
695 }
696
697
698 Object* RelocInfo::target_object() {
699 ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
700 return reinterpret_cast<Object*>(Assembler::target_address_at(pc_, host_));
701 }
702
703
704 Handle<Object> RelocInfo::target_object_handle(Assembler* origin) {
705 ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
706 return Handle<Object>(reinterpret_cast<Object**>(
707 Assembler::target_address_at(pc_, host_)));
708 }
709
710
711 void RelocInfo::set_target_object(Object* target,
712 WriteBarrierMode write_barrier_mode,
713 ICacheFlushMode icache_flush_mode) {
714 ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
715 ASSERT(!target->IsConsString());
716 Assembler::set_target_address_at(pc_, host_,
717 reinterpret_cast<Address>(target),
718 icache_flush_mode);
719 if (write_barrier_mode == UPDATE_WRITE_BARRIER &&
720 host() != NULL &&
721 target->IsHeapObject()) {
722 host()->GetHeap()->incremental_marking()->RecordWrite(
723 host(), &Memory::Object_at(pc_), HeapObject::cast(target));
724 }
725 }
726
727
728 Address RelocInfo::target_reference() {
729 ASSERT(rmode_ == EXTERNAL_REFERENCE);
730 return Assembler::target_address_at(pc_, host_);
731 }
732
733
734 Address RelocInfo::target_runtime_entry(Assembler* origin) {
735 ASSERT(IsRuntimeEntry(rmode_));
736 return target_address();
737 }
738
739
740 void RelocInfo::set_target_runtime_entry(Address target,
741 WriteBarrierMode write_barrier_mode,
742 ICacheFlushMode icache_flush_mode) {
743 ASSERT(IsRuntimeEntry(rmode_));
744 if (target_address() != target) {
745 set_target_address(target, write_barrier_mode, icache_flush_mode);
746 }
747 }
748
749
750 Handle<Cell> RelocInfo::target_cell_handle() {
751 UNIMPLEMENTED();
752 Cell *null_cell = NULL;
753 return Handle<Cell>(null_cell);
754 }
755
756
757 Cell* RelocInfo::target_cell() {
758 ASSERT(rmode_ == RelocInfo::CELL);
759 return Cell::FromValueAddress(Memory::Address_at(pc_));
760 }
761
762
763 void RelocInfo::set_target_cell(Cell* cell,
764 WriteBarrierMode write_barrier_mode,
765 ICacheFlushMode icache_flush_mode) {
766 UNIMPLEMENTED();
767 }
768
769
770 static const int kNoCodeAgeSequenceLength = 5 * kInstructionSize;
771 static const int kCodeAgeStubEntryOffset = 3 * kInstructionSize;
772
773
774 Handle<Object> RelocInfo::code_age_stub_handle(Assembler* origin) {
775 UNREACHABLE(); // This should never be reached on ARM64.
776 return Handle<Object>();
777 }
778
779
780 Code* RelocInfo::code_age_stub() {
781 ASSERT(rmode_ == RelocInfo::CODE_AGE_SEQUENCE);
782 // Read the stub entry point from the code age sequence.
783 Address stub_entry_address = pc_ + kCodeAgeStubEntryOffset;
784 return Code::GetCodeFromTargetAddress(Memory::Address_at(stub_entry_address));
785 }
786
787
788 void RelocInfo::set_code_age_stub(Code* stub,
789 ICacheFlushMode icache_flush_mode) {
790 ASSERT(rmode_ == RelocInfo::CODE_AGE_SEQUENCE);
791 ASSERT(!Code::IsYoungSequence(stub->GetIsolate(), pc_));
792 // Overwrite the stub entry point in the code age sequence. This is loaded as
793 // a literal so there is no need to call FlushICache here.
794 Address stub_entry_address = pc_ + kCodeAgeStubEntryOffset;
795 Memory::Address_at(stub_entry_address) = stub->instruction_start();
796 }
797
798
799 Address RelocInfo::call_address() {
800 ASSERT((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
801 (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
802 // For the above sequences the Relocinfo points to the load literal loading
803 // the call address.
804 return Assembler::target_address_at(pc_, host_);
805 }
806
807
808 void RelocInfo::set_call_address(Address target) {
809 ASSERT((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
810 (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
811 Assembler::set_target_address_at(pc_, host_, target);
812 if (host() != NULL) {
813 Object* target_code = Code::GetCodeFromTargetAddress(target);
814 host()->GetHeap()->incremental_marking()->RecordWriteIntoCode(
815 host(), this, HeapObject::cast(target_code));
816 }
817 }
818
819
820 void RelocInfo::WipeOut() {
821 ASSERT(IsEmbeddedObject(rmode_) ||
822 IsCodeTarget(rmode_) ||
823 IsRuntimeEntry(rmode_) ||
824 IsExternalReference(rmode_));
825 Assembler::set_target_address_at(pc_, host_, NULL);
826 }
827
828
829 bool RelocInfo::IsPatchedReturnSequence() {
830 // The sequence must be:
831 // ldr ip0, [pc, #offset]
832 // blr ip0
833 // See arm64/debug-arm64.cc BreakLocationIterator::SetDebugBreakAtReturn().
834 Instruction* i1 = reinterpret_cast<Instruction*>(pc_);
835 Instruction* i2 = i1->following();
836 return i1->IsLdrLiteralX() && (i1->Rt() == ip0.code()) &&
837 i2->IsBranchAndLinkToRegister() && (i2->Rn() == ip0.code());
838 }
839
840
841 bool RelocInfo::IsPatchedDebugBreakSlotSequence() {
842 Instruction* current_instr = reinterpret_cast<Instruction*>(pc_);
843 return !current_instr->IsNop(Assembler::DEBUG_BREAK_NOP);
844 }
845
846
847 void RelocInfo::Visit(Isolate* isolate, ObjectVisitor* visitor) {
848 RelocInfo::Mode mode = rmode();
849 if (mode == RelocInfo::EMBEDDED_OBJECT) {
850 visitor->VisitEmbeddedPointer(this);
851 } else if (RelocInfo::IsCodeTarget(mode)) {
852 visitor->VisitCodeTarget(this);
853 } else if (mode == RelocInfo::CELL) {
854 visitor->VisitCell(this);
855 } else if (mode == RelocInfo::EXTERNAL_REFERENCE) {
856 visitor->VisitExternalReference(this);
857 } else if (((RelocInfo::IsJSReturn(mode) &&
858 IsPatchedReturnSequence()) ||
859 (RelocInfo::IsDebugBreakSlot(mode) &&
860 IsPatchedDebugBreakSlotSequence())) &&
861 isolate->debug()->has_break_points()) {
862 visitor->VisitDebugTarget(this);
863 } else if (RelocInfo::IsRuntimeEntry(mode)) {
864 visitor->VisitRuntimeEntry(this);
865 }
866 }
867
868
869 template<typename StaticVisitor>
870 void RelocInfo::Visit(Heap* heap) {
871 RelocInfo::Mode mode = rmode();
872 if (mode == RelocInfo::EMBEDDED_OBJECT) {
873 StaticVisitor::VisitEmbeddedPointer(heap, this);
874 } else if (RelocInfo::IsCodeTarget(mode)) {
875 StaticVisitor::VisitCodeTarget(heap, this);
876 } else if (mode == RelocInfo::CELL) {
877 StaticVisitor::VisitCell(heap, this);
878 } else if (mode == RelocInfo::EXTERNAL_REFERENCE) {
879 StaticVisitor::VisitExternalReference(this);
880 } else if (heap->isolate()->debug()->has_break_points() &&
881 ((RelocInfo::IsJSReturn(mode) &&
882 IsPatchedReturnSequence()) ||
883 (RelocInfo::IsDebugBreakSlot(mode) &&
884 IsPatchedDebugBreakSlotSequence()))) {
885 StaticVisitor::VisitDebugTarget(heap, this);
886 } else if (RelocInfo::IsRuntimeEntry(mode)) {
887 StaticVisitor::VisitRuntimeEntry(this);
888 }
889 }
890
891
892 LoadStoreOp Assembler::LoadOpFor(const CPURegister& rt) {
893 ASSERT(rt.IsValid());
894 if (rt.IsRegister()) {
895 return rt.Is64Bits() ? LDR_x : LDR_w;
896 } else {
897 ASSERT(rt.IsFPRegister());
898 return rt.Is64Bits() ? LDR_d : LDR_s;
899 }
900 }
901
902
903 LoadStorePairOp Assembler::LoadPairOpFor(const CPURegister& rt,
904 const CPURegister& rt2) {
905 ASSERT(AreSameSizeAndType(rt, rt2));
906 USE(rt2);
907 if (rt.IsRegister()) {
908 return rt.Is64Bits() ? LDP_x : LDP_w;
909 } else {
910 ASSERT(rt.IsFPRegister());
911 return rt.Is64Bits() ? LDP_d : LDP_s;
912 }
913 }
914
915
916 LoadStoreOp Assembler::StoreOpFor(const CPURegister& rt) {
917 ASSERT(rt.IsValid());
918 if (rt.IsRegister()) {
919 return rt.Is64Bits() ? STR_x : STR_w;
920 } else {
921 ASSERT(rt.IsFPRegister());
922 return rt.Is64Bits() ? STR_d : STR_s;
923 }
924 }
925
926
927 LoadStorePairOp Assembler::StorePairOpFor(const CPURegister& rt,
928 const CPURegister& rt2) {
929 ASSERT(AreSameSizeAndType(rt, rt2));
930 USE(rt2);
931 if (rt.IsRegister()) {
932 return rt.Is64Bits() ? STP_x : STP_w;
933 } else {
934 ASSERT(rt.IsFPRegister());
935 return rt.Is64Bits() ? STP_d : STP_s;
936 }
937 }
938
939
940 LoadStorePairNonTemporalOp Assembler::LoadPairNonTemporalOpFor(
941 const CPURegister& rt, const CPURegister& rt2) {
942 ASSERT(AreSameSizeAndType(rt, rt2));
943 USE(rt2);
944 if (rt.IsRegister()) {
945 return rt.Is64Bits() ? LDNP_x : LDNP_w;
946 } else {
947 ASSERT(rt.IsFPRegister());
948 return rt.Is64Bits() ? LDNP_d : LDNP_s;
949 }
950 }
951
952
953 LoadStorePairNonTemporalOp Assembler::StorePairNonTemporalOpFor(
954 const CPURegister& rt, const CPURegister& rt2) {
955 ASSERT(AreSameSizeAndType(rt, rt2));
956 USE(rt2);
957 if (rt.IsRegister()) {
958 return rt.Is64Bits() ? STNP_x : STNP_w;
959 } else {
960 ASSERT(rt.IsFPRegister());
961 return rt.Is64Bits() ? STNP_d : STNP_s;
962 }
963 }
964
965
966 LoadLiteralOp Assembler::LoadLiteralOpFor(const CPURegister& rt) {
967 if (rt.IsRegister()) {
968 return rt.Is64Bits() ? LDR_x_lit : LDR_w_lit;
969 } else {
970 ASSERT(rt.IsFPRegister());
971 return rt.Is64Bits() ? LDR_d_lit : LDR_s_lit;
972 }
973 }
974
975
976 int Assembler::LinkAndGetInstructionOffsetTo(Label* label) {
977 ASSERT(kStartOfLabelLinkChain == 0);
978 int offset = LinkAndGetByteOffsetTo(label);
979 ASSERT(IsAligned(offset, kInstructionSize));
980 return offset >> kInstructionSizeLog2;
981 }
982
983
984 Instr Assembler::Flags(FlagsUpdate S) {
985 if (S == SetFlags) {
986 return 1 << FlagsUpdate_offset;
987 } else if (S == LeaveFlags) {
988 return 0 << FlagsUpdate_offset;
989 }
990 UNREACHABLE();
991 return 0;
992 }
993
994
995 Instr Assembler::Cond(Condition cond) {
996 return cond << Condition_offset;
997 }
998
999
1000 Instr Assembler::ImmPCRelAddress(int imm21) {
1001 CHECK(is_int21(imm21));
1002 Instr imm = static_cast<Instr>(truncate_to_int21(imm21));
1003 Instr immhi = (imm >> ImmPCRelLo_width) << ImmPCRelHi_offset;
1004 Instr immlo = imm << ImmPCRelLo_offset;
1005 return (immhi & ImmPCRelHi_mask) | (immlo & ImmPCRelLo_mask);
1006 }
1007
1008
1009 Instr Assembler::ImmUncondBranch(int imm26) {
1010 CHECK(is_int26(imm26));
1011 return truncate_to_int26(imm26) << ImmUncondBranch_offset;
1012 }
1013
1014
1015 Instr Assembler::ImmCondBranch(int imm19) {
1016 CHECK(is_int19(imm19));
1017 return truncate_to_int19(imm19) << ImmCondBranch_offset;
1018 }
1019
1020
1021 Instr Assembler::ImmCmpBranch(int imm19) {
1022 CHECK(is_int19(imm19));
1023 return truncate_to_int19(imm19) << ImmCmpBranch_offset;
1024 }
1025
1026
1027 Instr Assembler::ImmTestBranch(int imm14) {
1028 CHECK(is_int14(imm14));
1029 return truncate_to_int14(imm14) << ImmTestBranch_offset;
1030 }
1031
1032
1033 Instr Assembler::ImmTestBranchBit(unsigned bit_pos) {
1034 ASSERT(is_uint6(bit_pos));
1035 // Subtract five from the shift offset, as we need bit 5 from bit_pos.
1036 unsigned b5 = bit_pos << (ImmTestBranchBit5_offset - 5);
1037 unsigned b40 = bit_pos << ImmTestBranchBit40_offset;
1038 b5 &= ImmTestBranchBit5_mask;
1039 b40 &= ImmTestBranchBit40_mask;
1040 return b5 | b40;
1041 }
1042
1043
1044 Instr Assembler::SF(Register rd) {
1045 return rd.Is64Bits() ? SixtyFourBits : ThirtyTwoBits;
1046 }
1047
1048
1049 Instr Assembler::ImmAddSub(int64_t imm) {
1050 ASSERT(IsImmAddSub(imm));
1051 if (is_uint12(imm)) { // No shift required.
1052 return imm << ImmAddSub_offset;
1053 } else {
1054 return ((imm >> 12) << ImmAddSub_offset) | (1 << ShiftAddSub_offset);
1055 }
1056 }
1057
1058
1059 Instr Assembler::ImmS(unsigned imms, unsigned reg_size) {
1060 ASSERT(((reg_size == kXRegSizeInBits) && is_uint6(imms)) ||
1061 ((reg_size == kWRegSizeInBits) && is_uint5(imms)));
1062 USE(reg_size);
1063 return imms << ImmS_offset;
1064 }
1065
1066
1067 Instr Assembler::ImmR(unsigned immr, unsigned reg_size) {
1068 ASSERT(((reg_size == kXRegSizeInBits) && is_uint6(immr)) ||
1069 ((reg_size == kWRegSizeInBits) && is_uint5(immr)));
1070 USE(reg_size);
1071 ASSERT(is_uint6(immr));
1072 return immr << ImmR_offset;
1073 }
1074
1075
1076 Instr Assembler::ImmSetBits(unsigned imms, unsigned reg_size) {
1077 ASSERT((reg_size == kWRegSizeInBits) || (reg_size == kXRegSizeInBits));
1078 ASSERT(is_uint6(imms));
1079 ASSERT((reg_size == kXRegSizeInBits) || is_uint6(imms + 3));
1080 USE(reg_size);
1081 return imms << ImmSetBits_offset;
1082 }
1083
1084
1085 Instr Assembler::ImmRotate(unsigned immr, unsigned reg_size) {
1086 ASSERT((reg_size == kWRegSizeInBits) || (reg_size == kXRegSizeInBits));
1087 ASSERT(((reg_size == kXRegSizeInBits) && is_uint6(immr)) ||
1088 ((reg_size == kWRegSizeInBits) && is_uint5(immr)));
1089 USE(reg_size);
1090 return immr << ImmRotate_offset;
1091 }
1092
1093
1094 Instr Assembler::ImmLLiteral(int imm19) {
1095 CHECK(is_int19(imm19));
1096 return truncate_to_int19(imm19) << ImmLLiteral_offset;
1097 }
1098
1099
1100 Instr Assembler::BitN(unsigned bitn, unsigned reg_size) {
1101 ASSERT((reg_size == kWRegSizeInBits) || (reg_size == kXRegSizeInBits));
1102 ASSERT((reg_size == kXRegSizeInBits) || (bitn == 0));
1103 USE(reg_size);
1104 return bitn << BitN_offset;
1105 }
1106
1107
1108 Instr Assembler::ShiftDP(Shift shift) {
1109 ASSERT(shift == LSL || shift == LSR || shift == ASR || shift == ROR);
1110 return shift << ShiftDP_offset;
1111 }
1112
1113
1114 Instr Assembler::ImmDPShift(unsigned amount) {
1115 ASSERT(is_uint6(amount));
1116 return amount << ImmDPShift_offset;
1117 }
1118
1119
1120 Instr Assembler::ExtendMode(Extend extend) {
1121 return extend << ExtendMode_offset;
1122 }
1123
1124
1125 Instr Assembler::ImmExtendShift(unsigned left_shift) {
1126 ASSERT(left_shift <= 4);
1127 return left_shift << ImmExtendShift_offset;
1128 }
1129
1130
1131 Instr Assembler::ImmCondCmp(unsigned imm) {
1132 ASSERT(is_uint5(imm));
1133 return imm << ImmCondCmp_offset;
1134 }
1135
1136
1137 Instr Assembler::Nzcv(StatusFlags nzcv) {
1138 return ((nzcv >> Flags_offset) & 0xf) << Nzcv_offset;
1139 }
1140
1141
1142 Instr Assembler::ImmLSUnsigned(int imm12) {
1143 ASSERT(is_uint12(imm12));
1144 return imm12 << ImmLSUnsigned_offset;
1145 }
1146
1147
1148 Instr Assembler::ImmLS(int imm9) {
1149 ASSERT(is_int9(imm9));
1150 return truncate_to_int9(imm9) << ImmLS_offset;
1151 }
1152
1153
1154 Instr Assembler::ImmLSPair(int imm7, LSDataSize size) {
1155 ASSERT(((imm7 >> size) << size) == imm7);
1156 int scaled_imm7 = imm7 >> size;
1157 ASSERT(is_int7(scaled_imm7));
1158 return truncate_to_int7(scaled_imm7) << ImmLSPair_offset;
1159 }
1160
1161
1162 Instr Assembler::ImmShiftLS(unsigned shift_amount) {
1163 ASSERT(is_uint1(shift_amount));
1164 return shift_amount << ImmShiftLS_offset;
1165 }
1166
1167
1168 Instr Assembler::ImmException(int imm16) {
1169 ASSERT(is_uint16(imm16));
1170 return imm16 << ImmException_offset;
1171 }
1172
1173
1174 Instr Assembler::ImmSystemRegister(int imm15) {
1175 ASSERT(is_uint15(imm15));
1176 return imm15 << ImmSystemRegister_offset;
1177 }
1178
1179
1180 Instr Assembler::ImmHint(int imm7) {
1181 ASSERT(is_uint7(imm7));
1182 return imm7 << ImmHint_offset;
1183 }
1184
1185
1186 Instr Assembler::ImmBarrierDomain(int imm2) {
1187 ASSERT(is_uint2(imm2));
1188 return imm2 << ImmBarrierDomain_offset;
1189 }
1190
1191
1192 Instr Assembler::ImmBarrierType(int imm2) {
1193 ASSERT(is_uint2(imm2));
1194 return imm2 << ImmBarrierType_offset;
1195 }
1196
1197
1198 LSDataSize Assembler::CalcLSDataSize(LoadStoreOp op) {
1199 ASSERT((SizeLS_offset + SizeLS_width) == (kInstructionSize * 8));
1200 return static_cast<LSDataSize>(op >> SizeLS_offset);
1201 }
1202
1203
1204 Instr Assembler::ImmMoveWide(uint64_t imm) {
1205 ASSERT(is_uint16(imm));
1206 return imm << ImmMoveWide_offset;
1207 }
1208
1209
1210 Instr Assembler::ShiftMoveWide(int64_t shift) {
1211 ASSERT(is_uint2(shift));
1212 return shift << ShiftMoveWide_offset;
1213 }
1214
1215
1216 Instr Assembler::FPType(FPRegister fd) {
1217 return fd.Is64Bits() ? FP64 : FP32;
1218 }
1219
1220
1221 Instr Assembler::FPScale(unsigned scale) {
1222 ASSERT(is_uint6(scale));
1223 return scale << FPScale_offset;
1224 }
1225
1226
1227 const Register& Assembler::AppropriateZeroRegFor(const CPURegister& reg) const {
1228 return reg.Is64Bits() ? xzr : wzr;
1229 }
1230
1231
1232 inline void Assembler::CheckBufferSpace() {
1233 ASSERT(pc_ < (buffer_ + buffer_size_));
1234 if (buffer_space() < kGap) {
1235 GrowBuffer();
1236 }
1237 }
1238
1239
1240 inline void Assembler::CheckBuffer() {
1241 CheckBufferSpace();
1242 if (pc_offset() >= next_veneer_pool_check_) {
1243 CheckVeneerPool(false, true);
1244 }
1245 if (pc_offset() >= next_constant_pool_check_) {
1246 CheckConstPool(false, true);
1247 }
1248 }
1249
1250
1251 TypeFeedbackId Assembler::RecordedAstId() {
1252 ASSERT(!recorded_ast_id_.IsNone());
1253 return recorded_ast_id_;
1254 }
1255
1256
1257 void Assembler::ClearRecordedAstId() {
1258 recorded_ast_id_ = TypeFeedbackId::None();
1259 }
1260
1261
1262 } } // namespace v8::internal
1263
1264 #endif // V8_ARM64_ASSEMBLER_ARM64_INL_H_
1265