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 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 DCHECK(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 int64_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 uintptr_t LiteralAddress() { 356 int offset = ImmLLiteral() << kLoadLiteralScaleLog2; 357 return reinterpret_cast<uintptr_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 DCHECK(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