• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_INSTRUCTIONS_ARM64_H_
6 #define V8_ARM64_INSTRUCTIONS_ARM64_H_
7 
8 #include "src/arm64/constants-arm64.h"
9 #include "src/arm64/utils-arm64.h"
10 #include "src/globals.h"
11 #include "src/utils.h"
12 
13 namespace v8 {
14 namespace internal {
15 
16 
17 // ISA constants. --------------------------------------------------------------
18 
19 typedef uint32_t Instr;
20 
21 // The following macros initialize a float/double variable with a bit pattern
22 // without using static initializers: If ARM64_DEFINE_FP_STATICS is defined, the
23 // symbol is defined as uint32_t/uint64_t initialized with the desired bit
24 // pattern. Otherwise, the same symbol is declared as an external float/double.
25 #if defined(ARM64_DEFINE_FP_STATICS)
26 #define DEFINE_FLOAT(name, value) extern const uint32_t name = value
27 #define DEFINE_DOUBLE(name, value) extern const uint64_t name = value
28 #else
29 #define DEFINE_FLOAT(name, value) extern const float name
30 #define DEFINE_DOUBLE(name, value) extern const double name
31 #endif  // defined(ARM64_DEFINE_FP_STATICS)
32 
33 DEFINE_FLOAT(kFP32PositiveInfinity, 0x7f800000);
34 DEFINE_FLOAT(kFP32NegativeInfinity, 0xff800000);
35 DEFINE_DOUBLE(kFP64PositiveInfinity, 0x7ff0000000000000UL);
36 DEFINE_DOUBLE(kFP64NegativeInfinity, 0xfff0000000000000UL);
37 
38 // This value is a signalling NaN as both a double and as a float (taking the
39 // least-significant word).
40 DEFINE_DOUBLE(kFP64SignallingNaN, 0x7ff000007f800001);
41 DEFINE_FLOAT(kFP32SignallingNaN, 0x7f800001);
42 
43 // A similar value, but as a quiet NaN.
44 DEFINE_DOUBLE(kFP64QuietNaN, 0x7ff800007fc00001);
45 DEFINE_FLOAT(kFP32QuietNaN, 0x7fc00001);
46 
47 // The default NaN values (for FPCR.DN=1).
48 DEFINE_DOUBLE(kFP64DefaultNaN, 0x7ff8000000000000UL);
49 DEFINE_FLOAT(kFP32DefaultNaN, 0x7fc00000);
50 
51 #undef DEFINE_FLOAT
52 #undef DEFINE_DOUBLE
53 
54 
55 enum LSDataSize {
56   LSByte        = 0,
57   LSHalfword    = 1,
58   LSWord        = 2,
59   LSDoubleWord  = 3
60 };
61 
62 LSDataSize CalcLSPairDataSize(LoadStorePairOp op);
63 
64 enum ImmBranchType {
65   UnknownBranchType = 0,
66   CondBranchType    = 1,
67   UncondBranchType  = 2,
68   CompareBranchType = 3,
69   TestBranchType    = 4
70 };
71 
72 enum AddrMode {
73   Offset,
74   PreIndex,
75   PostIndex
76 };
77 
78 enum FPRounding {
79   // The first four values are encodable directly by FPCR<RMode>.
80   FPTieEven = 0x0,
81   FPPositiveInfinity = 0x1,
82   FPNegativeInfinity = 0x2,
83   FPZero = 0x3,
84 
85   // The final rounding mode is only available when explicitly specified by the
86   // instruction (such as with fcvta). It cannot be set in FPCR.
87   FPTieAway
88 };
89 
90 enum Reg31Mode {
91   Reg31IsStackPointer,
92   Reg31IsZeroRegister
93 };
94 
95 // Instructions. ---------------------------------------------------------------
96 
97 class Instruction {
98  public:
InstructionBits()99   V8_INLINE Instr InstructionBits() const {
100     return *reinterpret_cast<const Instr*>(this);
101   }
102 
SetInstructionBits(Instr new_instr)103   V8_INLINE void SetInstructionBits(Instr new_instr) {
104     *reinterpret_cast<Instr*>(this) = new_instr;
105   }
106 
Bit(int pos)107   int Bit(int pos) const {
108     return (InstructionBits() >> pos) & 1;
109   }
110 
Bits(int msb,int lsb)111   uint32_t Bits(int msb, int lsb) const {
112     return unsigned_bitextract_32(msb, lsb, InstructionBits());
113   }
114 
SignedBits(int msb,int lsb)115   int32_t SignedBits(int msb, int lsb) const {
116     int32_t bits = *(reinterpret_cast<const int32_t*>(this));
117     return signed_bitextract_32(msb, lsb, bits);
118   }
119 
Mask(uint32_t mask)120   Instr Mask(uint32_t mask) const {
121     return InstructionBits() & mask;
122   }
123 
124   V8_INLINE const Instruction* following(int count = 1) const {
125     return InstructionAtOffset(count * static_cast<int>(kInstructionSize));
126   }
127 
128   V8_INLINE Instruction* following(int count = 1) {
129     return InstructionAtOffset(count * static_cast<int>(kInstructionSize));
130   }
131 
132   V8_INLINE const Instruction* preceding(int count = 1) const {
133     return following(-count);
134   }
135 
136   V8_INLINE Instruction* preceding(int count = 1) {
137     return following(-count);
138   }
139 
140 #define DEFINE_GETTER(Name, HighBit, LowBit, Func) \
141   int32_t Name() const { return Func(HighBit, LowBit); }
INSTRUCTION_FIELDS_LIST(DEFINE_GETTER)142   INSTRUCTION_FIELDS_LIST(DEFINE_GETTER)
143   #undef DEFINE_GETTER
144 
145   // ImmPCRel is a compound field (not present in INSTRUCTION_FIELDS_LIST),
146   // formed from ImmPCRelLo and ImmPCRelHi.
147   int ImmPCRel() const {
148     DCHECK(IsPCRelAddressing());
149     int offset = ((ImmPCRelHi() << ImmPCRelLo_width) | ImmPCRelLo());
150     int width = ImmPCRelLo_width + ImmPCRelHi_width;
151     return signed_bitextract_32(width - 1, 0, offset);
152   }
153 
154   uint64_t ImmLogical();
155   float ImmFP32();
156   double ImmFP64();
157 
SizeLSPair()158   LSDataSize SizeLSPair() const {
159     return CalcLSPairDataSize(
160         static_cast<LoadStorePairOp>(Mask(LoadStorePairMask)));
161   }
162 
163   // Helpers.
IsCondBranchImm()164   bool IsCondBranchImm() const {
165     return Mask(ConditionalBranchFMask) == ConditionalBranchFixed;
166   }
167 
IsUncondBranchImm()168   bool IsUncondBranchImm() const {
169     return Mask(UnconditionalBranchFMask) == UnconditionalBranchFixed;
170   }
171 
IsCompareBranch()172   bool IsCompareBranch() const {
173     return Mask(CompareBranchFMask) == CompareBranchFixed;
174   }
175 
IsTestBranch()176   bool IsTestBranch() const {
177     return Mask(TestBranchFMask) == TestBranchFixed;
178   }
179 
IsImmBranch()180   bool IsImmBranch() const {
181     return BranchType() != UnknownBranchType;
182   }
183 
IsLdrLiteral()184   bool IsLdrLiteral() const {
185     return Mask(LoadLiteralFMask) == LoadLiteralFixed;
186   }
187 
IsLdrLiteralX()188   bool IsLdrLiteralX() const {
189     return Mask(LoadLiteralMask) == LDR_x_lit;
190   }
191 
IsPCRelAddressing()192   bool IsPCRelAddressing() const {
193     return Mask(PCRelAddressingFMask) == PCRelAddressingFixed;
194   }
195 
IsAdr()196   bool IsAdr() const {
197     return Mask(PCRelAddressingMask) == ADR;
198   }
199 
IsBrk()200   bool IsBrk() const { return Mask(ExceptionMask) == BRK; }
201 
IsUnresolvedInternalReference()202   bool IsUnresolvedInternalReference() const {
203     // Unresolved internal references are encoded as two consecutive brk
204     // instructions.
205     return IsBrk() && following()->IsBrk();
206   }
207 
IsLogicalImmediate()208   bool IsLogicalImmediate() const {
209     return Mask(LogicalImmediateFMask) == LogicalImmediateFixed;
210   }
211 
IsAddSubImmediate()212   bool IsAddSubImmediate() const {
213     return Mask(AddSubImmediateFMask) == AddSubImmediateFixed;
214   }
215 
IsAddSubShifted()216   bool IsAddSubShifted() const {
217     return Mask(AddSubShiftedFMask) == AddSubShiftedFixed;
218   }
219 
IsAddSubExtended()220   bool IsAddSubExtended() const {
221     return Mask(AddSubExtendedFMask) == AddSubExtendedFixed;
222   }
223 
224   // Match any loads or stores, including pairs.
IsLoadOrStore()225   bool IsLoadOrStore() const {
226     return Mask(LoadStoreAnyFMask) == LoadStoreAnyFixed;
227   }
228 
229   // Match any loads, including pairs.
230   bool IsLoad() const;
231   // Match any stores, including pairs.
232   bool IsStore() const;
233 
234   // Indicate whether Rd can be the stack pointer or the zero register. This
235   // does not check that the instruction actually has an Rd field.
RdMode()236   Reg31Mode RdMode() const {
237     // The following instructions use csp or wsp as Rd:
238     //  Add/sub (immediate) when not setting the flags.
239     //  Add/sub (extended) when not setting the flags.
240     //  Logical (immediate) when not setting the flags.
241     // Otherwise, r31 is the zero register.
242     if (IsAddSubImmediate() || IsAddSubExtended()) {
243       if (Mask(AddSubSetFlagsBit)) {
244         return Reg31IsZeroRegister;
245       } else {
246         return Reg31IsStackPointer;
247       }
248     }
249     if (IsLogicalImmediate()) {
250       // Of the logical (immediate) instructions, only ANDS (and its aliases)
251       // can set the flags. The others can all write into csp.
252       // Note that some logical operations are not available to
253       // immediate-operand instructions, so we have to combine two masks here.
254       if (Mask(LogicalImmediateMask & LogicalOpMask) == ANDS) {
255         return Reg31IsZeroRegister;
256       } else {
257         return Reg31IsStackPointer;
258       }
259     }
260     return Reg31IsZeroRegister;
261   }
262 
263   // Indicate whether Rn can be the stack pointer or the zero register. This
264   // does not check that the instruction actually has an Rn field.
RnMode()265   Reg31Mode RnMode() const {
266     // The following instructions use csp or wsp as Rn:
267     //  All loads and stores.
268     //  Add/sub (immediate).
269     //  Add/sub (extended).
270     // Otherwise, r31 is the zero register.
271     if (IsLoadOrStore() || IsAddSubImmediate() || IsAddSubExtended()) {
272       return Reg31IsStackPointer;
273     }
274     return Reg31IsZeroRegister;
275   }
276 
BranchType()277   ImmBranchType BranchType() const {
278     if (IsCondBranchImm()) {
279       return CondBranchType;
280     } else if (IsUncondBranchImm()) {
281       return UncondBranchType;
282     } else if (IsCompareBranch()) {
283       return CompareBranchType;
284     } else if (IsTestBranch()) {
285       return TestBranchType;
286     } else {
287       return UnknownBranchType;
288     }
289   }
290 
ImmBranchRangeBitwidth(ImmBranchType branch_type)291   static int ImmBranchRangeBitwidth(ImmBranchType branch_type) {
292     switch (branch_type) {
293       case UncondBranchType:
294         return ImmUncondBranch_width;
295       case CondBranchType:
296         return ImmCondBranch_width;
297       case CompareBranchType:
298         return ImmCmpBranch_width;
299       case TestBranchType:
300         return ImmTestBranch_width;
301       default:
302         UNREACHABLE();
303         return 0;
304     }
305   }
306 
307   // The range of the branch instruction, expressed as 'instr +- range'.
ImmBranchRange(ImmBranchType branch_type)308   static int32_t ImmBranchRange(ImmBranchType branch_type) {
309     return
310       (1 << (ImmBranchRangeBitwidth(branch_type) + kInstructionSizeLog2)) / 2 -
311       kInstructionSize;
312   }
313 
ImmBranch()314   int ImmBranch() const {
315     switch (BranchType()) {
316       case CondBranchType: return ImmCondBranch();
317       case UncondBranchType: return ImmUncondBranch();
318       case CompareBranchType: return ImmCmpBranch();
319       case TestBranchType: return ImmTestBranch();
320       default: UNREACHABLE();
321     }
322     return 0;
323   }
324 
ImmUnresolvedInternalReference()325   int ImmUnresolvedInternalReference() const {
326     DCHECK(IsUnresolvedInternalReference());
327     // Unresolved references are encoded as two consecutive brk instructions.
328     // The associated immediate is made of the two 16-bit payloads.
329     int32_t high16 = ImmException();
330     int32_t low16 = following()->ImmException();
331     return (high16 << 16) | low16;
332   }
333 
IsBranchAndLinkToRegister()334   bool IsBranchAndLinkToRegister() const {
335     return Mask(UnconditionalBranchToRegisterMask) == BLR;
336   }
337 
IsMovz()338   bool IsMovz() const {
339     return (Mask(MoveWideImmediateMask) == MOVZ_x) ||
340            (Mask(MoveWideImmediateMask) == MOVZ_w);
341   }
342 
IsMovk()343   bool IsMovk() const {
344     return (Mask(MoveWideImmediateMask) == MOVK_x) ||
345            (Mask(MoveWideImmediateMask) == MOVK_w);
346   }
347 
IsMovn()348   bool IsMovn() const {
349     return (Mask(MoveWideImmediateMask) == MOVN_x) ||
350            (Mask(MoveWideImmediateMask) == MOVN_w);
351   }
352 
IsNop(int n)353   bool IsNop(int n) {
354     // A marking nop is an instruction
355     //   mov r<n>,  r<n>
356     // which is encoded as
357     //   orr r<n>, xzr, r<n>
358     return (Mask(LogicalShiftedMask) == ORR_x) &&
359            (Rd() == Rm()) &&
360            (Rd() == n);
361   }
362 
363   // Find the PC offset encoded in this instruction. 'this' may be a branch or
364   // a PC-relative addressing instruction.
365   // The offset returned is unscaled.
366   int64_t ImmPCOffset();
367 
368   // Find the target of this instruction. 'this' may be a branch or a
369   // PC-relative addressing instruction.
370   Instruction* ImmPCOffsetTarget();
371 
372   static bool IsValidImmPCOffset(ImmBranchType branch_type, ptrdiff_t offset);
373   bool IsTargetInImmPCOffsetRange(Instruction* target);
374   // Patch a PC-relative offset to refer to 'target'. 'this' may be a branch or
375   // a PC-relative addressing instruction.
376   void SetImmPCOffsetTarget(Isolate* isolate, Instruction* target);
377   void SetUnresolvedInternalReferenceImmTarget(Isolate* isolate,
378                                                Instruction* target);
379   // Patch a literal load instruction to load from 'source'.
380   void SetImmLLiteral(Instruction* source);
381 
LiteralAddress()382   uintptr_t LiteralAddress() {
383     int offset = ImmLLiteral() << kLoadLiteralScaleLog2;
384     return reinterpret_cast<uintptr_t>(this) + offset;
385   }
386 
387   enum CheckAlignment { NO_CHECK, CHECK_ALIGNMENT };
388 
389   V8_INLINE const Instruction* InstructionAtOffset(
390       int64_t offset, CheckAlignment check = CHECK_ALIGNMENT) const {
391     // The FUZZ_disasm test relies on no check being done.
392     DCHECK(check == NO_CHECK || IsAligned(offset, kInstructionSize));
393     return this + offset;
394   }
395 
396   V8_INLINE Instruction* InstructionAtOffset(
397       int64_t offset, CheckAlignment check = CHECK_ALIGNMENT) {
398     // The FUZZ_disasm test relies on no check being done.
399     DCHECK(check == NO_CHECK || IsAligned(offset, kInstructionSize));
400     return this + offset;
401   }
402 
Cast(T src)403   template<typename T> V8_INLINE static Instruction* Cast(T src) {
404     return reinterpret_cast<Instruction*>(src);
405   }
406 
DistanceTo(Instruction * target)407   V8_INLINE ptrdiff_t DistanceTo(Instruction* target) {
408     return reinterpret_cast<Address>(target) - reinterpret_cast<Address>(this);
409   }
410 
411 
412   static const int ImmPCRelRangeBitwidth = 21;
IsValidPCRelOffset(ptrdiff_t offset)413   static bool IsValidPCRelOffset(ptrdiff_t offset) { return is_int21(offset); }
414   void SetPCRelImmTarget(Isolate* isolate, Instruction* target);
415   void SetBranchImmTarget(Instruction* target);
416 };
417 
418 
419 // Where Instruction looks at instructions generated by the Assembler,
420 // InstructionSequence looks at instructions sequences generated by the
421 // MacroAssembler.
422 class InstructionSequence : public Instruction {
423  public:
At(Address address)424   static InstructionSequence* At(Address address) {
425     return reinterpret_cast<InstructionSequence*>(address);
426   }
427 
428   // Sequences generated by MacroAssembler::InlineData().
429   bool IsInlineData() const;
430   uint64_t InlineData() const;
431 };
432 
433 
434 // Simulator/Debugger debug instructions ---------------------------------------
435 // Each debug marker is represented by a HLT instruction. The immediate comment
436 // field in the instruction is used to identify the type of debug marker. Each
437 // marker encodes arguments in a different way, as described below.
438 
439 // Indicate to the Debugger that the instruction is a redirected call.
440 const Instr kImmExceptionIsRedirectedCall = 0xca11;
441 
442 // Represent unreachable code. This is used as a guard in parts of the code that
443 // should not be reachable, such as in data encoded inline in the instructions.
444 const Instr kImmExceptionIsUnreachable = 0xdebf;
445 
446 // A pseudo 'printf' instruction. The arguments will be passed to the platform
447 // printf method.
448 const Instr kImmExceptionIsPrintf = 0xdeb1;
449 // Most parameters are stored in ARM64 registers as if the printf
450 // pseudo-instruction was a call to the real printf method:
451 //      x0: The format string.
452 //   x1-x7: Optional arguments.
453 //   d0-d7: Optional arguments.
454 //
455 // Also, the argument layout is described inline in the instructions:
456 //  - arg_count: The number of arguments.
457 //  - arg_pattern: A set of PrintfArgPattern values, packed into two-bit fields.
458 //
459 // Floating-point and integer arguments are passed in separate sets of registers
460 // in AAPCS64 (even for varargs functions), so it is not possible to determine
461 // the type of each argument without some information about the values that were
462 // passed in. This information could be retrieved from the printf format string,
463 // but the format string is not trivial to parse so we encode the relevant
464 // information with the HLT instruction.
465 const unsigned kPrintfArgCountOffset = 1 * kInstructionSize;
466 const unsigned kPrintfArgPatternListOffset = 2 * kInstructionSize;
467 const unsigned kPrintfLength = 3 * kInstructionSize;
468 
469 const unsigned kPrintfMaxArgCount = 4;
470 
471 // The argument pattern is a set of two-bit-fields, each with one of the
472 // following values:
473 enum PrintfArgPattern {
474   kPrintfArgW = 1,
475   kPrintfArgX = 2,
476   // There is no kPrintfArgS because floats are always converted to doubles in C
477   // varargs calls.
478   kPrintfArgD = 3
479 };
480 static const unsigned kPrintfArgPatternBits = 2;
481 
482 // A pseudo 'debug' instruction.
483 const Instr kImmExceptionIsDebug = 0xdeb0;
484 // Parameters are inlined in the code after a debug pseudo-instruction:
485 // - Debug code.
486 // - Debug parameters.
487 // - Debug message string. This is a NULL-terminated ASCII string, padded to
488 //   kInstructionSize so that subsequent instructions are correctly aligned.
489 // - A kImmExceptionIsUnreachable marker, to catch accidental execution of the
490 //   string data.
491 const unsigned kDebugCodeOffset = 1 * kInstructionSize;
492 const unsigned kDebugParamsOffset = 2 * kInstructionSize;
493 const unsigned kDebugMessageOffset = 3 * kInstructionSize;
494 
495 // Debug parameters.
496 // Used without a TRACE_ option, the Debugger will print the arguments only
497 // once. Otherwise TRACE_ENABLE and TRACE_DISABLE will enable or disable tracing
498 // before every instruction for the specified LOG_ parameters.
499 //
500 // TRACE_OVERRIDE enables the specified LOG_ parameters, and disabled any
501 // others that were not specified.
502 //
503 // For example:
504 //
505 // __ debug("print registers and fp registers", 0, LOG_REGS | LOG_FP_REGS);
506 // will print the registers and fp registers only once.
507 //
508 // __ debug("trace disasm", 1, TRACE_ENABLE | LOG_DISASM);
509 // starts disassembling the code.
510 //
511 // __ debug("trace rets", 2, TRACE_ENABLE | LOG_REGS);
512 // adds the general purpose registers to the trace.
513 //
514 // __ debug("stop regs", 3, TRACE_DISABLE | LOG_REGS);
515 // stops tracing the registers.
516 const unsigned kDebuggerTracingDirectivesMask = 3 << 6;
517 enum DebugParameters {
518   NO_PARAM       = 0,
519   BREAK          = 1 << 0,
520   LOG_DISASM     = 1 << 1,  // Use only with TRACE. Disassemble the code.
521   LOG_REGS       = 1 << 2,  // Log general purpose registers.
522   LOG_FP_REGS    = 1 << 3,  // Log floating-point registers.
523   LOG_SYS_REGS   = 1 << 4,  // Log the status flags.
524   LOG_WRITE      = 1 << 5,  // Log any memory write.
525 
526   LOG_STATE      = LOG_REGS | LOG_FP_REGS | LOG_SYS_REGS,
527   LOG_ALL        = LOG_DISASM | LOG_STATE | LOG_WRITE,
528 
529   // Trace control.
530   TRACE_ENABLE   = 1 << 6,
531   TRACE_DISABLE  = 2 << 6,
532   TRACE_OVERRIDE = 3 << 6
533 };
534 
535 
536 }  // namespace internal
537 }  // namespace v8
538 
539 
540 #endif  // V8_ARM64_INSTRUCTIONS_ARM64_H_
541