WasmSummarizeInsn.cpp (62261B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "wasm/WasmSummarizeInsn.h" 8 9 // for Loongson extension detection 10 #if defined(JS_CODEGEN_MIPS64) 11 # include "jit/mips-shared/Architecture-mips-shared.h" 12 #endif 13 14 #if defined(JS_CODEGEN_RISCV64) 15 # include "jit/riscv64/constant/Constant-riscv64.h" 16 #endif 17 18 using namespace js::jit; 19 20 using mozilla::Maybe; 21 using mozilla::Nothing; 22 using mozilla::Some; 23 24 namespace js { 25 namespace wasm { 26 27 // Sources of documentation of instruction-set encoding: 28 // 29 // Documentation for the ARM instruction sets can be found at 30 // https://developer.arm.com/documentation/ddi0487/latest. The documentation 31 // is vast -- more than 10000 pages. When looking up an instruction, be sure 32 // to look in the correct section for the target word size -- AArch64 (arm64) 33 // and AArch32 (arm32) instructions are listed in different sections. And for 34 // AArch32, be sure to look only at the "A<digit> variant/encoding" and not at 35 // the "T<digit>" ones. The latter are for Thumb encodings, which we don't 36 // generate. 37 // 38 // The Intel documentation is similarly comprehensive: search for "Intel® 64 39 // and IA-32 Architectures Software Developer’s Manual Combined Volumes: 1, 40 // 2A, 2B, 2C, 2D, 3A, 3B, 3C, 3D, and 4". It's easy to find. 41 42 #if defined(DEBUG) 43 44 // ===================================================== x86_32 and x86_64 ==== 45 46 # if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86) 47 48 // Returns true iff a "Mod R/M" byte indicates a memory transaction. 49 static bool ModRMisM(uint8_t modrm) { 50 return (modrm & 0b11'000'000) != 0b11'000'000; 51 } 52 53 // Returns bits 6:4 of a Mod R/M byte, which (for our very limited purposes) 54 // is sometimes interpreted as an opcode extension. 55 static uint8_t ModRMmid3(uint8_t modrm) { return (modrm >> 3) & 0b00000111; } 56 57 // Some simple helpers for dealing with (bitsets of) instruction prefixes. 58 enum Prefix : uint32_t { 59 PfxLock = 1 << 0, 60 Pfx66 = 1 << 1, 61 PfxF2 = 1 << 2, 62 PfxF3 = 1 << 3, 63 PfxRexW = 1 << 4, 64 PfxVexL = 1 << 5 65 }; 66 static bool isEmpty(uint32_t set) { return set == 0; } 67 static bool hasAllOf(uint32_t set, uint32_t mustBePresent) { 68 return (set & mustBePresent) == mustBePresent; 69 } 70 static bool hasNoneOf(uint32_t set, uint32_t mustNotBePresent) { 71 return (set & mustNotBePresent) == 0; 72 } 73 static bool hasOnly(uint32_t set, uint32_t onlyTheseMayBePresent) { 74 return (set & ~onlyTheseMayBePresent) == 0; 75 } 76 77 // Implied opcode-escape prefixes; these are used for decoding VEX-prefixed 78 // (AVX) instructions only. This is a real enumeration, not a bitset. 79 enum Escape { EscNone, Esc0F, Esc0F38, Esc0F3A }; 80 81 Maybe<TrapMachineInsn> SummarizeTrapInstruction(const uint8_t* insn) { 82 const bool is64bit = sizeof(void*) == 8; 83 84 // First off, use up the prefix bytes, so we wind up pointing `insn` at the 85 // primary opcode byte, and at the same time accumulate the prefixes in 86 // `prefixes`. 87 uint32_t prefixes = 0; 88 89 bool hasREX = false; 90 bool hasVEX = false; 91 92 // Parse the "legacy" prefixes (only those we care about). Skip REX on 93 // 32-bit x86. 94 while (true) { 95 if (insn[0] >= 0x40 && insn[0] <= 0x4F && is64bit) { 96 hasREX = true; 97 // It has a REX prefix, but is REX.W set? 98 if (insn[0] >= 0x48) { 99 prefixes |= PfxRexW; 100 } 101 insn++; 102 continue; 103 } 104 if (insn[0] == 0x66) { 105 prefixes |= Pfx66; 106 insn++; 107 continue; 108 } 109 if (insn[0] == 0xF0) { 110 prefixes |= PfxLock; 111 insn++; 112 continue; 113 } 114 if (insn[0] == 0xF2) { 115 prefixes |= PfxF2; 116 insn++; 117 continue; 118 } 119 if (insn[0] == 0xF3) { 120 prefixes |= PfxF3; 121 insn++; 122 continue; 123 } 124 if (insn[0] == 0xC4 || insn[0] == 0xC5) { 125 hasVEX = true; 126 // And fall through to the `break`, leaving `insn` pointing at the start 127 // of the VEX prefix. 128 } 129 break; 130 } 131 132 // Throw out some invalid prefix combinations. 133 if (hasAllOf(prefixes, PfxF2 | PfxF3) || (hasREX && hasVEX)) { 134 return Nothing(); 135 } 136 137 if (!hasVEX) { 138 // The instruction has legacy prefixes only. Deal with all these cases 139 // first. 140 141 // Determine the data size (in bytes) for "standard form" non-SIMD, non-FP 142 // instructions whose opcode byte(s) don't directly imply an 8-bit 143 // operation. If both REX.W and 0x66 are present then REX.W "wins". 144 int opSize = 4; 145 if (prefixes & Pfx66) { 146 opSize = 2; 147 } 148 if (prefixes & PfxRexW) { 149 MOZ_ASSERT(is64bit); 150 opSize = 8; 151 } 152 153 // `insn` should now point at the primary opcode, at least for the cases 154 // we care about. Start identifying instructions. The OP_/OP2_/OP3_ 155 // comments are references to names as declared in Encoding-x86-shared.h. 156 157 // This is the most common trap insn, so deal with it early. Created by 158 // MacroAssembler::wasmTrapInstruction. 159 // OP_2BYTE_ESCAPE OP2_UD2 160 // 0F 0B = ud2 161 if (insn[0] == 0x0F && insn[1] == 0x0B && isEmpty(prefixes)) { 162 return Some(TrapMachineInsn::OfficialUD); 163 } 164 165 // ==== Atomics 166 167 // This is something of a kludge, but .. if the insn has a LOCK prefix, 168 // declare it to be TrapMachineInsn::Atomic, regardless of what it 169 // actually does. It's good enough for checking purposes. 170 if (prefixes & PfxLock) { 171 return Some(TrapMachineInsn::Atomic); 172 } 173 // After this point, we can assume that no instruction has a lock prefix. 174 175 // OP_XCHG_GbEb 176 // OP_XCHG_GvEv 177 // 86 = XCHG reg8, reg8/mem8. 178 // 87 = XCHG reg64/32/16, reg64/32/16 / mem64/32/16. 179 // The memory variants are atomic even though there is no LOCK prefix. 180 if (insn[0] == 0x86 && ModRMisM(insn[1]) && isEmpty(prefixes)) { 181 return Some(TrapMachineInsn::Atomic); 182 } 183 if (insn[0] == 0x87 && ModRMisM(insn[1]) && 184 hasOnly(prefixes, Pfx66 | PfxRexW)) { 185 return Some(TrapMachineInsn::Atomic); 186 } 187 188 // ==== Scalar loads and stores 189 190 // OP_MOV_EbGv 191 // OP_MOV_GvEb 192 // 88 = MOV src=reg, dst=mem/reg (8 bit int only) 193 // 8A = MOV src=mem/reg, dst=reg (8 bit int only) 194 if ((insn[0] == 0x88 || insn[0] == 0x8A) && ModRMisM(insn[1]) && 195 isEmpty(prefixes)) { 196 return Some(insn[0] == 0x88 ? TrapMachineInsn::Store8 197 : TrapMachineInsn::Load8); 198 } 199 200 // OP_MOV_EvGv 201 // OP_MOV_GvEv 202 // 89 = MOV src=reg, dst=mem/reg (64/32/16 bit int only) 203 // 8B = MOV src=mem/reg, dst=reg (64/32/16 bit int only) 204 if ((insn[0] == 0x89 || insn[0] == 0x8B) && ModRMisM(insn[1]) && 205 hasOnly(prefixes, Pfx66 | PfxRexW)) { 206 return Some(insn[0] == 0x89 ? TrapMachineInsnForStore(opSize) 207 : TrapMachineInsnForLoad(opSize)); 208 } 209 210 // OP_GROUP11_EvIb GROUP11_MOV 211 // C6 /0 = MOV src=immediate, dst=mem (8 bit int only) 212 if (insn[0] == 0xC6 && ModRMisM(insn[1]) && ModRMmid3(insn[1]) == 0 && 213 isEmpty(prefixes)) { 214 return Some(TrapMachineInsn::Store8); 215 } 216 // OP_GROUP11_EvIz GROUP11_MOV 217 // C7 /0 = MOV src=immediate, dst=mem (64/32/16 bit int only) 218 if (insn[0] == 0xC7 && ModRMisM(insn[1]) && ModRMmid3(insn[1]) == 0 && 219 hasOnly(prefixes, Pfx66 | PfxRexW)) { 220 return Some(TrapMachineInsnForStore(opSize)); 221 } 222 223 // OP_2BYTE_ESCAPE OP2_MOVZX_GvEb 224 // OP_2BYTE_ESCAPE OP2_MOVSX_GvEb 225 // 0F B6 = MOVZB{W,L,Q} src=reg/mem, dst=reg (8 -> 16, 32 or 64, int only) 226 // 0F BE = MOVSB{W,L,Q} src=reg/mem, dst=reg (8 -> 16, 32 or 64, int only) 227 if (insn[0] == 0x0F && (insn[1] == 0xB6 || insn[1] == 0xBE) && 228 ModRMisM(insn[2]) && (opSize == 2 || opSize == 4 || opSize == 8) && 229 hasOnly(prefixes, Pfx66 | PfxRexW)) { 230 return Some(TrapMachineInsn::Load8); 231 } 232 // OP_2BYTE_ESCAPE OP2_MOVZX_GvEw 233 // OP_2BYTE_ESCAPE OP2_MOVSX_GvEw 234 // 0F B7 = MOVZW{L,Q} src=reg/mem, dst=reg (16 -> 32 or 64, int only) 235 // 0F BF = MOVSW{L,Q} src=reg/mem, dst=reg (16 -> 32 or 64, int only) 236 if (insn[0] == 0x0F && (insn[1] == 0xB7 || insn[1] == 0xBF) && 237 ModRMisM(insn[2]) && (opSize == 4 || opSize == 8) && 238 hasOnly(prefixes, PfxRexW)) { 239 return Some(TrapMachineInsn::Load16); 240 } 241 242 // OP_MOVSXD_GvEv 243 // REX.W 63 = MOVSLQ src=reg32/mem32, dst=reg64 244 if (hasAllOf(prefixes, PfxRexW) && insn[0] == 0x63 && ModRMisM(insn[1]) && 245 hasOnly(prefixes, Pfx66 | PfxRexW)) { 246 return Some(TrapMachineInsn::Load32); 247 } 248 249 // ==== SSE{2,3,E3,4} insns 250 251 // OP_2BYTE_ESCAPE OP2_MOVPS_VpsWps 252 // OP_2BYTE_ESCAPE OP2_MOVPS_WpsVps 253 // 0F 10 = MOVUPS src=xmm/mem128, dst=xmm 254 // 0F 11 = MOVUPS src=xmm, dst=xmm/mem128 255 if (insn[0] == 0x0F && (insn[1] == 0x10 || insn[1] == 0x11) && 256 ModRMisM(insn[2]) && hasOnly(prefixes, PfxRexW)) { 257 return Some(insn[1] == 0x10 ? TrapMachineInsn::Load128 258 : TrapMachineInsn::Store128); 259 } 260 261 // OP_2BYTE_ESCAPE OP2_MOVLPS_VqEq 262 // OP_2BYTE_ESCAPE OP2_MOVHPS_VqEq 263 // 0F 12 = MOVLPS src=xmm64/mem64, dst=xmm 264 // 0F 16 = MOVHPS src=xmm64/mem64, dst=xmm 265 if (insn[0] == 0x0F && (insn[1] == 0x12 || insn[1] == 0x16) && 266 ModRMisM(insn[2]) && hasOnly(prefixes, PfxRexW)) { 267 return Some(TrapMachineInsn::Load64); 268 } 269 270 // OP_2BYTE_ESCAPE OP2_MOVLPS_EqVq 271 // OP_2BYTE_ESCAPE OP2_MOVHPS_EqVq 272 // 0F 13 = MOVLPS src=xmm64, dst=xmm64/mem64 273 // 0F 17 = MOVHPS src=xmm64, dst=xmm64/mem64 274 if (insn[0] == 0x0F && (insn[1] == 0x13 || insn[1] == 0x17) && 275 ModRMisM(insn[2]) && hasOnly(prefixes, PfxRexW)) { 276 return Some(TrapMachineInsn::Store64); 277 } 278 279 // PRE_SSE_F2 OP_2BYTE_ESCAPE OP2_MOVSD_VsdWsd 280 // PRE_SSE_F2 OP_2BYTE_ESCAPE OP2_MOVSD_WsdVsd 281 // F2 0F 10 = MOVSD src=mem64/xmm64, dst=xmm64 282 // F2 0F 11 = MOVSD src=xmm64, dst=mem64/xmm64 283 if (hasAllOf(prefixes, PfxF2) && insn[0] == 0x0F && 284 (insn[1] == 0x10 || insn[1] == 0x11) && ModRMisM(insn[2]) && 285 hasOnly(prefixes, PfxRexW | PfxF2)) { 286 return Some(insn[1] == 0x10 ? TrapMachineInsn::Load64 287 : TrapMachineInsn::Store64); 288 } 289 290 // PRE_SSE_F2 OP_2BYTE_ESCAPE OP2_MOVDDUP_VqWq 291 // F2 0F 12 = MOVDDUP src=mem64/xmm64, dst=xmm 292 if (hasAllOf(prefixes, PfxF2) && insn[0] == 0x0F && insn[1] == 0x12 && 293 ModRMisM(insn[2]) && hasOnly(prefixes, PfxF2)) { 294 return Some(TrapMachineInsn::Load64); 295 } 296 297 // PRE_SSE_F3 OP_2BYTE_ESCAPE OP2_MOVSS_VssWss (name does not exist) 298 // PRE_SSE_F3 OP_2BYTE_ESCAPE OP2_MOVSS_WssVss (name does not exist) 299 // F3 0F 10 = MOVSS src=mem32/xmm32, dst=xmm32 300 // F3 0F 11 = MOVSS src=xmm32, dst=mem32/xmm32 301 if (hasAllOf(prefixes, PfxF3) && insn[0] == 0x0F && 302 (insn[1] == 0x10 || insn[1] == 0x11) && ModRMisM(insn[2]) && 303 hasOnly(prefixes, PfxRexW | PfxF3)) { 304 return Some(insn[1] == 0x10 ? TrapMachineInsn::Load32 305 : TrapMachineInsn::Store32); 306 } 307 308 // PRE_SSE_F3 OP_2BYTE_ESCAPE OP2_MOVDQ_VdqWdq 309 // PRE_SSE_F3 OP_2BYTE_ESCAPE OP2_MOVDQ_WdqVdq 310 // F3 0F 6F = MOVDQU src=mem128/xmm, dst=xmm 311 // F3 0F 7F = MOVDQU src=xmm, dst=mem128/xmm 312 if (hasAllOf(prefixes, PfxF3) && insn[0] == 0x0F && 313 (insn[1] == 0x6F || insn[1] == 0x7F) && ModRMisM(insn[2]) && 314 hasOnly(prefixes, PfxF3)) { 315 return Some(insn[1] == 0x6F ? TrapMachineInsn::Load128 316 : TrapMachineInsn::Store128); 317 } 318 319 // PRE_SSE_66 OP_2BYTE_ESCAPE ESCAPE_3A OP3_PINSRB_VdqEvIb 320 // 66 0F 3A 20 /r ib = PINSRB $imm8, src=mem8/ireg8, dst=xmm128. I'd guess 321 // that REX.W is meaningless here and therefore we should exclude it. 322 if (hasAllOf(prefixes, Pfx66) && insn[0] == 0x0F && insn[1] == 0x3A && 323 insn[2] == 0x20 && ModRMisM(insn[3]) && hasOnly(prefixes, Pfx66)) { 324 return Some(TrapMachineInsn::Load8); 325 } 326 // PRE_SSE_66 OP_2BYTE_ESCAPE OP2_PINSRW 327 // 66 0F C4 /r ib = PINSRW $imm8, src=mem16/ireg16, dst=xmm128. REX.W is 328 // probably meaningless here. 329 if (hasAllOf(prefixes, Pfx66) && insn[0] == 0x0F && insn[1] == 0xC4 && 330 ModRMisM(insn[2]) && hasOnly(prefixes, Pfx66)) { 331 return Some(TrapMachineInsn::Load16); 332 } 333 // PRE_SSE_66 OP_2BYTE_ESCAPE ESCAPE_3A OP3_INSERTPS_VpsUps 334 // 66 0F 3A 21 /r ib = INSERTPS $imm8, src=mem32/xmm32, dst=xmm128. 335 // REX.W is probably meaningless here. 336 if (hasAllOf(prefixes, Pfx66) && insn[0] == 0x0F && insn[1] == 0x3A && 337 insn[2] == 0x21 && ModRMisM(insn[3]) && hasOnly(prefixes, Pfx66)) { 338 return Some(TrapMachineInsn::Load32); 339 } 340 341 // PRE_SSE_66 OP_2BYTE_ESCAPE ESCAPE_3A OP3_PEXTRB_EvVdqIb 342 // PRE_SSE_66 OP_2BYTE_ESCAPE ESCAPE_3A OP3_PEXTRW_EwVdqIb 343 // PRE_SSE_66 OP_2BYTE_ESCAPE ESCAPE_3A OP3_EXTRACTPS_EdVdqIb 344 // 66 0F 3A 14 /r ib = PEXTRB src=xmm8, dst=reg8/mem8 345 // 66 0F 3A 15 /r ib = PEXTRW src=xmm16, dst=reg16/mem16 346 // 66 0F 3A 17 /r ib = EXTRACTPS src=xmm32, dst=reg32/mem32 347 // REX.W is probably meaningless here. 348 if (hasAllOf(prefixes, Pfx66) && insn[0] == 0x0F && insn[1] == 0x3A && 349 (insn[2] == 0x14 || insn[2] == 0x15 || insn[2] == 0x17) && 350 ModRMisM(insn[3]) && hasOnly(prefixes, Pfx66)) { 351 return Some(insn[2] == 0x14 ? TrapMachineInsn::Store8 352 : insn[2] == 0x15 ? TrapMachineInsn::Store16 353 : TrapMachineInsn::Store32); 354 } 355 356 // PRE_SSE_66 OP_2BYTE_ESCAPE ESCAPE_38 OP3_PMOVSXBW_VdqWdq 357 // PRE_SSE_66 OP_2BYTE_ESCAPE ESCAPE_38 OP3_PMOVSXWD_VdqWdq 358 // PRE_SSE_66 OP_2BYTE_ESCAPE ESCAPE_38 OP3_PMOVSXDQ_VdqWdq 359 // PRE_SSE_66 OP_2BYTE_ESCAPE ESCAPE_38 OP3_PMOVZXBW_VdqWdq 360 // PRE_SSE_66 OP_2BYTE_ESCAPE ESCAPE_38 OP3_PMOVZXWD_VdqWdq 361 // PRE_SSE_66 OP_2BYTE_ESCAPE ESCAPE_38 OP3_PMOVZXDQ_VdqWdq 362 // 66 0F 38 20 /r = PMOVSXBW src=mem64/xmm64, dst=xmm 363 // 66 0F 38 23 /r = PMOVSXWD src=mem64/xmm64, dst=xmm 364 // 66 0F 38 25 /r = PMOVSXDQ src=mem64/xmm64, dst=xmm 365 // 66 0F 38 30 /r = PMOVZXBW src=mem64/xmm64, dst=xmm 366 // 66 0F 38 33 /r = PMOVZXWD src=mem64/xmm64, dst=xmm 367 // 66 0F 38 35 /r = PMOVZXDQ src=mem64/xmm64, dst=xmm 368 if (hasAllOf(prefixes, Pfx66) && insn[0] == 0x0F && insn[1] == 0x38 && 369 (insn[2] == 0x20 || insn[2] == 0x23 || insn[2] == 0x25 || 370 insn[2] == 0x30 || insn[2] == 0x33 || insn[2] == 0x35) && 371 ModRMisM(insn[3]) && hasOnly(prefixes, Pfx66)) { 372 return Some(TrapMachineInsn::Load64); 373 } 374 375 // The insn only has legacy prefixes, and was not identified. 376 return Nothing(); 377 } 378 379 // We're dealing with a VEX-prefixed insn. Fish out relevant bits of the 380 // VEX prefix. VEX prefixes come in two kinds: a 3-byte prefix, first byte 381 // 0xC4, which gives us 16 bits of extra data, and a 2-byte prefix, first 382 // byte 0xC5, which gives us 8 bits of extra data. The 2-byte variant 383 // contains a subset of the data that the 3-byte variant does and 384 // (presumably) is to be used when the default values of the omitted fields 385 // are correct for the instruction that is encoded. 386 // 387 // An instruction can't have both VEX and REX prefixes, because a 3-byte VEX 388 // prefix specifies everything a REX prefix does, that is, the four bits 389 // REX.{WRXB} and allowing both to be present would allow conflicting values 390 // for them. Of these four bits, we only care about REX.W (as obtained here 391 // from the VEX prefix). 392 // 393 // A VEX prefix can also specify (imply?) the presence of the legacy 394 // prefixes 66, F2 and F3. Although the byte sequence we will have parsed 395 // for this insn doesn't actually contain any of those, we must decode as if 396 // we had seen them as legacy prefixes. 397 // 398 // A VEX prefix can also specify (imply?) the presence of the opcode escape 399 // byte sequences 0F, 0F38 and 0F3A. These are collected up into `esc`. 400 // Again, we must decode as if we had actually seen these, although we 401 // haven't really. 402 // 403 // The VEX prefix also holds various other bits which we ignore, because 404 // these specify details of registers etc which we don't care about. 405 MOZ_ASSERT(hasVEX && !hasREX); 406 MOZ_ASSERT(hasNoneOf(prefixes, PfxRexW)); 407 MOZ_ASSERT(insn[0] == 0xC4 || insn[0] == 0xC5); 408 409 Escape esc = EscNone; 410 411 if (insn[0] == 0xC4) { 412 // This is a 3-byte VEX prefix (3 bytes including the 0xC4). 413 switch (insn[1] & 0x1F) { 414 case 1: 415 esc = Esc0F; 416 break; 417 case 2: 418 esc = Esc0F38; 419 break; 420 case 3: 421 esc = Esc0F3A; 422 break; 423 default: 424 return Nothing(); 425 } 426 switch (insn[2] & 3) { 427 case 0: 428 break; 429 case 1: 430 prefixes |= Pfx66; 431 break; 432 case 2: 433 prefixes |= PfxF3; 434 break; 435 case 3: 436 prefixes |= PfxF2; 437 break; 438 } 439 if (insn[2] & 4) { 440 // VEX.L distinguishes 128-bit (VEX.L==0) from 256-bit (VEX.L==1) 441 // operations. 442 prefixes |= PfxVexL; 443 } 444 if ((insn[2] & 0x80) && is64bit) { 445 // Pull out REX.W, but only on 64-bit targets. We'll need it for insn 446 // decoding. Recall that REX.W == 1 basically means "the 447 // integer-register (GPR) aspect of this instruction requires a 64-bit 448 // transaction", so we expect these to be relatively rare, since VEX is 449 // primary used for SIMD instructions. 450 prefixes |= PfxRexW; 451 } 452 // Step forwards to the primary opcode byte 453 insn += 3; 454 } else if (insn[0] == 0xC5) { 455 // This is a 2-byte VEX prefix (2 bytes including the 0xC5). Since it has 456 // only 8 bits of useful payload, it adds less information than an 0xC4 457 // prefix. 458 esc = Esc0F; 459 switch (insn[1] & 3) { 460 case 0: 461 break; 462 case 1: 463 prefixes |= Pfx66; 464 break; 465 case 2: 466 prefixes |= PfxF3; 467 break; 468 case 3: 469 prefixes |= PfxF2; 470 break; 471 } 472 if (insn[1] & 4) { 473 prefixes |= PfxVexL; 474 } 475 insn += 2; 476 } 477 478 // This isn't allowed. 479 if (hasAllOf(prefixes, PfxF2 | PfxF3)) { 480 return Nothing(); 481 } 482 483 // This is useful for diagnosing decoding failures. 484 // if (0) { 485 // fprintf(stderr, "FAIL VEX 66=%d,F2=%d,F3=%d,REXW=%d,VEXL=%d esc=%s\n", 486 // (prefixes & Pfx66) ? 1 : 0, (prefixes & PfxF2) ? 1 : 0, 487 // (prefixes & PfxF3) ? 1 : 0, (prefixes & PfxRexW) ? 1 : 0, 488 // (prefixes & PfxVexL) ? 1 : 0, 489 // esc == Esc0F3A ? "0F3A" 490 // : esc == Esc0F38 ? "0F38" 491 // : esc == Esc0F ? "0F" 492 // : "none"); 493 // } 494 495 // (vex prefix) OP2_MOVPS_VpsWps 496 // (vex prefix) OP2_MOVPS_WpsVps 497 // 66=0,F2=0,F3=0,REXW=0,VEXL=0 esc=0F 10 = VMOVUPS src=xmm/mem128, dst=xmm 498 // 66=0,F2=0,F3=0,REXW=0,VEXL=0 esc=0F 11 = VMOVUPS src=xmm, dst=xmm/mem128 499 // REX.W is ignored. 500 if (hasNoneOf(prefixes, Pfx66 | PfxF2 | PfxF3 | PfxRexW | PfxVexL) && 501 esc == Esc0F && (insn[0] == 0x10 || insn[0] == 0x11) && 502 ModRMisM(insn[1])) { 503 return Some(insn[0] == 0x10 ? TrapMachineInsn::Load128 504 : TrapMachineInsn::Store128); 505 } 506 507 // (vex prefix) OP2_MOVSD_VsdWsd 508 // (vex prefix) OP2_MOVSD_WsdVsd 509 // 66=0,F2=1,F3=0,REXW=0,VEXL=0 esc=0F 10 = VMOVSD src=mem64, dst=xmm 510 // 66=0,F2=1,F3=0,REXW=0,VEXL=0 esc=0F 11 = VMOVSD src=xmm, dst=mem64 511 // REX.W and VEX.L are ignored. 512 if (hasAllOf(prefixes, PfxF2) && 513 hasNoneOf(prefixes, Pfx66 | PfxF3 | PfxRexW | PfxVexL) && esc == Esc0F && 514 (insn[0] == 0x10 || insn[0] == 0x11) && ModRMisM(insn[1])) { 515 return Some(insn[0] == 0x10 ? TrapMachineInsn::Load64 516 : TrapMachineInsn::Store64); 517 } 518 519 // (vex prefix) OP2_MOVSS_VssWss (name does not exist) 520 // (vex prefix) OP2_MOVSS_WssVss (name does not exist) 521 // 66=0,F2=0,F3=1,REXW=0,VEXL=0 esc=0F 10 = VMOVSS src=mem32, dst=xmm 522 // 66=0,F2=0,F3=1,REXW=0,VEXL=0 esc=0F 11 = VMOVSS src=xmm, dst=mem32 523 // REX.W and VEX.L are ignored. 524 if (hasAllOf(prefixes, PfxF3) && 525 hasNoneOf(prefixes, Pfx66 | PfxF2 | PfxRexW | PfxVexL) && esc == Esc0F && 526 (insn[0] == 0x10 || insn[0] == 0x11) && ModRMisM(insn[1])) { 527 return Some(insn[0] == 0x10 ? TrapMachineInsn::Load32 528 : TrapMachineInsn::Store32); 529 } 530 531 // (vex prefix) OP2_MOVDDUP_VqWq 532 // 66=0,F2=1,F3=0,REXW=0,VEXL=0 esc=0F 12 = VMOVDDUP src=xmm/m64, dst=xmm 533 // REX.W is ignored. 534 if (hasAllOf(prefixes, PfxF2) && 535 hasNoneOf(prefixes, Pfx66 | PfxF3 | PfxRexW | PfxVexL) && esc == Esc0F && 536 insn[0] == 0x12 && ModRMisM(insn[1])) { 537 return Some(TrapMachineInsn::Load64); 538 } 539 540 // (vex prefix) OP2_MOVLPS_EqVq 541 // (vex prefix) OP2_MOVHPS_EqVq 542 // 66=0,F2=0,F3=0,REXW=0,VEXL=0 esc=0F 13 = VMOVLPS src=xmm, dst=mem64 543 // 66=0,F2=0,F3=0,REXW=0,VEXL=0 esc=0F 17 = VMOVHPS src=xmm, dst=mem64 544 // REX.W is ignored. These do a 64-bit mem transaction despite the 'S' in 545 // the name, because a pair of float32s are transferred. 546 if (hasNoneOf(prefixes, Pfx66 | PfxF2 | PfxF3 | PfxRexW | PfxVexL) && 547 esc == Esc0F && (insn[0] == 0x13 || insn[0] == 0x17) && 548 ModRMisM(insn[1])) { 549 return Some(TrapMachineInsn::Store64); 550 } 551 552 // (vex prefix) OP2_MOVDQ_VdqWdq 553 // (vex prefix) OP2_MOVDQ_WdqVdq 554 // 66=0,F2=0,F3=1,REXW=0,VEXL=0 esc=0F 6F = VMOVDQU src=xmm/mem128, dst=xmm 555 // 66=0,F2=0,F3=1,REXW=0,VEXL=0 esc=0F 7F = VMOVDQU src=xmm, dst=xmm/mem128 556 // REX.W is ignored. 557 if (hasAllOf(prefixes, PfxF3) && 558 hasNoneOf(prefixes, Pfx66 | PfxF2 | PfxRexW | PfxVexL) && esc == Esc0F && 559 (insn[0] == 0x6F || insn[0] == 0x7F) && ModRMisM(insn[1])) { 560 return Some(insn[0] == 0x6F ? TrapMachineInsn::Load128 561 : TrapMachineInsn::Store128); 562 } 563 564 // (vex prefix) OP2_PINSRW 565 // 66=1,F2=0,F3=0,REXW=0,VEXL=0 esc=OF C4 566 // = PINSRW src=ireg/mem16,src=xmm,dst=xmm 567 // REX.W is ignored. 568 if (hasAllOf(prefixes, Pfx66) && 569 hasNoneOf(prefixes, PfxF2 | PfxF3 | PfxRexW | PfxVexL) && esc == Esc0F && 570 insn[0] == 0xC4 && ModRMisM(insn[1])) { 571 return Some(TrapMachineInsn::Load16); 572 } 573 574 // (vex prefix) OP3_PMOVSXBW_VdqWdq 575 // (vex prefix) OP3_PMOVSXWD_VdqWdq 576 // (vex prefix) OP3_PMOVSXDQ_VdqWdq 577 // (vex prefix) OP3_PMOVZXBW_VdqWdq 578 // (vex prefix) OP3_PMOVZXWD_VdqWdq 579 // (vex prefix) OP3_PMOVZXDQ_VdqWdq 580 // 66=1,F2=0,F3=0,REXW=0,VEXL=0 esc=0F38 20 = VPMOVSXBW src=xmm/m64, dst=xmm 581 // 66=1,F2=0,F3=0,REXW=0,VEXL=0 esc=0F38 23 = VPMOVSXWD src=xmm/m64, dst=xmm 582 // 66=1,F2=0,F3=0,REXW=0,VEXL=0 esc=0F38 25 = VPMOVSXDQ src=xmm/m64, dst=xmm 583 // 66=1,F2=0,F3=0,REXW=0,VEXL=0 esc=0F38 30 = VPMOVZXBW src=xmm/m64, dst=xmm 584 // 66=1,F2=0,F3=0,REXW=0,VEXL=0 esc=0F38 33 = VPMOVZXWD src=xmm/m64, dst=xmm 585 // 66=1,F2=0,F3=0,REXW=0,VEXL=0 esc=0F38 35 = VPMOVZXDQ src=xmm/m64, dst=xmm 586 // REX.W is ignored. 587 if (hasAllOf(prefixes, Pfx66) && 588 hasNoneOf(prefixes, PfxF2 | PfxF3 | PfxRexW | PfxVexL) && 589 esc == Esc0F38 && 590 (insn[0] == 0x20 || insn[0] == 0x23 || insn[0] == 0x25 || 591 insn[0] == 0x30 || insn[0] == 0x33 || insn[0] == 0x35) && 592 ModRMisM(insn[1])) { 593 return Some(TrapMachineInsn::Load64); 594 } 595 596 // (vex prefix) OP3_VBROADCASTB_VxWx 597 // (vex prefix) OP3_VBROADCASTW_VxWx 598 // (vex prefix) OP3_VBROADCASTSS_VxWd 599 // 66=1,F2=0,F3=0,REXW=0,VEXL=0 esc=0F38 78 600 // = VPBROADCASTB src=xmm8/mem8, dst=xmm 601 // 66=1,F2=0,F3=0,REXW=0,VEXL=0 esc=0F38 79 602 // = VPBROADCASTW src=xmm16/mem16, dst=xmm 603 // 66=1,F2=0,F3=0,REXW=0,VEXL=0 esc=0F38 18 604 // = VBROADCASTSS src=m32, dst=xmm 605 // VPBROADCASTB/W require REX.W == 0; VBROADCASTSS ignores REX.W. 606 if (hasAllOf(prefixes, Pfx66) && 607 hasNoneOf(prefixes, PfxF2 | PfxF3 | PfxRexW | PfxVexL) && 608 esc == Esc0F38 && 609 (insn[0] == 0x78 || insn[0] == 0x79 || insn[0] == 0x18) && 610 ModRMisM(insn[1])) { 611 return Some(insn[0] == 0x78 ? TrapMachineInsn::Load8 612 : insn[0] == 0x79 ? TrapMachineInsn::Load16 613 : TrapMachineInsn::Load32); 614 } 615 616 // (vex prefix) OP3_PEXTRB_EvVdqIb 617 // (vex prefix) OP3_PEXTRW_EwVdqIb 618 // 66=1,F2=0,F3=0,REXW=0,VEXL=0 esc=0F3A 14 = VPEXTRB src=xmm, dst=ireg/mem8 619 // 66=1,F2=0,F3=0,REXW=0,VEXL=0 esc=0F3A 15 = VPEXTRW src=xmm, dst=ireg/mem16 620 // These require REX.W == 0. 621 if (hasAllOf(prefixes, Pfx66) && 622 hasNoneOf(prefixes, PfxF2 | PfxF3 | PfxRexW | PfxVexL) && 623 esc == Esc0F3A && (insn[0] == 0x14 || insn[0] == 0x15) && 624 ModRMisM(insn[1])) { 625 return Some(insn[0] == 0x14 ? TrapMachineInsn::Store8 626 : TrapMachineInsn::Store16); 627 } 628 629 // (vex prefix) OP3_EXTRACTPS_EdVdqIb 630 // 66=1,F2=0,F3=0,REXW=0,VEXL=0 esc=0F3A 17 631 // = VEXTRACTPS src=xmm, dst=ireg/mem32 632 // REX.W is ignored. 633 if (hasAllOf(prefixes, Pfx66) && 634 hasNoneOf(prefixes, PfxF2 | PfxF3 | PfxRexW | PfxVexL) && 635 esc == Esc0F3A && insn[0] == 0x17 && ModRMisM(insn[1])) { 636 return Some(TrapMachineInsn::Store32); 637 } 638 639 // The instruction was not identified. 640 return Nothing(); 641 } 642 643 // ================================================================= arm64 ==== 644 645 # elif defined(JS_CODEGEN_ARM64) 646 647 Maybe<TrapMachineInsn> SummarizeTrapInstruction(const uint8_t* insnAddr) { 648 // Check instruction alignment. 649 MOZ_ASSERT(0 == (uintptr_t(insnAddr) & 3)); 650 651 const uint32_t insn = *(uint32_t*)insnAddr; 652 653 # define INSN(_maxIx, _minIx) \ 654 ((insn >> (_minIx)) & ((uint32_t(1) << ((_maxIx) - (_minIx) + 1)) - 1)) 655 656 // MacroAssembler::wasmTrapInstruction uses this to create SIGILL. 657 if (insn == 0xD4A00000) { 658 return Some(TrapMachineInsn::OfficialUD); 659 } 660 661 // A note about loads and stores. Many (perhaps all) integer loads and 662 // stores use bits 31:30 of the instruction as a size encoding, thusly: 663 // 664 // 11 -> 64 bit, 10 -> 32 bit, 01 -> 16 bit, 00 -> 8 bit 665 // 666 // It is also very common for corresponding load and store instructions to 667 // differ by exactly one bit (logically enough). 668 // 669 // Meaning of register names: 670 // 671 // Xn The n-th GPR (all 64 bits), for 0 <= n <= 31 672 // Xn|SP The n-th GPR (all 64 bits), for 0 <= n <= 30, or SP when n == 31 673 // Wn The lower 32 bits of the n-th GPR 674 // Qn All 128 bits of the n-th SIMD register 675 // Dn Lower 64 bits of the n-th SIMD register 676 // Sn Lower 32 bits of the n-th SIMD register 677 678 // Plain and zero-extending loads/stores, reg + offset, scaled 679 switch (INSN(31, 22)) { 680 // 11 111 00100 imm12 n t = STR Xt, [Xn|SP, #imm12 * 8] 681 case 0b11'111'00100: 682 return Some(TrapMachineInsn::Store64); 683 // 10 111 00100 imm12 n t = STR Wt, [Xn|SP, #imm12 * 4] 684 case 0b10'111'00100: 685 return Some(TrapMachineInsn::Store32); 686 // 01 111 00100 imm12 n t = STRH Wt, [Xn|SP, #imm12 * 2] 687 case 0b01'111'00100: 688 return Some(TrapMachineInsn::Store16); 689 // 00 111 00100 imm12 n t = STRB Wt, [Xn|SP, #imm12 * 1] 690 case 0b00'111'00100: 691 return Some(TrapMachineInsn::Store8); 692 // 11 111 00101 imm12 n t = LDR Xt, [Xn|SP, #imm12 * 8] 693 case 0b11'111'00101: 694 return Some(TrapMachineInsn::Load64); 695 // 10 111 00101 imm12 n t = LDR Wt, [Xn|SP, #imm12 * 4] 696 case 0b10'111'00101: 697 return Some(TrapMachineInsn::Load32); 698 // 01 111 00101 imm12 n t = LDRH Wt, [Xn|SP, #imm12 * 2] 699 case 0b01'111'00101: 700 return Some(TrapMachineInsn::Load16); 701 // 00 111 00101 imm12 n t = LDRB Wt, [Xn|SP, #imm12 * 1] 702 case 0b00'111'00101: 703 return Some(TrapMachineInsn::Load8); 704 } 705 706 // Plain, sign- and zero-extending loads/stores, reg + offset, unscaled 707 708 if (INSN(11, 10) == 0b00) { 709 switch (INSN(31, 21)) { 710 // 11 111 00001 0 imm9 00 n t = LDUR Xt, [Xn|SP, #imm9] 711 case 0b11'111'00001'0: 712 return Some(TrapMachineInsn::Load64); 713 // 10 111 00001 0 imm9 00 n t = LDUR Wt, [Xn|SP, #imm9] 714 case 0b10'111'00001'0: 715 return Some(TrapMachineInsn::Load32); 716 // 01 111 00001 0 imm9 00 n t = LDURH Wt, [Xn|SP, #imm9] 717 case 0b01'111'00001'0: 718 return Some(TrapMachineInsn::Load16); 719 // We do have code to generate LDURB insns, but it appears to not be used. 720 // 11 111 00000 0 imm9 00 n t = STUR Xt, [Xn|SP, #imm9] 721 case 0b11'111'00000'0: 722 return Some(TrapMachineInsn::Store64); 723 // 10 111 00000 0 imm9 00 n t = STUR Wt, [Xn|SP, #imm9] 724 case 0b10'111'00000'0: 725 return Some(TrapMachineInsn::Store32); 726 // 01 111 00000 0 imm9 00 n t = STURH Wt, [Xn|SP, #imm9] 727 case 0b01'111'00000'0: 728 return Some(TrapMachineInsn::Store16); 729 // STURB missing? 730 // Sign extending loads: 731 // 10 111 000 10 0 imm9 00 n t = LDURSW Xt, [Xn|SP, #imm9] 732 case 0b10'111'000'10'0: 733 return Some(TrapMachineInsn::Load32); 734 // 01 111 000 11 0 imm9 00 n t = LDURSH Wt, [Xn|SP, #imm9] 735 // 01 111 000 10 0 imm9 00 n t = LDURSH Xt, [Xn|SP, #imm9] 736 case 0b01'111'000'11'0: 737 case 0b01'111'000'10'0: 738 return Some(TrapMachineInsn::Load16); 739 } 740 } 741 742 // Sign extending loads, reg + offset, scaled 743 744 switch (INSN(31, 22)) { 745 // 10 111 001 10 imm12 n t = LDRSW Xt, [Xn|SP, #imm12 * 4] 746 case 0b10'111'001'10: 747 return Some(TrapMachineInsn::Load32); 748 // 01 111 001 10 imm12 n t = LDRSH Xt, [Xn|SP, #imm12 * 2] 749 // 01 111 001 11 imm12 n t = LDRSH Wt, [Xn|SP, #imm12 * 2] 750 case 0b01'111'001'10: 751 case 0b01'111'001'11: 752 return Some(TrapMachineInsn::Load16); 753 // 00 111 001 10 imm12 n t = LDRSB Xt, [Xn|SP, #imm12 * 1] 754 // 00 111 001 11 imm12 n t = LDRSB Wt, [Xn|SP, #imm12 * 1] 755 case 0b00'111'001'10: 756 case 0b00'111'001'11: 757 return Some(TrapMachineInsn::Load8); 758 } 759 760 // Sign extending loads, reg + reg(extended/shifted) 761 762 if (INSN(11, 10) == 0b10) { 763 switch (INSN(31, 21)) { 764 // 10 1110001 01 m opt s 10 n t = LDRSW Xt, [Xn|SP, R<m>{ext/sh}] 765 case 0b10'1110001'01: 766 return Some(TrapMachineInsn::Load32); 767 // 01 1110001 01 m opt s 10 n t = LDRSH Xt, [Xn|SP, R<m>{ext/sh}] 768 case 0b01'1110001'01: 769 return Some(TrapMachineInsn::Load16); 770 // 01 1110001 11 m opt s 10 n t = LDRSH Wt, [Xn|SP, R<m>{ext/sh}] 771 case 0b01'1110001'11: 772 return Some(TrapMachineInsn::Load16); 773 // 00 1110001 01 m opt s 10 n t = LDRSB Xt, [Xn|SP, R<m>{ext/sh}] 774 case 0b00'1110001'01: 775 return Some(TrapMachineInsn::Load8); 776 // 00 1110001 11 m opt s 10 n t = LDRSB Wt, [Xn|SP, R<m>{ext/sh}] 777 case 0b00'1110001'11: 778 return Some(TrapMachineInsn::Load8); 779 } 780 } 781 782 // Plain and zero-extending loads/stores, reg + reg(extended/shifted) 783 784 if (INSN(11, 10) == 0b10) { 785 switch (INSN(31, 21)) { 786 // 11 111000001 m opt s 10 n t = STR Xt, [Xn|SP, Rm{ext/sh}] 787 case 0b11'111000001: 788 return Some(TrapMachineInsn::Store64); 789 // 10 111000001 m opt s 10 n t = STR Wt, [Xn|SP, Rm{ext/sh}] 790 case 0b10'111000001: 791 return Some(TrapMachineInsn::Store32); 792 // 01 111000001 m opt s 10 n t = STRH Wt, [Xn|SP, Rm{ext/sh}] 793 case 0b01'111000001: 794 return Some(TrapMachineInsn::Store16); 795 // 00 111000001 m opt s 10 n t = STRB Wt, [Xn|SP, Rm{ext/sh}] 796 case 0b00'111000001: 797 return Some(TrapMachineInsn::Store8); 798 // 11 111000011 m opt s 10 n t = LDR Xt, [Xn|SP, Rm{ext/sh}] 799 case 0b11'111000011: 800 return Some(TrapMachineInsn::Load64); 801 // 10 111000011 m opt s 10 n t = LDR Wt, [Xn|SP, Rm{ext/sh}] 802 case 0b10'111000011: 803 return Some(TrapMachineInsn::Load32); 804 // 01 111000011 m opt s 10 n t = LDRH Wt, [Xn|SP, Rm{ext/sh}] 805 case 0b01'111000011: 806 return Some(TrapMachineInsn::Load16); 807 // 00 111000011 m opt s 10 n t = LDRB Wt, [Xn|SP, Rm{ext/sh}] 808 case 0b00'111000011: 809 return Some(TrapMachineInsn::Load8); 810 } 811 } 812 813 // SIMD - scalar FP 814 815 switch (INSN(31, 22)) { 816 // 11 111 101 00 imm12 n t = STR Dt, [Xn|SP + imm12 * 8] 817 case 0b11'111'101'00: 818 return Some(TrapMachineInsn::Store64); 819 // 10 111 101 00 imm12 n t = STR St, [Xn|SP + imm12 * 4] 820 case 0b10'111'101'00: 821 return Some(TrapMachineInsn::Store32); 822 // 11 111 101 01 imm12 n t = LDR Dt, [Xn|SP + imm12 * 8] 823 case 0b11'111'101'01: 824 return Some(TrapMachineInsn::Load64); 825 // 10 111 101 01 imm12 n t = LDR St, [Xn|SP + imm12 * 4] 826 case 0b10'111'101'01: 827 return Some(TrapMachineInsn::Load32); 828 } 829 830 if (INSN(11, 10) == 0b00) { 831 switch (INSN(31, 21)) { 832 // 11 111 100 00 0 imm9 00 n t = STUR Dt, [Xn|SP, #imm9] 833 case 0b11'111'100'00'0: 834 return Some(TrapMachineInsn::Store64); 835 // 10 111 100 00 0 imm9 00 n t = STUR St, [Xn|SP, #imm9] 836 case 0b10'111'100'00'0: 837 return Some(TrapMachineInsn::Store32); 838 // 11 111 100 01 0 imm9 00 n t = LDUR Dt, [Xn|SP, #imm9] 839 case 0b11'111'100'01'0: 840 return Some(TrapMachineInsn::Load64); 841 // 10 111 100 01 0 imm9 00 n t = LDUR St, [Xn|SP, #imm9] 842 case 0b10'111'100'01'0: 843 return Some(TrapMachineInsn::Load32); 844 } 845 } 846 847 if (INSN(11, 10) == 0b10) { 848 switch (INSN(31, 21)) { 849 // 11 111100 001 m opt s 10 n t = STR Dt, [Xn|SP, Rm{ext/sh}] 850 case 0b11'111100'001: 851 return Some(TrapMachineInsn::Store64); 852 // 10 111100 001 m opt s 10 n t = STR St, [Xn|SP, Rm{ext/sh}] 853 case 0b10'111100'001: 854 return Some(TrapMachineInsn::Store32); 855 // 11 111100 011 m opt s 10 n t = LDR Dt, [Xn|SP, Rm{ext/sh}] 856 case 0b11'111100'011: 857 return Some(TrapMachineInsn::Load64); 858 // 10 111100 011 m opt s 10 n t = LDR St, [Xn|SP, Rm{ext/sh}] 859 case 0b10'111100'011: 860 return Some(TrapMachineInsn::Load32); 861 } 862 } 863 864 // SIMD - whole register 865 866 if (INSN(11, 10) == 0b00) { 867 // 00 111 100 10 0 imm9 00 n t = STUR Qt, [Xn|SP, #imm9] 868 if (INSN(31, 21) == 0b00'111'100'10'0) { 869 return Some(TrapMachineInsn::Store128); 870 } 871 // 00 111 100 11 0 imm9 00 n t = LDUR Qt, [Xn|SP, #imm9] 872 if (INSN(31, 21) == 0b00'111'100'11'0) { 873 return Some(TrapMachineInsn::Load128); 874 } 875 } 876 877 // 00 111 101 10 imm12 n t = STR Qt, [Xn|SP + imm12 * 16] 878 if (INSN(31, 22) == 0b00'111'101'10) { 879 return Some(TrapMachineInsn::Store128); 880 } 881 // 00 111 101 11 imm12 n t = LDR Qt, [Xn|SP + imm12 * 16] 882 if (INSN(31, 22) == 0b00'111'101'11) { 883 return Some(TrapMachineInsn::Load128); 884 } 885 886 if (INSN(11, 10) == 0b10) { 887 // 00 111100 101 m opt s 10 n t = STR Qt, [Xn|SP, Rm{ext/sh}] 888 if (INSN(31, 21) == 0b00'111100'101) { 889 return Some(TrapMachineInsn::Store128); 890 } 891 // 00 111100 111 m opt s 10 n t = LDR Qt, [Xn|SP, Rm{ext/sh}] 892 if (INSN(31, 21) == 0b00'111100'111) { 893 return Some(TrapMachineInsn::Load128); 894 } 895 } 896 897 // Atomics - loads/stores "exclusive" (with reservation) (LL/SC) 898 899 switch (INSN(31, 10)) { 900 // 11 001000 010 11111 0 11111 n t = LDXR Xt, [Xn|SP] 901 case 0b11'001000'010'11111'0'11111: 902 return Some(TrapMachineInsn::Load64); 903 // 10 001000 010 11111 0 11111 n t = LDXR Wt, [Xn|SP] 904 case 0b10'001000'010'11111'0'11111: 905 return Some(TrapMachineInsn::Load32); 906 // 01 001000 010 11111 0 11111 n t = LDXRH Wt, [Xn|SP] 907 case 0b01'001000'010'11111'0'11111: 908 return Some(TrapMachineInsn::Load16); 909 // 00 001000 010 11111 0 11111 n t = LDXRB Wt, [Xn|SP] 910 case 0b00'001000'010'11111'0'11111: 911 return Some(TrapMachineInsn::Load8); 912 // We are never asked to examine store-exclusive instructions, because any 913 // store-exclusive should be preceded by a load-exclusive instruction of 914 // the same size and for the same address. So the TrapSite is omitted for 915 // the store-exclusive since the load-exclusive will trap first. 916 } 917 918 // Atomics - atomic memory operations which do (LD- variants) or do not 919 // (ST-variants) return the original value at the location. 920 921 // 11 111 0000 11 s 0 000 00 n 11111 = STADDL Xs, [Xn|SP] 922 // 10 111 0000 11 s 0 000 00 n 11111 = STADDL Ws, [Xn|SP] 923 // 01 111 0000 11 s 0 000 00 n 11111 = STADDLH Ws, [Xn|SP] 924 // 00 111 0000 11 s 0 000 00 n 11111 = STADDLB Ws, [Xn|SP] 925 // and the same for 926 // ---------------- 0 001 00 ------- = STCLRL 927 // ---------------- 0 010 00 ------- = STEORL 928 // ---------------- 0 011 00 ------- = STSETL 929 if (INSN(29, 21) == 0b111'0000'11 && INSN(4, 0) == 0b11111) { 930 switch (INSN(15, 10)) { 931 case 0b0'000'00: // STADDL 932 case 0b0'001'00: // STCLRL 933 case 0b0'010'00: // STEORL 934 case 0b0'011'00: // STSETL 935 return Some(TrapMachineInsn::Atomic); 936 } 937 } 938 939 // 11 111 0001 11 s 0 000 00 n t = LDADDAL Xs, Xt, [Xn|SP] 940 // 10 111 0001 11 s 0 000 00 n t = LDADDAL Ws, Wt, [Xn|SP] 941 // 01 111 0001 11 s 0 000 00 n t = LDADDALH Ws, Wt, [Xn|SP] 942 // 00 111 0001 11 s 0 000 00 n t = LDADDALB Ws, Wt, [Xn|SP] 943 // and the same for 944 // ---------------- 0 001 00 --- = LDCLRAL 945 // ---------------- 0 010 00 --- = LDEORAL 946 // ---------------- 0 011 00 --- = LDSETAL 947 if (INSN(29, 21) == 0b111'0001'11) { 948 switch (INSN(15, 10)) { 949 case 0b0'000'00: // LDADDAL 950 case 0b0'001'00: // LDCLRAL 951 case 0b0'010'00: // LDEORAL 952 case 0b0'011'00: // LDSETAL 953 return Some(TrapMachineInsn::Atomic); 954 } 955 } 956 957 // Atomics -- compare-and-swap and plain swap 958 959 // 11 001000111 s 111111 n t = CASAL Xs, Xt, [Xn|SP] 960 // 10 001000111 s 111111 n t = CASAL Ws, Wt, [Xn|SP] 961 // 01 001000111 s 111111 n t = CASALH Ws, Wt, [Xn|SP] 962 // 00 001000111 s 111111 n t = CASALB Ws, Wt, [Xn|SP] 963 if (INSN(29, 21) == 0b001000111 && INSN(15, 10) == 0b111111) { 964 return Some(TrapMachineInsn::Atomic); 965 } 966 967 // 11 11100011 1 s 100000 n t = SWPAL Xs, Xt, [Xn|SP] 968 // 10 11100011 1 s 100000 n t = SWPAL Ws, Wt, [Xn|SP] 969 // 01 11100011 1 s 100000 n t = SWPALH Ws, Wt, [Xn|SP] 970 // 00 11100011 1 s 100000 n t = SWPALB Ws, Wt, [Xn|SP] 971 if (INSN(29, 21) == 0b11100011'1 && INSN(15, 10) == 0b100000) { 972 return Some(TrapMachineInsn::Atomic); 973 } 974 975 # undef INSN 976 977 // The instruction was not identified. 978 979 // This is useful for diagnosing decoding failures. 980 // if (0) { 981 // fprintf(stderr, "insn = "); 982 // for (int i = 31; i >= 0; i--) { 983 // fprintf(stderr, "%c", ((insn >> i) & 1) ? '1' : '0'); 984 // if (i < 31 && (i % 4) == 0) fprintf(stderr, " "); 985 // } 986 // fprintf(stderr, "\n"); 987 // } 988 989 return Nothing(); 990 } 991 992 // =================================================================== arm ==== 993 994 # elif defined(JS_CODEGEN_ARM) 995 996 Maybe<TrapMachineInsn> SummarizeTrapInstruction(const uint8_t* insnAddr) { 997 // Almost all AArch32 instructions that use the ARM encoding (not Thumb) use 998 // bits 31:28 as the guarding condition. Since we do not expect to 999 // encounter conditional loads or stores, most of the following is hardcoded 1000 // to check that those bits are 1110 (0xE), which is the "always execute" 1001 // condition. An exception is Neon instructions, which are never 1002 // conditional and so have those bits set to 1111 (0xF). 1003 1004 // Check instruction alignment. 1005 MOZ_ASSERT(0 == (uintptr_t(insnAddr) & 3)); 1006 1007 const uint32_t insn = *(uint32_t*)insnAddr; 1008 1009 # define INSN(_maxIx, _minIx) \ 1010 ((insn >> (_minIx)) & ((uint32_t(1) << ((_maxIx) - (_minIx) + 1)) - 1)) 1011 1012 // MacroAssembler::wasmTrapInstruction uses this to create SIGILL. 1013 if (insn == 0xE7F000F0) { 1014 return Some(TrapMachineInsn::OfficialUD); 1015 } 1016 1017 // 31 27 23 19 15 11 1018 // cond 0101 U000 Rn Rt imm12 = STR<cond> Rt, [Rn, +/- #imm12] 1019 // cond 0101 U001 Rn Rt imm12 = LDR<cond> Rt, [Rn, +/- #imm12] 1020 // cond 0101 U100 Rn Rt imm12 = STRB<cond> Rt, [Rn, +/- #imm12] 1021 // cond 0101 U101 Rn Rt imm12 = LDRB<cond> Rt, [Rn, +/- #imm12] 1022 // if cond != 1111 and Rn != 1111 1023 // U = 1 for +, U = 0 for - 1024 if (INSN(31, 28) == 0b1110 // unconditional 1025 && INSN(27, 24) == 0b0101 && INSN(19, 16) != 0b1111) { 1026 switch (INSN(22, 20)) { 1027 case 0b000: 1028 return Some(TrapMachineInsn::Store32); 1029 case 0b001: 1030 return Some(TrapMachineInsn::Load32); 1031 case 0b100: 1032 return Some(TrapMachineInsn::Store8); 1033 case 0b101: 1034 return Some(TrapMachineInsn::Load8); 1035 default: 1036 break; 1037 } 1038 } 1039 1040 // 31 27 23 19 15 11 7 3 1041 // cond 0001 U100 Rn Rt imm4 1011 imm4 = STRH<cond> Rt, [Rn +/- #imm8] 1042 // cond 0001 U101 Rn Rt imm4 1101 imm4 = LDRSB<cond> Rt, [Rn +/- #imm8] 1043 // cond 0001 U101 Rn Rt imm4 1111 imm4 = LDRSH<cond> Rt, [Rn +/- #imm8] 1044 // cond 0001 U101 Rn Rt imm4 1011 imm4 = LDRH<cond> Rt, [Rn +/- #imm8] 1045 // U = 1 for +, U = 0 for - 1046 if (INSN(31, 28) == 0b1110 // unconditional 1047 && INSN(27, 24) == 0b0001 && INSN(22, 21) == 0b10) { 1048 switch ((INSN(20, 20) << 4) | INSN(7, 4)) { 1049 case 0b0'1011: 1050 return Some(TrapMachineInsn::Store16); 1051 case 0b1'1101: 1052 return Some(TrapMachineInsn::Load8); 1053 case 0b1'1111: 1054 return Some(TrapMachineInsn::Load16); 1055 case 0b1'1011: 1056 return Some(TrapMachineInsn::Load16); 1057 default: 1058 break; 1059 } 1060 } 1061 1062 // clang-format off 1063 // 1064 // 31 27 23 19 15 11 6 4 3 1065 // cond 0111 U000 Rn Rt shimm 00 0 Rm = STR<cond> Rt, [Rn, +/- Rm, [lsl #shimm]] 1066 // cond 0111 U100 Rn Rt shimm 00 0 Rm = STRB<cond> Rt, [Rn, +/- Rm, [lsl #shimm]] 1067 // cond 0111 U001 Rn Rt shimm 00 0 Rm = LDR<cond> Rt, [Rn, +/- Rm, [lsl #shimm]] 1068 // cond 0111 U101 Rn Rt shimm 00 0 Rm = LDRB<cond> Rt, [Rn, +/- Rm, [lsl #shimm]] 1069 // U = 1 for +, U = 0 for - 1070 // 1071 // clang-format on 1072 if (INSN(31, 28) == 0b1110 // unconditional 1073 && INSN(27, 24) == 0b0111 && INSN(6, 4) == 0b00'0 // lsl 1074 ) { 1075 switch (INSN(22, 20)) { 1076 case 0b000: 1077 return Some(TrapMachineInsn::Store32); 1078 case 0b100: 1079 return Some(TrapMachineInsn::Store8); 1080 case 0b001: 1081 return Some(TrapMachineInsn::Load32); 1082 case 0b101: 1083 return Some(TrapMachineInsn::Load8); 1084 default: 1085 break; 1086 } 1087 } 1088 1089 // 31 27 23 19 15 11 7 3 1090 // cond 0001 U000 Rn Rt 0000 1011 Rm = STRH<cond> Rt, [Rn, +/- Rm] 1091 // cond 0001 U001 Rn Rt 0000 1011 Rm = LDRH<cond> Rt, [Rn, +/- Rm] 1092 // cond 0001 U001 Rn Rt 0000 1101 Rm = LDRSB<cond> Rt, [Rn, +/- Rm] 1093 // cond 0001 U001 Rn Rt 0000 1111 Rm = LDRSH<cond> Rt, [Rn, +/- Rm] 1094 if (INSN(31, 28) == 0b1110 // unconditional 1095 && INSN(27, 24) == 0b0001 && INSN(22, 21) == 0b00 && 1096 INSN(11, 8) == 0b0000) { 1097 switch ((INSN(20, 20) << 4) | INSN(7, 4)) { 1098 case 0b0'1011: 1099 return Some(TrapMachineInsn::Store16); 1100 case 0b1'1011: 1101 return Some(TrapMachineInsn::Load16); 1102 case 0b1'1101: 1103 return Some(TrapMachineInsn::Load8); 1104 case 0b1'1111: 1105 return Some(TrapMachineInsn::Load16); 1106 default: 1107 break; 1108 } 1109 } 1110 1111 // 31 27 23 19 15 11 7 1112 // cond 1101 UD00 Rn Vd 1010 imm8 = VSTR<cond> Sd, [Rn +/- #imm8] 1113 // cond 1101 UD00 Rn Vd 1011 imm8 = VSTR<cond> Dd, [Rn +/- #imm8] 1114 // cond 1101 UD01 Rn Vd 1010 imm8 = VLDR<cond> Sd, [Rn +/- #imm8] 1115 // cond 1101 UD01 Rn Vd 1011 imm8 = VLDR<cond> Dd, [Rn +/- #imm8] 1116 // U = 1 for +, U = 0 for - 1117 // D is an extension of Vd (so can be anything) 1118 if (INSN(31, 28) == 0b1110 // unconditional 1119 && INSN(27, 24) == 0b1101 && INSN(21, 21) == 0b0) { 1120 switch ((INSN(20, 20) << 4) | (INSN(11, 8))) { 1121 case 0b0'1010: 1122 return Some(TrapMachineInsn::Store32); 1123 case 0b0'1011: 1124 return Some(TrapMachineInsn::Store64); 1125 case 0b1'1010: 1126 return Some(TrapMachineInsn::Load32); 1127 case 0b1'1011: 1128 return Some(TrapMachineInsn::Load64); 1129 default: 1130 break; 1131 } 1132 } 1133 1134 // 31 27 23 19 15 11 7 3 1135 // 1111 0100 1D00 Rn Vd 1000 0000 1111 = VST1.32 {Dd[0], [Rn] 1136 // 1111 0100 1D10 Rn Vd 1000 0000 1111 = VLD1.32 {Dd[0], [Rn] 1137 if (INSN(31, 23) == 0b1111'0100'1 && INSN(20, 20) == 0 && 1138 INSN(11, 0) == 0b1000'0000'1111) { 1139 return INSN(21, 21) == 1 ? Some(TrapMachineInsn::Load32) 1140 : Some(TrapMachineInsn::Store32); 1141 } 1142 1143 // 31 27 23 19 15 11 7 3 1144 // 1111 0100 0D00 Rn Vd 0111 1100 1111 = VST1.64 {Dd], [Rn] 1145 // 1111 0100 0D10 Rn Vd 0111 1100 1111 = VLD1.64 {Dd], [Rn] 1146 if (INSN(31, 23) == 0b1111'0100'0 && INSN(20, 20) == 0 && 1147 INSN(11, 0) == 0b0111'1100'1111) { 1148 return INSN(21, 21) == 1 ? Some(TrapMachineInsn::Load64) 1149 : Some(TrapMachineInsn::Store64); 1150 } 1151 1152 // 31 27 23 19 15 11 7 3 1153 // cond 0001 1101 n t 1111 1001 1111 = LDREXB<cond> Rt, [Rn] 1154 // cond 0001 1111 n t 1111 1001 1111 = LDREXH<cond> Rt, [Rn] 1155 // cond 0001 1001 n t 1111 1001 1111 = LDREX<cond> Rt, [Rn] 1156 // cond 0001 1011 n t 1111 1001 1111 = LDREXD<cond> Rt, [Rn] 1157 if (INSN(31, 23) == 0b1110'0001'1 && INSN(11, 0) == 0b1111'1001'1111) { 1158 switch (INSN(22, 20)) { 1159 case 0b101: 1160 return Some(TrapMachineInsn::Load8); 1161 case 0b111: 1162 return Some(TrapMachineInsn::Load16); 1163 case 0b001: 1164 return Some(TrapMachineInsn::Load32); 1165 case 0b011: 1166 return Some(TrapMachineInsn::Load64); 1167 default: 1168 break; 1169 } 1170 } 1171 1172 # undef INSN 1173 1174 // The instruction was not identified. 1175 1176 // This is useful for diagnosing decoding failures. 1177 // if (0) { 1178 // fprintf(stderr, "insn = "); 1179 // for (int i = 31; i >= 0; i--) { 1180 // fprintf(stderr, "%c", ((insn >> i) & 1) ? '1' : '0'); 1181 // if (i < 31 && (i % 4) == 0) fprintf(stderr, " "); 1182 // } 1183 // fprintf(stderr, "\n"); 1184 // } 1185 1186 return Nothing(); 1187 } 1188 1189 // =============================================================== riscv64 ==== 1190 1191 # elif defined(JS_CODEGEN_RISCV64) 1192 1193 Maybe<TrapMachineInsn> SummarizeTrapInstruction(const uint8_t* insnAddr) { 1194 // Check instruction alignment. 1195 MOZ_ASSERT(0 == (uintptr_t(insnAddr) & 3)); 1196 1197 const uint32_t insn = *(uint32_t*)insnAddr; 1198 1199 # define INSN(_maxIx, _minIx) \ 1200 ((insn >> (_minIx)) & ((uint32_t(1) << ((_maxIx) - (_minIx) + 1)) - 1)) 1201 // MacroAssembler::wasmTrapInstruction uses this to create SIGILL. 1202 if (insn == 1203 (RO_CSRRWI | csr_cycle << kCsrShift | kWasmTrapCode << kRs1Shift)) { 1204 return Some(TrapMachineInsn::OfficialUD); 1205 } 1206 1207 if (INSN(6, 0) == STORE) { 1208 switch (INSN(14, 12)) { 1209 case 0b011: 1210 return Some(TrapMachineInsn::Load64); 1211 case 0b010: 1212 return Some(TrapMachineInsn::Load32); 1213 case 0b001: 1214 return Some(TrapMachineInsn::Load16); 1215 case 0b000: 1216 return Some(TrapMachineInsn::Load8); 1217 default: 1218 break; 1219 } 1220 } 1221 1222 if (INSN(6, 0) == LOAD) { 1223 switch (INSN(14, 12)) { 1224 case 0b011: 1225 return Some(TrapMachineInsn::Store64); 1226 case 0b010: 1227 return Some(TrapMachineInsn::Store32); 1228 case 0b001: 1229 return Some(TrapMachineInsn::Store16); 1230 case 0b000: 1231 return Some(TrapMachineInsn::Store8); 1232 default: 1233 break; 1234 } 1235 } 1236 1237 if (INSN(6, 0) == LOAD_FP) { 1238 switch (INSN(14, 12)) { 1239 case 0b011: 1240 return Some(TrapMachineInsn::Load64); 1241 case 0b010: 1242 return Some(TrapMachineInsn::Load32); 1243 default: 1244 break; 1245 } 1246 } 1247 1248 if (INSN(6, 0) == STORE_FP) { 1249 switch (INSN(14, 12)) { 1250 case 0b011: 1251 return Some(TrapMachineInsn::Store64); 1252 case 0b010: 1253 return Some(TrapMachineInsn::Store32); 1254 default: 1255 break; 1256 } 1257 } 1258 1259 if (INSN(6, 0) == AMO && INSN(31, 27) == 00010) { 1260 switch (INSN(14, 12)) { 1261 case 0b011: 1262 return Some(TrapMachineInsn::Load64); 1263 case 0b010: 1264 return Some(TrapMachineInsn::Load32); 1265 default: 1266 break; 1267 } 1268 } 1269 1270 if (INSN(6, 0) == AMO && INSN(31, 27) == 00011) { 1271 switch (INSN(14, 12)) { 1272 case 0b011: 1273 return Some(TrapMachineInsn::Store64); 1274 case 0b010: 1275 return Some(TrapMachineInsn::Store32); 1276 default: 1277 break; 1278 } 1279 } 1280 1281 # undef INSN 1282 1283 return Nothing(); 1284 } 1285 1286 // =========================================================== loongarch64 ==== 1287 1288 # elif defined(JS_CODEGEN_LOONG64) 1289 1290 Maybe<TrapMachineInsn> SummarizeTrapInstruction(const uint8_t* insnAddr) { 1291 // Check instruction alignment. 1292 MOZ_ASSERT(0 == (uintptr_t(insnAddr) & 3)); 1293 1294 const uint32_t insn = *(uint32_t*)insnAddr; 1295 1296 # define INSN(_maxIx, _minIx) \ 1297 ((insn >> (_minIx)) & ((uint32_t(1) << ((_maxIx) - (_minIx) + 1)) - 1)) 1298 1299 // LoongArch instructions encoding document: 1300 // https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN#table-of-instruction-encoding 1301 1302 // MacroAssembler::wasmTrapInstruction uses this to create SIGILL. 1303 // break 0x6 1304 if (insn == 0x002A0006) { 1305 return Some(TrapMachineInsn::OfficialUD); 1306 } 1307 1308 // Loads/stores with reg + offset (si12). 1309 if (INSN(31, 26) == 0b001010) { 1310 switch (INSN(25, 22)) { 1311 // ld.b rd, rj, si12 1312 case 0b0000: 1313 return Some(TrapMachineInsn::Load8); 1314 // ld.h rd, rj, si12 1315 case 0b0001: 1316 return Some(TrapMachineInsn::Load16); 1317 // ld.w rd, rj, si12 1318 case 0b0010: 1319 return Some(TrapMachineInsn::Load32); 1320 // ld.d rd, rj, si12 1321 case 0b0011: 1322 return Some(TrapMachineInsn::Load64); 1323 // st.b rd, rj, si12 1324 case 0b0100: 1325 return Some(TrapMachineInsn::Store8); 1326 // st.h rd, rj, si12 1327 case 0b0101: 1328 return Some(TrapMachineInsn::Store16); 1329 // st.w rd, rj, si12 1330 case 0b0110: 1331 return Some(TrapMachineInsn::Store32); 1332 // st.d rd, rj, si12 1333 case 0b0111: 1334 return Some(TrapMachineInsn::Store64); 1335 // ld.bu rd, rj, si12 1336 case 0b1000: 1337 return Some(TrapMachineInsn::Load8); 1338 // ld.hu rd, rj, si12 1339 case 0b1001: 1340 return Some(TrapMachineInsn::Load16); 1341 // ld.wu rd, rj, si12 1342 case 0b1010: 1343 return Some(TrapMachineInsn::Load32); 1344 // preld hint, rj, si12 1345 case 0b1011: 1346 break; 1347 // fld.s fd, rj, si12 1348 case 0b1100: 1349 return Some(TrapMachineInsn::Load32); 1350 // fst.s fd, rj, si12 1351 case 0b1101: 1352 return Some(TrapMachineInsn::Store32); 1353 // fld.d fd, rj, si12 1354 case 0b1110: 1355 return Some(TrapMachineInsn::Load64); 1356 // fst.s fd, rj, si12 1357 case 0b1111: 1358 return Some(TrapMachineInsn::Store64); 1359 default: 1360 break; 1361 } 1362 } 1363 1364 // Loads/stores with reg + reg. 1365 if (INSN(31, 22) == 0b0011100000 && INSN(17, 15) == 0b000) { 1366 switch (INSN(21, 18)) { 1367 // ldx.b rd, rj, rk 1368 case 0b0000: 1369 return Some(TrapMachineInsn::Load8); 1370 // ldx.h rd, rj, rk 1371 case 0b0001: 1372 return Some(TrapMachineInsn::Load16); 1373 // ldx.w rd, rj, rk 1374 case 0b0010: 1375 return Some(TrapMachineInsn::Load32); 1376 // ldx.d rd, rj, rk 1377 case 0b0011: 1378 return Some(TrapMachineInsn::Load64); 1379 // stx.b rd, rj, rk 1380 case 0b0100: 1381 return Some(TrapMachineInsn::Store8); 1382 // stx.h rd, rj, rk 1383 case 0b0101: 1384 return Some(TrapMachineInsn::Store16); 1385 // stx.w rd, rj, rk 1386 case 0b0110: 1387 return Some(TrapMachineInsn::Store32); 1388 // stx.d rd, rj, rk 1389 case 0b0111: 1390 return Some(TrapMachineInsn::Store64); 1391 // ldx.bu rd, rj, rk 1392 case 0b1000: 1393 return Some(TrapMachineInsn::Load8); 1394 // ldx.hu rd, rj, rk 1395 case 0b1001: 1396 return Some(TrapMachineInsn::Load16); 1397 // ldx.wu rd, rj, rk 1398 case 0b1010: 1399 return Some(TrapMachineInsn::Load32); 1400 // preldx hint, rj, rk 1401 case 0b1011: 1402 break; 1403 // fldx.s fd, rj, rk 1404 case 0b1100: 1405 return Some(TrapMachineInsn::Load32); 1406 // fldx.d fd, rj, rk 1407 case 0b1101: 1408 return Some(TrapMachineInsn::Load64); 1409 // fstx.s fd, rj, rk 1410 case 0b1110: 1411 return Some(TrapMachineInsn::Store32); 1412 // fstx.d fd, rj, rk 1413 case 0b1111: 1414 return Some(TrapMachineInsn::Store64); 1415 default: 1416 break; 1417 } 1418 } 1419 1420 // Loads/stores with reg + offset (si14). 1421 // 1. Atomics - loads/stores "exclusive" (with reservation) (LL/SC) 1422 // 2. {ld/st}ptr.{w/d} 1423 if (INSN(31, 27) == 0b00100) { 1424 switch (INSN(26, 24)) { 1425 // ll.w rd, rj, si14 1426 case 0b000: 1427 return Some(TrapMachineInsn::Load32); 1428 // ll.d rd, rj, si14 1429 case 0b010: 1430 return Some(TrapMachineInsn::Load64); 1431 // ldptr.w rd, rj, si14 1432 case 0b100: 1433 return Some(TrapMachineInsn::Load32); 1434 // stptr.w rd, rj, si14 1435 case 0b101: 1436 return Some(TrapMachineInsn::Store32); 1437 // ldptr.d rd, rj, si14 1438 case 0b110: 1439 return Some(TrapMachineInsn::Load64); 1440 // stptr.d rd, rj, si14 1441 case 0b111: 1442 return Some(TrapMachineInsn::Store64); 1443 default: 1444 break; 1445 // We are never asked to examine store-exclusive instructions, because 1446 // any store-exclusive should be preceded by a load-exclusive 1447 // instruction of the same size and for the same address. So the 1448 // TrapSite is omitted for the store-exclusive since the load-exclusive 1449 // will trap first. 1450 } 1451 } 1452 1453 # undef INSN 1454 1455 return Nothing(); 1456 } 1457 1458 // ================================================================ mips64 ==== 1459 1460 # elif defined(JS_CODEGEN_MIPS64) 1461 1462 Maybe<TrapMachineInsn> SummarizeTrapInstruction(const uint8_t* insnAddr) { 1463 // Check instruction alignment. 1464 MOZ_ASSERT(0 == (uintptr_t(insnAddr) & 3)); 1465 1466 const uint32_t insn = *(uint32_t*)insnAddr; 1467 1468 # define INSN(_maxIx, _minIx) \ 1469 ((insn >> (_minIx)) & ((uint32_t(1) << ((_maxIx) - (_minIx) + 1)) - 1)) 1470 1471 // MIPS64R2 instruction encoding document: 1472 // https://scc.ustc.edu.cn/_upload/article/files/c6/06/45556c084631b2855f0022175eaf/W020100308600769158777.pdf#G254.1001018 1473 1474 // Loongson GS464 extension: 1475 // No official encoding document. Refer to binutils-gdb/opcodes/mips-opc.c and 1476 // https://github.com/FlyGoat/loongson-insn/blob/master/loongson-ext.md 1477 // instead. 1478 1479 // MacroAssembler::wasmTrapInstruction uses this to create SIGILL. 1480 // teq zero, zero, 0x6 1481 if (insn == 0x000001b4) { 1482 return Some(TrapMachineInsn::OfficialUD); 1483 } 1484 1485 // MIPS64 Encoding of the Opcode Field of memory access instructions. 1486 // +--------+--------------------------------------------------------+ 1487 // | bits | 28..26 | 1488 // +--------+------+------+------+-------+------+------+------+------+ 1489 // | 31..29 | 000 | 001 | 010 | 011 | 100 | 101 | 110 | 111 | 1490 // +--------+------+------+------+-------+------+------+------+------+ 1491 // | 010 | | | | COP1X | | | | | 1492 // | 011 | | | LDL | LDR | | | | | 1493 // | 100 | LB | LH | LWL | LW | LBU | LHU | LWR | LWU | 1494 // | 101 | SB | SH | SWL | SW | SDL | SDR | SWR | | 1495 // | 110 | LL | LWC1 | LWC2 | | LLD | LDC1 | LDC2 | LD | 1496 // | 111 | SC | SWC1 | SWC2 | | SCD | SDC1 | SDC2 | SD | 1497 // +--------+------+------+------+-------+------+------+------+------+ 1498 // Loongson GS464 Encoding of the Opcode and Function Field of memory access 1499 // extension instructions. 1500 // +--------+-------------------------------------------------------+ 1501 // | bits | 2..0 | 1502 // +--------+-----+-----+-----+-----+-------+-------+-------+-------+ 1503 // | 31..26 | 000 | 001 | 010 | 011 | 100 | 101 | 110 | 111 | 1504 // +--------+-----+-----+-----+-----+-------+-------+-------+-------+ 1505 // | 110010 | | | | |GSLWLC1|GSLWRC1|GSLDLC1|GSLDRC1| 1506 // | 111010 | | | | |GSSWLC1|GSSWRC1|GSSDLC1|GSSDRC1| 1507 // | 110110 |GSLBX|GSLHX|GSLWX|GSLDX| | |GSLWXC1|GSLDXC1| 1508 // | 111110 |GSLBX|GSLHX|GSLWX|GSLDX| | |GSSWXC1|GSSDXC1| 1509 // +--------+-----+-----+-----+-----+-------+-------+-------+-------+ 1510 if (INSN(31, 29) == 0b010) { 1511 // MIPS64 COP1X Encoding of Function Field of memory access instructions. 1512 // +--------+-----------------------------------------------------+ 1513 // | bits | 2..0 | 1514 // +--------+-------+-------+-----+-----+-----+-------+-----+-----+ 1515 // | 5..3 | 000 | 001 | 010 | 011 | 100 | 101 | 110 | 111 | 1516 // +--------+-------+-------+-----+-----+-----+-------+-----+-----+ 1517 // | 000 | LWXC1 | LDXC1 | | | | LUXC1 | | | 1518 // | 001 | SWXC1 | SDXC1 | | | | SUXC1 | | | 1519 // +--------+-------+-------+-----+-----+-----+-------+-----+-----+ 1520 switch (INSN(5, 0)) { 1521 // lwxc1 1522 case 0b000000: 1523 return Some(TrapMachineInsn::Load32); 1524 // ldxc1 1525 case 0b000001: 1526 // luxc1 1527 case 0b000101: 1528 return Some(TrapMachineInsn::Load64); 1529 // swxc1 1530 case 0b001000: 1531 return Some(TrapMachineInsn::Store32); 1532 // sdxc1 1533 case 0b001001: 1534 // suxc1 1535 case 0b001101: 1536 return Some(TrapMachineInsn::Store64); 1537 default: 1538 break; 1539 } 1540 } else if (INSN(31, 29) == 0b011) { 1541 switch (INSN(28, 26)) { 1542 // ldl 1543 case 0b010: 1544 // ldr 1545 case 0b011: 1546 return Some(TrapMachineInsn::Load64); 1547 default: 1548 break; 1549 } 1550 } else if (INSN(31, 29) == 0b100) { 1551 switch (INSN(28, 26)) { 1552 // lb 1553 case 0b000: 1554 return Some(TrapMachineInsn::Load8); 1555 // lh 1556 case 0b001: 1557 return Some(TrapMachineInsn::Load16); 1558 // lwl 1559 case 0b010: 1560 // lw 1561 case 0b011: 1562 return Some(TrapMachineInsn::Load32); 1563 // lbu 1564 case 0b100: 1565 return Some(TrapMachineInsn::Load8); 1566 // lhu 1567 case 0b101: 1568 return Some(TrapMachineInsn::Load16); 1569 // lwr 1570 case 0b110: 1571 // lwu 1572 case 0b111: 1573 return Some(TrapMachineInsn::Load32); 1574 } 1575 } else if (INSN(31, 29) == 0b101) { 1576 switch (INSN(28, 26)) { 1577 // sb 1578 case 0b000: 1579 return Some(TrapMachineInsn::Store8); 1580 // sh 1581 case 0b001: 1582 return Some(TrapMachineInsn::Store16); 1583 // swl 1584 case 0b010: 1585 // sw 1586 case 0b011: 1587 return Some(TrapMachineInsn::Store32); 1588 // sdl 1589 case 0b100: 1590 // sdr 1591 case 0b101: 1592 return Some(TrapMachineInsn::Store64); 1593 // swr 1594 case 0b110: 1595 return Some(TrapMachineInsn::Store32); 1596 // cache 1597 case 0b111: 1598 break; 1599 } 1600 } else if (INSN(31, 29) == 0b110) { 1601 switch (INSN(28, 26)) { 1602 // ll 1603 case 0b000: 1604 // lwc1 1605 case 0b001: 1606 return Some(TrapMachineInsn::Load32); 1607 // lwc2 1608 case 0b010: 1609 if (jit::isLoongson()) { 1610 switch (INSN(2, 0)) { 1611 // gslsl 1612 case 0b100: 1613 // gslsr 1614 case 0b101: 1615 return Some(TrapMachineInsn::Load32); 1616 // gsldl 1617 case 0b110: 1618 // gsldr 1619 case 0b111: 1620 return Some(TrapMachineInsn::Load64); 1621 // invalid 1622 default: 1623 return Nothing(); 1624 } 1625 } 1626 return Some(TrapMachineInsn::Load32); 1627 // pref 1628 case 0b011: 1629 break; 1630 // lld 1631 case 0b100: 1632 // ldc1 1633 case 0b101: 1634 return Some(TrapMachineInsn::Load64); 1635 // ldc2 1636 case 0b110: 1637 if (jit::isLoongson()) { 1638 switch (INSN(2, 0)) { 1639 // gslbx 1640 case 0b000: 1641 return Some(TrapMachineInsn::Load8); 1642 // gslhx 1643 case 0b001: 1644 return Some(TrapMachineInsn::Load16); 1645 // gslwx 1646 case 0b010: 1647 // gslwx (float) 1648 case 0b110: 1649 return Some(TrapMachineInsn::Load32); 1650 // gsldx 1651 case 0b011: 1652 // gsldx (double) 1653 case 0b111: 1654 return Some(TrapMachineInsn::Load64); 1655 // invalid 1656 default: 1657 return Nothing(); 1658 } 1659 } 1660 return Some(TrapMachineInsn::Load64); 1661 // ld 1662 case 0b111: 1663 return Some(TrapMachineInsn::Load64); 1664 } 1665 } else if (INSN(31, 29) == 0b111) { 1666 switch (INSN(28, 26)) { 1667 // sc 1668 case 0b000: 1669 // swc1 1670 case 0b001: 1671 return Some(TrapMachineInsn::Store32); 1672 // swc2 1673 case 0b010: 1674 if (jit::isLoongson()) { 1675 switch (INSN(2, 0)) { 1676 // gsssl 1677 case 0b100: 1678 // gsssr 1679 case 0b101: 1680 return Some(TrapMachineInsn::Store32); 1681 // gssdl 1682 case 0b110: 1683 // gssdr 1684 case 0b111: 1685 return Some(TrapMachineInsn::Store64); 1686 // invalid 1687 default: 1688 return Nothing(); 1689 } 1690 } 1691 return Some(TrapMachineInsn::Store32); 1692 // reserved encoding 1693 case 0b011: 1694 break; 1695 // scd 1696 case 0b100: 1697 // sdc1 1698 case 0b101: 1699 return Some(TrapMachineInsn::Store64); 1700 // sdc2 1701 case 0b110: 1702 if (jit::isLoongson()) { 1703 switch (INSN(2, 0)) { 1704 // gssbx 1705 case 0b000: 1706 return Some(TrapMachineInsn::Store8); 1707 // gsshx 1708 case 0b001: 1709 return Some(TrapMachineInsn::Store16); 1710 // gsswx 1711 case 0b010: 1712 // gsswx (float) 1713 case 0b110: 1714 return Some(TrapMachineInsn::Store32); 1715 // gssdx 1716 case 0b011: 1717 // gssdx (double) 1718 case 0b111: 1719 return Some(TrapMachineInsn::Store64); 1720 // invalid 1721 default: 1722 return Nothing(); 1723 } 1724 } 1725 return Some(TrapMachineInsn::Store64); 1726 // sd 1727 case 0b111: 1728 return Some(TrapMachineInsn::Store64); 1729 } 1730 } 1731 1732 # undef INSN 1733 return Nothing(); 1734 } 1735 1736 // ================================================================== none ==== 1737 1738 # elif defined(JS_CODEGEN_NONE) 1739 1740 Maybe<TrapMachineInsn> SummarizeTrapInstruction(const uint8_t* insnAddr) { 1741 MOZ_CRASH(); 1742 } 1743 1744 // ================================================================= other ==== 1745 1746 # else 1747 1748 # error "SummarizeTrapInstruction: not implemented on this architecture" 1749 1750 # endif // defined(JS_CODEGEN_*) 1751 1752 #endif // defined(DEBUG) 1753 1754 } // namespace wasm 1755 } // namespace js