• 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/globals.h"
9 #include "src/utils.h"
10 #include "src/arm64/constants-arm64.h"
11 #include "src/arm64/utils-arm64.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 Instruction* following(int count = 1) {
125     return InstructionAtOffset(count * static_cast<int>(kInstructionSize));
126   }
127 
128   V8_INLINE Instruction* preceding(int count = 1) {
129     return following(-count);
130   }
131 
132   #define DEFINE_GETTER(Name, HighBit, LowBit, Func)             \
133   int64_t Name() const { return Func(HighBit, LowBit); }
INSTRUCTION_FIELDS_LIST(DEFINE_GETTER)134   INSTRUCTION_FIELDS_LIST(DEFINE_GETTER)
135   #undef DEFINE_GETTER
136 
137   // ImmPCRel is a compound field (not present in INSTRUCTION_FIELDS_LIST),
138   // formed from ImmPCRelLo and ImmPCRelHi.
139   int ImmPCRel() const {
140     ASSERT(IsPCRelAddressing());
141     int const offset = ((ImmPCRelHi() << ImmPCRelLo_width) | ImmPCRelLo());
142     int const width = ImmPCRelLo_width + ImmPCRelHi_width;
143     return signed_bitextract_32(width - 1, 0, offset);
144   }
145 
146   uint64_t ImmLogical();
147   float ImmFP32();
148   double ImmFP64();
149 
SizeLSPair()150   LSDataSize SizeLSPair() const {
151     return CalcLSPairDataSize(
152         static_cast<LoadStorePairOp>(Mask(LoadStorePairMask)));
153   }
154 
155   // Helpers.
IsCondBranchImm()156   bool IsCondBranchImm() const {
157     return Mask(ConditionalBranchFMask) == ConditionalBranchFixed;
158   }
159 
IsUncondBranchImm()160   bool IsUncondBranchImm() const {
161     return Mask(UnconditionalBranchFMask) == UnconditionalBranchFixed;
162   }
163 
IsCompareBranch()164   bool IsCompareBranch() const {
165     return Mask(CompareBranchFMask) == CompareBranchFixed;
166   }
167 
IsTestBranch()168   bool IsTestBranch() const {
169     return Mask(TestBranchFMask) == TestBranchFixed;
170   }
171 
IsImmBranch()172   bool IsImmBranch() const {
173     return BranchType() != UnknownBranchType;
174   }
175 
IsLdrLiteral()176   bool IsLdrLiteral() const {
177     return Mask(LoadLiteralFMask) == LoadLiteralFixed;
178   }
179 
IsLdrLiteralX()180   bool IsLdrLiteralX() const {
181     return Mask(LoadLiteralMask) == LDR_x_lit;
182   }
183 
IsPCRelAddressing()184   bool IsPCRelAddressing() const {
185     return Mask(PCRelAddressingFMask) == PCRelAddressingFixed;
186   }
187 
IsAdr()188   bool IsAdr() const {
189     return Mask(PCRelAddressingMask) == ADR;
190   }
191 
IsLogicalImmediate()192   bool IsLogicalImmediate() const {
193     return Mask(LogicalImmediateFMask) == LogicalImmediateFixed;
194   }
195 
IsAddSubImmediate()196   bool IsAddSubImmediate() const {
197     return Mask(AddSubImmediateFMask) == AddSubImmediateFixed;
198   }
199 
IsAddSubShifted()200   bool IsAddSubShifted() const {
201     return Mask(AddSubShiftedFMask) == AddSubShiftedFixed;
202   }
203 
IsAddSubExtended()204   bool IsAddSubExtended() const {
205     return Mask(AddSubExtendedFMask) == AddSubExtendedFixed;
206   }
207 
208   // Match any loads or stores, including pairs.
IsLoadOrStore()209   bool IsLoadOrStore() const {
210     return Mask(LoadStoreAnyFMask) == LoadStoreAnyFixed;
211   }
212 
213   // Match any loads, including pairs.
214   bool IsLoad() const;
215   // Match any stores, including pairs.
216   bool IsStore() const;
217 
218   // Indicate whether Rd can be the stack pointer or the zero register. This
219   // does not check that the instruction actually has an Rd field.
RdMode()220   Reg31Mode RdMode() const {
221     // The following instructions use csp or wsp as Rd:
222     //  Add/sub (immediate) when not setting the flags.
223     //  Add/sub (extended) when not setting the flags.
224     //  Logical (immediate) when not setting the flags.
225     // Otherwise, r31 is the zero register.
226     if (IsAddSubImmediate() || IsAddSubExtended()) {
227       if (Mask(AddSubSetFlagsBit)) {
228         return Reg31IsZeroRegister;
229       } else {
230         return Reg31IsStackPointer;
231       }
232     }
233     if (IsLogicalImmediate()) {
234       // Of the logical (immediate) instructions, only ANDS (and its aliases)
235       // can set the flags. The others can all write into csp.
236       // Note that some logical operations are not available to
237       // immediate-operand instructions, so we have to combine two masks here.
238       if (Mask(LogicalImmediateMask & LogicalOpMask) == ANDS) {
239         return Reg31IsZeroRegister;
240       } else {
241         return Reg31IsStackPointer;
242       }
243     }
244     return Reg31IsZeroRegister;
245   }
246 
247   // Indicate whether Rn can be the stack pointer or the zero register. This
248   // does not check that the instruction actually has an Rn field.
RnMode()249   Reg31Mode RnMode() const {
250     // The following instructions use csp or wsp as Rn:
251     //  All loads and stores.
252     //  Add/sub (immediate).
253     //  Add/sub (extended).
254     // Otherwise, r31 is the zero register.
255     if (IsLoadOrStore() || IsAddSubImmediate() || IsAddSubExtended()) {
256       return Reg31IsStackPointer;
257     }
258     return Reg31IsZeroRegister;
259   }
260 
BranchType()261   ImmBranchType BranchType() const {
262     if (IsCondBranchImm()) {
263       return CondBranchType;
264     } else if (IsUncondBranchImm()) {
265       return UncondBranchType;
266     } else if (IsCompareBranch()) {
267       return CompareBranchType;
268     } else if (IsTestBranch()) {
269       return TestBranchType;
270     } else {
271       return UnknownBranchType;
272     }
273   }
274 
ImmBranchRangeBitwidth(ImmBranchType branch_type)275   static int ImmBranchRangeBitwidth(ImmBranchType branch_type) {
276     switch (branch_type) {
277       case UncondBranchType:
278         return ImmUncondBranch_width;
279       case CondBranchType:
280         return ImmCondBranch_width;
281       case CompareBranchType:
282         return ImmCmpBranch_width;
283       case TestBranchType:
284         return ImmTestBranch_width;
285       default:
286         UNREACHABLE();
287         return 0;
288     }
289   }
290 
291   // The range of the branch instruction, expressed as 'instr +- range'.
ImmBranchRange(ImmBranchType branch_type)292   static int32_t ImmBranchRange(ImmBranchType branch_type) {
293     return
294       (1 << (ImmBranchRangeBitwidth(branch_type) + kInstructionSizeLog2)) / 2 -
295       kInstructionSize;
296   }
297 
ImmBranch()298   int ImmBranch() const {
299     switch (BranchType()) {
300       case CondBranchType: return ImmCondBranch();
301       case UncondBranchType: return ImmUncondBranch();
302       case CompareBranchType: return ImmCmpBranch();
303       case TestBranchType: return ImmTestBranch();
304       default: UNREACHABLE();
305     }
306     return 0;
307   }
308 
IsBranchAndLinkToRegister()309   bool IsBranchAndLinkToRegister() const {
310     return Mask(UnconditionalBranchToRegisterMask) == BLR;
311   }
312 
IsMovz()313   bool IsMovz() const {
314     return (Mask(MoveWideImmediateMask) == MOVZ_x) ||
315            (Mask(MoveWideImmediateMask) == MOVZ_w);
316   }
317 
IsMovk()318   bool IsMovk() const {
319     return (Mask(MoveWideImmediateMask) == MOVK_x) ||
320            (Mask(MoveWideImmediateMask) == MOVK_w);
321   }
322 
IsMovn()323   bool IsMovn() const {
324     return (Mask(MoveWideImmediateMask) == MOVN_x) ||
325            (Mask(MoveWideImmediateMask) == MOVN_w);
326   }
327 
IsNop(int n)328   bool IsNop(int n) {
329     // A marking nop is an instruction
330     //   mov r<n>,  r<n>
331     // which is encoded as
332     //   orr r<n>, xzr, r<n>
333     return (Mask(LogicalShiftedMask) == ORR_x) &&
334            (Rd() == Rm()) &&
335            (Rd() == n);
336   }
337 
338   // Find the PC offset encoded in this instruction. 'this' may be a branch or
339   // a PC-relative addressing instruction.
340   // The offset returned is unscaled.
341   ptrdiff_t ImmPCOffset();
342 
343   // Find the target of this instruction. 'this' may be a branch or a
344   // PC-relative addressing instruction.
345   Instruction* ImmPCOffsetTarget();
346 
347   static bool IsValidImmPCOffset(ImmBranchType branch_type, int32_t offset);
348   bool IsTargetInImmPCOffsetRange(Instruction* target);
349   // Patch a PC-relative offset to refer to 'target'. 'this' may be a branch or
350   // a PC-relative addressing instruction.
351   void SetImmPCOffsetTarget(Instruction* target);
352   // Patch a literal load instruction to load from 'source'.
353   void SetImmLLiteral(Instruction* source);
354 
LiteralAddress()355   uint8_t* LiteralAddress() {
356     int offset = ImmLLiteral() << kLoadLiteralScaleLog2;
357     return reinterpret_cast<uint8_t*>(this) + offset;
358   }
359 
360   enum CheckAlignment { NO_CHECK, CHECK_ALIGNMENT };
361 
362   V8_INLINE Instruction* InstructionAtOffset(
363       int64_t offset,
364       CheckAlignment check = CHECK_ALIGNMENT) {
365     Address addr = reinterpret_cast<Address>(this) + offset;
366     // The FUZZ_disasm test relies on no check being done.
367     ASSERT(check == NO_CHECK || IsAddressAligned(addr, kInstructionSize));
368     return Cast(addr);
369   }
370 
Cast(T src)371   template<typename T> V8_INLINE static Instruction* Cast(T src) {
372     return reinterpret_cast<Instruction*>(src);
373   }
374 
DistanceTo(Instruction * target)375   V8_INLINE ptrdiff_t DistanceTo(Instruction* target) {
376     return reinterpret_cast<Address>(target) - reinterpret_cast<Address>(this);
377   }
378 
379 
380   static const int ImmPCRelRangeBitwidth = 21;
IsValidPCRelOffset(int offset)381   static bool IsValidPCRelOffset(int offset) {
382     return is_int21(offset);
383   }
384   void SetPCRelImmTarget(Instruction* target);
385   void SetBranchImmTarget(Instruction* target);
386 };
387 
388 
389 // Where Instruction looks at instructions generated by the Assembler,
390 // InstructionSequence looks at instructions sequences generated by the
391 // MacroAssembler.
392 class InstructionSequence : public Instruction {
393  public:
At(Address address)394   static InstructionSequence* At(Address address) {
395     return reinterpret_cast<InstructionSequence*>(address);
396   }
397 
398   // Sequences generated by MacroAssembler::InlineData().
399   bool IsInlineData() const;
400   uint64_t InlineData() const;
401 };
402 
403 
404 // Simulator/Debugger debug instructions ---------------------------------------
405 // Each debug marker is represented by a HLT instruction. The immediate comment
406 // field in the instruction is used to identify the type of debug marker. Each
407 // marker encodes arguments in a different way, as described below.
408 
409 // Indicate to the Debugger that the instruction is a redirected call.
410 const Instr kImmExceptionIsRedirectedCall = 0xca11;
411 
412 // Represent unreachable code. This is used as a guard in parts of the code that
413 // should not be reachable, such as in data encoded inline in the instructions.
414 const Instr kImmExceptionIsUnreachable = 0xdebf;
415 
416 // A pseudo 'printf' instruction. The arguments will be passed to the platform
417 // printf method.
418 const Instr kImmExceptionIsPrintf = 0xdeb1;
419 // Most parameters are stored in ARM64 registers as if the printf
420 // pseudo-instruction was a call to the real printf method:
421 //      x0: The format string.
422 //   x1-x7: Optional arguments.
423 //   d0-d7: Optional arguments.
424 //
425 // Also, the argument layout is described inline in the instructions:
426 //  - arg_count: The number of arguments.
427 //  - arg_pattern: A set of PrintfArgPattern values, packed into two-bit fields.
428 //
429 // Floating-point and integer arguments are passed in separate sets of registers
430 // in AAPCS64 (even for varargs functions), so it is not possible to determine
431 // the type of each argument without some information about the values that were
432 // passed in. This information could be retrieved from the printf format string,
433 // but the format string is not trivial to parse so we encode the relevant
434 // information with the HLT instruction.
435 const unsigned kPrintfArgCountOffset = 1 * kInstructionSize;
436 const unsigned kPrintfArgPatternListOffset = 2 * kInstructionSize;
437 const unsigned kPrintfLength = 3 * kInstructionSize;
438 
439 const unsigned kPrintfMaxArgCount = 4;
440 
441 // The argument pattern is a set of two-bit-fields, each with one of the
442 // following values:
443 enum PrintfArgPattern {
444   kPrintfArgW = 1,
445   kPrintfArgX = 2,
446   // There is no kPrintfArgS because floats are always converted to doubles in C
447   // varargs calls.
448   kPrintfArgD = 3
449 };
450 static const unsigned kPrintfArgPatternBits = 2;
451 
452 // A pseudo 'debug' instruction.
453 const Instr kImmExceptionIsDebug = 0xdeb0;
454 // Parameters are inlined in the code after a debug pseudo-instruction:
455 // - Debug code.
456 // - Debug parameters.
457 // - Debug message string. This is a NULL-terminated ASCII string, padded to
458 //   kInstructionSize so that subsequent instructions are correctly aligned.
459 // - A kImmExceptionIsUnreachable marker, to catch accidental execution of the
460 //   string data.
461 const unsigned kDebugCodeOffset = 1 * kInstructionSize;
462 const unsigned kDebugParamsOffset = 2 * kInstructionSize;
463 const unsigned kDebugMessageOffset = 3 * kInstructionSize;
464 
465 // Debug parameters.
466 // Used without a TRACE_ option, the Debugger will print the arguments only
467 // once. Otherwise TRACE_ENABLE and TRACE_DISABLE will enable or disable tracing
468 // before every instruction for the specified LOG_ parameters.
469 //
470 // TRACE_OVERRIDE enables the specified LOG_ parameters, and disabled any
471 // others that were not specified.
472 //
473 // For example:
474 //
475 // __ debug("print registers and fp registers", 0, LOG_REGS | LOG_FP_REGS);
476 // will print the registers and fp registers only once.
477 //
478 // __ debug("trace disasm", 1, TRACE_ENABLE | LOG_DISASM);
479 // starts disassembling the code.
480 //
481 // __ debug("trace rets", 2, TRACE_ENABLE | LOG_REGS);
482 // adds the general purpose registers to the trace.
483 //
484 // __ debug("stop regs", 3, TRACE_DISABLE | LOG_REGS);
485 // stops tracing the registers.
486 const unsigned kDebuggerTracingDirectivesMask = 3 << 6;
487 enum DebugParameters {
488   NO_PARAM       = 0,
489   BREAK          = 1 << 0,
490   LOG_DISASM     = 1 << 1,  // Use only with TRACE. Disassemble the code.
491   LOG_REGS       = 1 << 2,  // Log general purpose registers.
492   LOG_FP_REGS    = 1 << 3,  // Log floating-point registers.
493   LOG_SYS_REGS   = 1 << 4,  // Log the status flags.
494   LOG_WRITE      = 1 << 5,  // Log any memory write.
495 
496   LOG_STATE      = LOG_REGS | LOG_FP_REGS | LOG_SYS_REGS,
497   LOG_ALL        = LOG_DISASM | LOG_STATE | LOG_WRITE,
498 
499   // Trace control.
500   TRACE_ENABLE   = 1 << 6,
501   TRACE_DISABLE  = 2 << 6,
502   TRACE_OVERRIDE = 3 << 6
503 };
504 
505 
506 } }  // namespace v8::internal
507 
508 
509 #endif  // V8_ARM64_INSTRUCTIONS_ARM64_H_
510