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: 99 V8_INLINE Instr InstructionBits() const { 100 return *reinterpret_cast<const Instr*>(this); 101 } 102 103 V8_INLINE void SetInstructionBits(Instr new_instr) { 104 *reinterpret_cast<Instr*>(this) = new_instr; 105 } 106 107 int Bit(int pos) const { 108 return (InstructionBits() >> pos) & 1; 109 } 110 111 uint32_t Bits(int msb, int lsb) const { 112 return unsigned_bitextract_32(msb, lsb, InstructionBits()); 113 } 114 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 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); } 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 158 LSDataSize SizeLSPair() const { 159 return CalcLSPairDataSize( 160 static_cast<LoadStorePairOp>(Mask(LoadStorePairMask))); 161 } 162 163 // Helpers. 164 bool IsCondBranchImm() const { 165 return Mask(ConditionalBranchFMask) == ConditionalBranchFixed; 166 } 167 168 bool IsUncondBranchImm() const { 169 return Mask(UnconditionalBranchFMask) == UnconditionalBranchFixed; 170 } 171 172 bool IsCompareBranch() const { 173 return Mask(CompareBranchFMask) == CompareBranchFixed; 174 } 175 176 bool IsTestBranch() const { 177 return Mask(TestBranchFMask) == TestBranchFixed; 178 } 179 180 bool IsImmBranch() const { 181 return BranchType() != UnknownBranchType; 182 } 183 184 bool IsLdrLiteral() const { 185 return Mask(LoadLiteralFMask) == LoadLiteralFixed; 186 } 187 188 bool IsLdrLiteralX() const { 189 return Mask(LoadLiteralMask) == LDR_x_lit; 190 } 191 192 bool IsPCRelAddressing() const { 193 return Mask(PCRelAddressingFMask) == PCRelAddressingFixed; 194 } 195 196 bool IsAdr() const { 197 return Mask(PCRelAddressingMask) == ADR; 198 } 199 200 bool IsBrk() const { return Mask(ExceptionMask) == BRK; } 201 202 bool IsUnresolvedInternalReference() const { 203 // Unresolved internal references are encoded as two consecutive brk 204 // instructions. 205 return IsBrk() && following()->IsBrk(); 206 } 207 208 bool IsLogicalImmediate() const { 209 return Mask(LogicalImmediateFMask) == LogicalImmediateFixed; 210 } 211 212 bool IsAddSubImmediate() const { 213 return Mask(AddSubImmediateFMask) == AddSubImmediateFixed; 214 } 215 216 bool IsAddSubShifted() const { 217 return Mask(AddSubShiftedFMask) == AddSubShiftedFixed; 218 } 219 220 bool IsAddSubExtended() const { 221 return Mask(AddSubExtendedFMask) == AddSubExtendedFixed; 222 } 223 224 // Match any loads or stores, including pairs. 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. 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. 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 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 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'. 308 static int32_t ImmBranchRange(ImmBranchType branch_type) { 309 return 310 (1 << (ImmBranchRangeBitwidth(branch_type) + kInstructionSizeLog2)) / 2 - 311 kInstructionSize; 312 } 313 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 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 334 bool IsBranchAndLinkToRegister() const { 335 return Mask(UnconditionalBranchToRegisterMask) == BLR; 336 } 337 338 bool IsMovz() const { 339 return (Mask(MoveWideImmediateMask) == MOVZ_x) || 340 (Mask(MoveWideImmediateMask) == MOVZ_w); 341 } 342 343 bool IsMovk() const { 344 return (Mask(MoveWideImmediateMask) == MOVK_x) || 345 (Mask(MoveWideImmediateMask) == MOVK_w); 346 } 347 348 bool IsMovn() const { 349 return (Mask(MoveWideImmediateMask) == MOVN_x) || 350 (Mask(MoveWideImmediateMask) == MOVN_w); 351 } 352 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 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 403 template<typename T> V8_INLINE static Instruction* Cast(T src) { 404 return reinterpret_cast<Instruction*>(src); 405 } 406 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; 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: 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