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 13namespace v8 { 14namespace internal { 15 16 17// ISA constants. -------------------------------------------------------------- 18 19typedef 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 33DEFINE_FLOAT(kFP32PositiveInfinity, 0x7f800000); 34DEFINE_FLOAT(kFP32NegativeInfinity, 0xff800000); 35DEFINE_DOUBLE(kFP64PositiveInfinity, 0x7ff0000000000000UL); 36DEFINE_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). 40DEFINE_DOUBLE(kFP64SignallingNaN, 0x7ff000007f800001); 41DEFINE_FLOAT(kFP32SignallingNaN, 0x7f800001); 42 43// A similar value, but as a quiet NaN. 44DEFINE_DOUBLE(kFP64QuietNaN, 0x7ff800007fc00001); 45DEFINE_FLOAT(kFP32QuietNaN, 0x7fc00001); 46 47// The default NaN values (for FPCR.DN=1). 48DEFINE_DOUBLE(kFP64DefaultNaN, 0x7ff8000000000000UL); 49DEFINE_FLOAT(kFP32DefaultNaN, 0x7fc00000); 50 51#undef DEFINE_FLOAT 52#undef DEFINE_DOUBLE 53 54 55enum LSDataSize { 56 LSByte = 0, 57 LSHalfword = 1, 58 LSWord = 2, 59 LSDoubleWord = 3 60}; 61 62LSDataSize CalcLSPairDataSize(LoadStorePairOp op); 63 64enum ImmBranchType { 65 UnknownBranchType = 0, 66 CondBranchType = 1, 67 UncondBranchType = 2, 68 CompareBranchType = 3, 69 TestBranchType = 4 70}; 71 72enum AddrMode { 73 Offset, 74 PreIndex, 75 PostIndex 76}; 77 78enum 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 90enum Reg31Mode { 91 Reg31IsStackPointer, 92 Reg31IsZeroRegister 93}; 94 95// Instructions. --------------------------------------------------------------- 96 97class 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 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); } 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 150 LSDataSize SizeLSPair() const { 151 return CalcLSPairDataSize( 152 static_cast<LoadStorePairOp>(Mask(LoadStorePairMask))); 153 } 154 155 // Helpers. 156 bool IsCondBranchImm() const { 157 return Mask(ConditionalBranchFMask) == ConditionalBranchFixed; 158 } 159 160 bool IsUncondBranchImm() const { 161 return Mask(UnconditionalBranchFMask) == UnconditionalBranchFixed; 162 } 163 164 bool IsCompareBranch() const { 165 return Mask(CompareBranchFMask) == CompareBranchFixed; 166 } 167 168 bool IsTestBranch() const { 169 return Mask(TestBranchFMask) == TestBranchFixed; 170 } 171 172 bool IsImmBranch() const { 173 return BranchType() != UnknownBranchType; 174 } 175 176 bool IsLdrLiteral() const { 177 return Mask(LoadLiteralFMask) == LoadLiteralFixed; 178 } 179 180 bool IsLdrLiteralX() const { 181 return Mask(LoadLiteralMask) == LDR_x_lit; 182 } 183 184 bool IsPCRelAddressing() const { 185 return Mask(PCRelAddressingFMask) == PCRelAddressingFixed; 186 } 187 188 bool IsAdr() const { 189 return Mask(PCRelAddressingMask) == ADR; 190 } 191 192 bool IsLogicalImmediate() const { 193 return Mask(LogicalImmediateFMask) == LogicalImmediateFixed; 194 } 195 196 bool IsAddSubImmediate() const { 197 return Mask(AddSubImmediateFMask) == AddSubImmediateFixed; 198 } 199 200 bool IsAddSubShifted() const { 201 return Mask(AddSubShiftedFMask) == AddSubShiftedFixed; 202 } 203 204 bool IsAddSubExtended() const { 205 return Mask(AddSubExtendedFMask) == AddSubExtendedFixed; 206 } 207 208 // Match any loads or stores, including pairs. 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. 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. 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 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 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'. 292 static int32_t ImmBranchRange(ImmBranchType branch_type) { 293 return 294 (1 << (ImmBranchRangeBitwidth(branch_type) + kInstructionSizeLog2)) / 2 - 295 kInstructionSize; 296 } 297 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 309 bool IsBranchAndLinkToRegister() const { 310 return Mask(UnconditionalBranchToRegisterMask) == BLR; 311 } 312 313 bool IsMovz() const { 314 return (Mask(MoveWideImmediateMask) == MOVZ_x) || 315 (Mask(MoveWideImmediateMask) == MOVZ_w); 316 } 317 318 bool IsMovk() const { 319 return (Mask(MoveWideImmediateMask) == MOVK_x) || 320 (Mask(MoveWideImmediateMask) == MOVK_w); 321 } 322 323 bool IsMovn() const { 324 return (Mask(MoveWideImmediateMask) == MOVN_x) || 325 (Mask(MoveWideImmediateMask) == MOVN_w); 326 } 327 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 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 371 template<typename T> V8_INLINE static Instruction* Cast(T src) { 372 return reinterpret_cast<Instruction*>(src); 373 } 374 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; 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. 392class InstructionSequence : public Instruction { 393 public: 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. 410const 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. 414const Instr kImmExceptionIsUnreachable = 0xdebf; 415 416// A pseudo 'printf' instruction. The arguments will be passed to the platform 417// printf method. 418const 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. 435const unsigned kPrintfArgCountOffset = 1 * kInstructionSize; 436const unsigned kPrintfArgPatternListOffset = 2 * kInstructionSize; 437const unsigned kPrintfLength = 3 * kInstructionSize; 438 439const unsigned kPrintfMaxArgCount = 4; 440 441// The argument pattern is a set of two-bit-fields, each with one of the 442// following values: 443enum 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}; 450static const unsigned kPrintfArgPatternBits = 2; 451 452// A pseudo 'debug' instruction. 453const 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. 461const unsigned kDebugCodeOffset = 1 * kInstructionSize; 462const unsigned kDebugParamsOffset = 2 * kInstructionSize; 463const 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. 486const unsigned kDebuggerTracingDirectivesMask = 3 << 6; 487enum 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