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 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. 422class 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. 440const 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. 444const Instr kImmExceptionIsUnreachable = 0xdebf; 445 446// A pseudo 'printf' instruction. The arguments will be passed to the platform 447// printf method. 448const 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. 465const unsigned kPrintfArgCountOffset = 1 * kInstructionSize; 466const unsigned kPrintfArgPatternListOffset = 2 * kInstructionSize; 467const unsigned kPrintfLength = 3 * kInstructionSize; 468 469const unsigned kPrintfMaxArgCount = 4; 470 471// The argument pattern is a set of two-bit-fields, each with one of the 472// following values: 473enum 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}; 480static const unsigned kPrintfArgPatternBits = 2; 481 482// A pseudo 'debug' instruction. 483const 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. 491const unsigned kDebugCodeOffset = 1 * kInstructionSize; 492const unsigned kDebugParamsOffset = 2 * kInstructionSize; 493const 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. 516const unsigned kDebuggerTracingDirectivesMask = 3 << 6; 517enum 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