1 /** 2 * Declarations for ptrntab.d, the instruction tables for the inline assembler. 3 * 4 * Copyright: Copyright (C) 1982-1998 by Symantec 5 * Copyright (C) 2000-2023 by The D Language Foundation, All Rights Reserved 6 * Authors: Mike Cote, John Micco, $(LINK2 https://www.digitalmars.com, Walter Bright), 7 * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 8 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/backend/iasm.d, backend/iasm.d) 9 * Documentation: https://dlang.org/phobos/dmd_backend_iasm.html 10 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/backend/iasm.d 11 */ 12 13 module dmd.backend.iasm; 14 15 // Online documentation: https://dlang.org/phobos/dmd_backend_iasm.html 16 17 import dmd.backend.cc : block; 18 import dmd.backend.code_x86 : opcode_t; 19 20 extern (C++): 21 @nogc: 22 nothrow: 23 24 //#include <setjmp.h> 25 26 ///////////////////////////////////////////////// 27 // Instruction flags (usFlags) 28 // 29 // 30 31 enum _modrm = 0x10; 32 33 // This is for when the reg field of modregrm specifies which instruction it is 34 enum 35 { 36 NUM_MASK = 0x7, 37 NUM_MASKR = 0x8, // for REX extended registers 38 _0 = (0x0 | _modrm), // insure that some _modrm bit is set 39 _1 = 0x1, // with _0 40 _2 = 0x2, 41 _3 = 0x3, 42 _4 = 0x4, 43 _5 = 0x5, 44 _6 = 0x6, 45 _7 = 0x7, 46 } 47 48 enum 49 { 50 _r = _modrm, 51 _cb = _modrm, 52 _cw = _modrm, 53 _cd = _modrm, 54 _cq = _modrm, 55 _cp = _modrm, 56 _ib = 0, 57 _iw = 0, 58 _id = 0, 59 _rb = 0, 60 _rw = 0, 61 _rd = 0, 62 _16_bit = 0x20, 63 _32_bit = 0x40, 64 _64_bit = 0x10000, 65 _i64_bit = 0x20000, // opcode is invalid in 64bit mode 66 _I386 = 0x80, // opcode is only for 386 and later 67 _16_bit_addr = 0x100, 68 _32_bit_addr = 0x200, 69 _fwait = 0x400, // Add an FWAIT prior to the instruction opcode 70 _nfwait = 0x800, // Do not add an FWAIT prior to the instruction 71 } 72 73 enum 74 { 75 MOD_MASK = 0xF000, // Mod mask 76 _modsi = 0x1000, // Instruction modifies SI 77 _moddx = 0x2000, // Instruction modifies DX 78 _mod2 = 0x3000, // Instruction modifies second operand 79 _modax = 0x4000, // Instruction modifies AX 80 _modnot1 = 0x5000, // Instruction does not modify first operand 81 _modaxdx = 0x6000, // instruction modifies AX and DX 82 _moddi = 0x7000, // Instruction modifies DI 83 _modsidi = 0x8000, // Instruction modifies SI and DI 84 _modcx = 0x9000, // Instruction modifies CX 85 _modes = 0xa000, // Instruction modifies ES 86 _modall = 0xb000, // Instruction modifies all register values 87 _modsiax = 0xc000, // Instruction modifies AX and SI 88 _modsinot1 = 0xd000, // Instruction modifies SI and not first param 89 _modcxr11 = 0xe000, // Instruction modifies CX and R11 90 _modxmm0 = 0xf000, // Instruction modifies XMM0 91 } 92 93 // translates opcode into equivalent vex encoding 94 uint VEX_128_W0(opcode_t op) { return _VEX(op)|_VEX_NOO; } 95 uint VEX_128_W1(opcode_t op) { return _VEX(op)|_VEX_NOO|_VEX_W; } 96 uint VEX_128_WIG(opcode_t op) { return VEX_128_W0(op); } 97 uint VEX_256_W0(opcode_t op) { return _VEX(op)|_VEX_NOO|_VEX_L; } 98 uint VEX_256_W1(opcode_t op) { return _VEX(op)|_VEX_NOO|_VEX_W|_VEX_L; } 99 uint VEX_256_WIG(opcode_t op) { return VEX_256_W0(op); } 100 uint VEX_NDS_128_W0(opcode_t op) { return _VEX(op)|_VEX_NDS; } 101 uint VEX_NDS_128_W1(opcode_t op) { return _VEX(op)|_VEX_NDS|_VEX_W; } 102 uint VEX_NDS_128_WIG(opcode_t op) { return VEX_NDS_128_W0(op); } 103 uint VEX_NDS_256_W0(opcode_t op) { return _VEX(op)|_VEX_NDS|_VEX_L; } 104 uint VEX_NDS_256_W1(opcode_t op) { return _VEX(op)|_VEX_NDS|_VEX_W|_VEX_L; } 105 uint VEX_NDS_256_WIG(opcode_t op) { return VEX_NDS_256_W0(op); } 106 uint VEX_NDD_128_W0(opcode_t op) { return _VEX(op)|_VEX_NDD; } 107 uint VEX_NDD_128_W1(opcode_t op) { return _VEX(op)|_VEX_NDD|_VEX_W; } 108 uint VEX_NDD_128_WIG(opcode_t op) { return VEX_NDD_128_W0(op); } 109 uint VEX_NDD_256_W0(opcode_t op) { return _VEX(op)|_VEX_NDD|_VEX_L; } 110 uint VEX_NDD_256_W1(opcode_t op) { return _VEX(op)|_VEX_NDD|_VEX_W|_VEX_L; } 111 uint VEX_NDD_256_WIG(opcode_t op) { return VEX_NDD_256_W0(op); } 112 uint VEX_DDS_128_W0(opcode_t op) { return _VEX(op)|_VEX_DDS; } 113 uint VEX_DDS_128_W1(opcode_t op) { return _VEX(op)|_VEX_DDS|_VEX_W; } 114 uint VEX_DDS_128_WIG(opcode_t op) { return VEX_DDS_128_W0(op); } 115 uint VEX_DDS_256_W0(opcode_t op) { return _VEX(op)|_VEX_DDS|_VEX_L; } 116 uint VEX_DDS_256_W1(opcode_t op) { return _VEX(op)|_VEX_DDS|_VEX_W|_VEX_L; } 117 uint VEX_DDS_256_WIG(opcode_t op) { return VEX_DDS_256_W0(op); } 118 119 enum _VEX_W = 0x8000; 120 /* Don't encode LIG/LZ use 128 for these. 121 */ 122 enum _VEX_L = 0x0400; 123 /* Encode nds, ndd, dds in the vvvv field, it gets 124 * overwritten with the actual register later. 125 */ 126 enum 127 { 128 VEX_NOO = 0, // neither of nds, ndd, dds 129 VEX_NDS = 1, 130 VEX_NDD = 2, 131 VEX_DDS = 3, 132 _VEX_NOO = VEX_NOO << 11, 133 _VEX_NDS = VEX_NDS << 11, 134 _VEX_NDD = VEX_NDD << 11, 135 _VEX_DDS = VEX_DDS << 11, 136 } 137 138 uint _VEX(opcode_t op) { return (0xC4 << 24) | _VEX_MM(op >> 8) | (op & 0xFF); } 139 140 uint _VEX_MM(opcode_t op) 141 { 142 return 143 (op & 0x00FF) == 0x000F ? (0x1 << 16 | _VEX_PP(op >> 8)) : 144 (op & 0xFFFF) == 0x0F38 ? (0x2 << 16 | _VEX_PP(op >> 16)) : 145 (op & 0xFFFF) == 0x0F3A ? (0x3 << 16 | _VEX_PP(op >> 16)) : 146 _VEX_ASSERT0; 147 } 148 149 uint _VEX_PP(opcode_t op) 150 { 151 return 152 op == 0x00 ? 0x00 << 8 : 153 op == 0x66 ? 0x01 << 8 : 154 op == 0xF3 ? 0x02 << 8 : 155 op == 0xF2 ? 0x03 << 8 : 156 _VEX_ASSERT0; 157 } 158 159 // avoid dynamic initialization of the asm tables 160 debug 161 { 162 @property uint _VEX_ASSERT0() { assert(0); } 163 } 164 else 165 { 166 @property uint _VEX_ASSERT0() { return 0; } 167 } 168 169 170 ///////////////////////////////////////////////// 171 // Operand flags - usOp1, usOp2, usOp3 172 // 173 174 alias opflag_t = uint; 175 176 // Operand flags for normal opcodes 177 enum 178 { 179 _r8 = CONSTRUCT_FLAGS(OpndSize._8, _reg, _normal, 0 ), 180 _r16 = CONSTRUCT_FLAGS(OpndSize._16, _reg, _normal, 0 ), 181 _r32 = CONSTRUCT_FLAGS(OpndSize._32, _reg, _normal, 0 ), 182 _r64 = CONSTRUCT_FLAGS(OpndSize._64, _reg, _normal, 0 ), 183 _m8 = CONSTRUCT_FLAGS(OpndSize._8, _m, _normal, 0 ), 184 _m16 = CONSTRUCT_FLAGS(OpndSize._16, _m, _normal, 0 ), 185 _m32 = CONSTRUCT_FLAGS(OpndSize._32, _m, _normal, 0 ), 186 _m48 = CONSTRUCT_FLAGS(OpndSize._48, _m, _normal, 0 ), 187 _m64 = CONSTRUCT_FLAGS(OpndSize._64, _m, _normal, 0 ), 188 _m128 = CONSTRUCT_FLAGS(OpndSize._128, _m, _normal, 0 ), 189 _m256 = CONSTRUCT_FLAGS(OpndSize._anysize, _m, _normal, 0 ), 190 _m48_32_16_8 = CONSTRUCT_FLAGS(OpndSize._48_32_16_8, _m, _normal, 0 ), 191 _m64_48_32_16_8 = CONSTRUCT_FLAGS(OpndSize._64_48_32_16_8, _m, _normal, 0 ), 192 _rm8 = CONSTRUCT_FLAGS(OpndSize._8, _rm, _normal, 0 ), 193 _rm16 = CONSTRUCT_FLAGS(OpndSize._16, _rm, _normal, 0 ), 194 _rm32 = CONSTRUCT_FLAGS(OpndSize._32, _rm, _normal, 0), 195 _rm64 = CONSTRUCT_FLAGS(OpndSize._64, _rm, _normal, 0), 196 _r32m8 = CONSTRUCT_FLAGS(OpndSize._32_8, _rm, _normal, 0), 197 _r32m16 = CONSTRUCT_FLAGS(OpndSize._32_16, _rm, _normal, 0), 198 _regm8 = CONSTRUCT_FLAGS(OpndSize._64_32_8, _rm, _normal, 0), 199 _imm8 = CONSTRUCT_FLAGS(OpndSize._8, _imm, _normal, 0 ), 200 _imm16 = CONSTRUCT_FLAGS(OpndSize._16, _imm, _normal, 0), 201 _imm32 = CONSTRUCT_FLAGS(OpndSize._32, _imm, _normal, 0), 202 _imm64 = CONSTRUCT_FLAGS(OpndSize._64, _imm, _normal, 0), 203 _rel8 = CONSTRUCT_FLAGS(OpndSize._8, _rel, _normal, 0), 204 _rel16 = CONSTRUCT_FLAGS(OpndSize._16, _rel, _normal, 0), 205 _rel32 = CONSTRUCT_FLAGS(OpndSize._32, _rel, _normal, 0), 206 _p1616 = CONSTRUCT_FLAGS(OpndSize._32, _p, _normal, 0), 207 _m1616 = CONSTRUCT_FLAGS(OpndSize._32, _mnoi, _normal, 0), 208 _p1632 = CONSTRUCT_FLAGS(OpndSize._48, _p, _normal, 0 ), 209 _m1632 = CONSTRUCT_FLAGS(OpndSize._48, _mnoi, _normal, 0), 210 _special = CONSTRUCT_FLAGS( 0, 0, _rspecial, 0 ), 211 _seg = CONSTRUCT_FLAGS( 0, 0, _rseg, 0 ), 212 _a16 = CONSTRUCT_FLAGS( 0, 0, _addr16, 0 ), 213 _a32 = CONSTRUCT_FLAGS( 0, 0, _addr32, 0 ), 214 _f16 = CONSTRUCT_FLAGS( 0, 0, _fn16, 0), 215 // Near function pointer 216 _f32 = CONSTRUCT_FLAGS( 0, 0, _fn32, 0), 217 // Far function pointer 218 _lbl = CONSTRUCT_FLAGS( 0, 0, _flbl, 0 ), 219 // Label (in current function) 220 221 _mmm32 = CONSTRUCT_FLAGS( 0, _m, 0, OpndSize._32), 222 _mmm64 = CONSTRUCT_FLAGS( OpndSize._64, _m, 0, _f64), 223 _mmm128 = CONSTRUCT_FLAGS( 0, _m, 0, _f128), 224 225 _xmm_m16 = CONSTRUCT_FLAGS( OpndSize._16, _m, _rspecial, ASM_GET_uRegmask(_xmm)), 226 _xmm_m32 = CONSTRUCT_FLAGS( OpndSize._32, _m, _rspecial, ASM_GET_uRegmask(_xmm)), 227 _xmm_m64 = CONSTRUCT_FLAGS( OpndSize._anysize, _m, _rspecial, ASM_GET_uRegmask(_xmm)), 228 _xmm_m128 = CONSTRUCT_FLAGS( OpndSize._128, _m, _rspecial, ASM_GET_uRegmask(_xmm)), 229 _ymm_m256 = CONSTRUCT_FLAGS( OpndSize._anysize, _m, _rspecial, ASM_GET_uRegmask(_ymm)), 230 231 _moffs8 = _rel8, 232 _moffs16 = _rel16, 233 _moffs32 = _rel32, 234 } 235 236 //////////////////////////////////////////////////////////////////// 237 // Operand flags for floating point opcodes are all just aliases for 238 // normal opcode variants and only asm_determine_operator_flags should 239 // need to care. 240 241 enum 242 { 243 _fm80 = CONSTRUCT_FLAGS( 0, _m, 0, _f80 ), 244 _fm64 = CONSTRUCT_FLAGS( 0, _m, 0, _f64 ), 245 _fm128 = CONSTRUCT_FLAGS( 0, _m, 0, _f128 ), 246 _fanysize = (_f64 | _f80 | _f112 ), 247 248 _float_m = CONSTRUCT_FLAGS( OpndSize._anysize, _float, 0, _fanysize), 249 250 _st = CONSTRUCT_FLAGS( 0, _float, 0, _rst ), // stack register 0 251 _m112 = CONSTRUCT_FLAGS( 0, _m, 0, _f112 ), 252 _m224 = _m112, 253 _m512 = _m224, 254 _sti = CONSTRUCT_FLAGS( 0, _float, 0, _rsti ), 255 } 256 257 ////////////////// FLAGS ///////////////////////////////////// 258 259 // bit size 5 3 3 7 260 opflag_t CONSTRUCT_FLAGS(uint uSizemask, uint aopty, uint amod, uint uRegmask) 261 { 262 return uSizemask | (aopty << 5) | (amod << 8) | (uRegmask << 11); 263 } 264 265 uint ASM_GET_aopty(uint us) { return cast(ASM_OPERAND_TYPE)((us >> 5) & 7); } 266 uint ASM_GET_amod(uint us) { return cast(ASM_MODIFIERS)((us >> 8) & 7); } 267 uint ASM_GET_uRegmask(uint us) { return (us >> 11) & 0x7F; } 268 269 // For uSizemask (5 bits) 270 enum OpndSize : ubyte 271 { 272 none = 0, 273 274 _8, // 0x1, 275 _16, // 0x2, 276 _32, // 0x4, 277 _48, // 0x8, 278 _64, // 0x10, 279 _128, // 0x20, 280 281 _16_8, // _16 | _8, 282 _32_8, // _32 | _8, 283 _32_16, // _32 | _16, 284 _32_16_8, // _32 | _16 | _8, 285 _48_32, // _48 | _32, 286 _48_32_16_8, // _48 | _32 | _16 | _8, 287 _64_32, // _64 | _32, 288 _64_32_8, // _64 | _32 | _8, 289 _64_32_16, // _64 | _32 | _16, 290 _64_32_16_8, // _64 | _32 | _16 | _8, 291 _64_48_32_16_8, // _64 | _48 | _32 | _16 | _8, 292 293 _anysize, 294 } 295 296 /************************************* 297 * Extract OpndSize from opflag_t. 298 */ 299 OpndSize getOpndSize(opflag_t us) { return cast(OpndSize) (us & 0x1F); } 300 301 // For aopty (3 bits) 302 alias ASM_OPERAND_TYPE = uint; 303 enum 304 { 305 _reg, // _r8, _r16, _r32 306 _m, // _m8, _m16, _m32, _m48 307 _imm, // _imm8, _imm16, _imm32, _imm64 308 _rel, // _rel8, _rel16, _rel32 309 _mnoi, // _m1616, _m1632 310 _p, // _p1616, _p1632 311 _rm, // _rm8, _rm16, _rm32 312 _float // Floating point operand, look at cRegmask for the 313 // actual size 314 } 315 316 // For amod (3 bits) 317 alias ASM_MODIFIERS = uint; 318 enum 319 { 320 _normal, // Normal register value 321 _rseg, // Segment registers 322 _rspecial, // Special registers 323 _addr16, // 16 bit address 324 _addr32, // 32 bit address 325 _fn16, // 16 bit function call 326 _fn32, // 32 bit function call 327 _flbl // Label 328 } 329 330 // For uRegmask (7 bits) 331 332 // uRegmask flags when aopty == _float 333 enum 334 { 335 _rst = 0x1, 336 _rsti = 0x2, 337 _f64 = 0x4, 338 _f80 = 0x8, 339 _f112 = 0x10, 340 _f128 = 0x20, 341 } 342 343 // _seg register values (amod == _rseg) 344 // 345 enum 346 { 347 _ds = CONSTRUCT_FLAGS( 0, 0, _rseg, 0x01 ), 348 _es = CONSTRUCT_FLAGS( 0, 0, _rseg, 0x02 ), 349 _ss = CONSTRUCT_FLAGS( 0, 0, _rseg, 0x04 ), 350 _fs = CONSTRUCT_FLAGS( 0, 0, _rseg, 0x08 ), 351 _gs = CONSTRUCT_FLAGS( 0, 0, _rseg, 0x10 ), 352 _cs = CONSTRUCT_FLAGS( 0, 0, _rseg, 0x20 ), 353 } 354 355 // 356 // _special register values 357 // 358 enum 359 { 360 _crn = CONSTRUCT_FLAGS( 0, 0, _rspecial, 0x01 ), // CRn register (0,2,3) 361 _drn = CONSTRUCT_FLAGS( 0, 0, _rspecial, 0x02 ), // DRn register (0-3,6-7) 362 _trn = CONSTRUCT_FLAGS( 0, 0, _rspecial, 0x04 ), // TRn register (3-7) 363 _mm = CONSTRUCT_FLAGS( 0, 0, _rspecial, 0x08 ), // MMn register (0-7) 364 _xmm = CONSTRUCT_FLAGS( 0, 0, _rspecial, 0x10 ), // XMMn register (0-7) 365 _xmm0 = CONSTRUCT_FLAGS( 0, 0, _rspecial, 0x20 ), // XMM0 register 366 _ymm = CONSTRUCT_FLAGS( 0, 0, _rspecial, 0x40 ), // YMMn register (0-15) 367 } 368 369 // 370 // Default register values 371 // 372 enum 373 { 374 _al = CONSTRUCT_FLAGS( 0, 0, _normal, 0x01 ), // AL register 375 _ax = CONSTRUCT_FLAGS( 0, 0, _normal, 0x02 ), // AX register 376 _eax = CONSTRUCT_FLAGS( 0, 0, _normal, 0x04 ), // EAX register 377 _dx = CONSTRUCT_FLAGS( 0, 0, _normal, 0x08 ), // DX register 378 _cl = CONSTRUCT_FLAGS( 0, 0, _normal, 0x10 ), // CL register 379 _rax = CONSTRUCT_FLAGS( 0, 0, _normal, 0x40 ), // RAX register 380 } 381 382 383 enum _rplus_r = 0x20; 384 enum _plus_r = CONSTRUCT_FLAGS( 0, 0, 0, _rplus_r ); 385 // Add the register to the opcode (no mod r/m) 386 387 388 389 ////////////////////////////////////////////////////////////////// 390 391 enum 392 { 393 ITprefix = 0x10, // special prefix 394 ITjump = 0x20, // jump instructions CALL, Jxx and LOOPxx 395 ITimmed = 0x30, // value of an immediate operand controls 396 // code generation 397 ITopt = 0x40, // not all operands are required 398 ITshift = 0x50, // rotate and shift instructions 399 ITfloat = 0x60, // floating point coprocessor instructions 400 ITdata = 0x70, // DB, DW, DD, DQ, DT pseudo-ops 401 ITaddr = 0x80, // DA (define addresss) pseudo-op 402 ITMASK = 0xF0, 403 ITSIZE = 0x0F, // mask for size 404 } 405 406 version (SCPP) 407 { 408 alias OP_DB = int; 409 enum 410 { 411 // These are the number of bytes 412 OPdb = 1, 413 OPdw = 2, 414 OPdd = 4, 415 OPdq = 8, 416 OPdt = 10, 417 OPdf = 4, 418 OPde = 10, 419 OPds = 2, 420 OPdi = 4, 421 OPdl = 8, 422 } 423 } 424 version (MARS) 425 { 426 alias OP_DB = int; 427 enum 428 { 429 // Integral types 430 OPdb, 431 OPds, 432 OPdi, 433 OPdl, 434 435 // Float types 436 OPdf, 437 OPdd, 438 OPde, 439 440 // Deprecated 441 OPdw = OPds, 442 OPdq = OPdl, 443 OPdt = OPde, 444 } 445 } 446 447 448 /* from iasm.c */ 449 int asm_state(int iFlags); 450 451 void asm_process_fixup( block **ppblockLabels ); 452 453 struct PTRNTAB4 454 { 455 opcode_t opcode; 456 uint usFlags; 457 opflag_t usOp1; 458 opflag_t usOp2; 459 opflag_t usOp3; 460 opflag_t usOp4; 461 } 462 463 struct PTRNTAB3 { 464 opcode_t opcode; 465 uint usFlags; 466 opflag_t usOp1; 467 opflag_t usOp2; 468 opflag_t usOp3; 469 } 470 471 struct PTRNTAB2 { 472 opcode_t opcode; 473 uint usFlags; 474 opflag_t usOp1; 475 opflag_t usOp2; 476 } 477 478 struct PTRNTAB1 { 479 opcode_t opcode; 480 uint usFlags; 481 opflag_t usOp1; 482 } 483 484 enum ASM_END = 0xffff; // special opcode meaning end of PTRNTABx table 485 486 struct PTRNTAB0 { 487 opcode_t opcode; 488 uint usFlags; 489 } 490 491 union PTRNTAB { 492 void *ppt; 493 PTRNTAB0 *pptb0; 494 PTRNTAB1 *pptb1; 495 PTRNTAB2 *pptb2; 496 PTRNTAB3 *pptb3; 497 PTRNTAB4 *pptb4; 498 } 499 500 struct OP 501 { 502 string str; // opcode string 503 ubyte usNumops; 504 PTRNTAB ptb; 505 }