1 /** 2 * Inline assembler implementation for DMD. 3 * https://dlang.org/spec/iasm.html 4 * 5 * Copyright: Copyright (c) 1992-1999 by Symantec 6 * Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved 7 * Authors: Mike Cote, John Micco and $(LINK2 http://www.digitalmars.com, Walter Bright) 8 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 9 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/iasmdmd.d, _iasmdmd.d) 10 * Documentation: https://dlang.org/phobos/dmd_iasmdmd.html 11 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/iasmdmd.d 12 */ 13 14 module dmd.iasmdmd; 15 16 import core.stdc.stdio; 17 import core.stdc.stdarg; 18 import core.stdc.stdlib; 19 import core.stdc.string; 20 21 import dmd.declaration; 22 import dmd.denum; 23 import dmd.dscope; 24 import dmd.dsymbol; 25 import dmd.errors; 26 import dmd.expression; 27 import dmd.expressionsem; 28 import dmd.globals; 29 import dmd.id; 30 import dmd.identifier; 31 import dmd.init; 32 import dmd.mtype; 33 import dmd.optimize; 34 import dmd.statement; 35 import dmd.target; 36 import dmd.tokens; 37 38 import dmd.root.ctfloat; 39 import dmd.root.rmem; 40 import dmd.root.rootobject; 41 42 import dmd.backend.cc; 43 import dmd.backend.cdef; 44 import dmd.backend.code; 45 import dmd.backend.code_x86; 46 import dmd.backend.codebuilder : CodeBuilder; 47 import dmd.backend.global; 48 import dmd.backend.iasm; 49 import dmd.backend.ptrntab : asm_opstr, asm_op_lookup, init_optab; 50 import dmd.backend.xmm; 51 52 //debug = EXTRA_DEBUG; 53 //debug = debuga; 54 55 /******************************* 56 * Clean up iasm things before exiting the compiler. 57 * Currently not called. 58 */ 59 60 version (none) 61 public void iasm_term() 62 { 63 if (asmstate.bInit) 64 { 65 asmstate.psDollar = null; 66 asmstate.psLocalsize = null; 67 asmstate.bInit = false; 68 } 69 } 70 71 /************************ 72 * Perform semantic analysis on InlineAsmStatement. 73 * Params: 74 * s = inline asm statement 75 * sc = context 76 * Returns: 77 * `s` on success, ErrorStatement if errors happened 78 */ 79 public Statement inlineAsmSemantic(InlineAsmStatement s, Scope *sc) 80 { 81 //printf("InlineAsmStatement.semantic()\n"); 82 83 OP *o; 84 OPND[4] opnds; 85 int nOps; 86 PTRNTAB ptb; 87 int usNumops; 88 89 asmstate.ucItype = 0; 90 asmstate.bReturnax = false; 91 asmstate.lbracketNestCount = 0; 92 asmstate.errors = false; 93 94 asmstate.statement = s; 95 asmstate.sc = sc; 96 97 version (none) // don't use bReturnax anymore, and will fail anyway if we use return type inference 98 { 99 // Scalar return values will always be in AX. So if it is a scalar 100 // then asm block sets return value if it modifies AX, if it is non-scalar 101 // then always assume that the ASM block sets up an appropriate return 102 // value. 103 104 asmstate.bReturnax = true; 105 if (sc.func.type.nextOf().isscalar()) 106 asmstate.bReturnax = false; 107 } 108 109 if (!asmstate.bInit) 110 { 111 asmstate.bInit = true; 112 init_optab(); 113 asmstate.psDollar = LabelDsymbol.create(Id._dollar); 114 asmstate.psLocalsize = Dsymbol.create(Id.__LOCAL_SIZE); 115 } 116 117 asmstate.loc = s.loc; 118 119 asmstate.tok = s.tokens; 120 asm_token_trans(asmstate.tok); 121 122 switch (asmstate.tokValue) 123 { 124 case cast(TOK)ASMTKnaked: 125 s.naked = true; 126 sc.func.naked = true; 127 asm_token(); 128 break; 129 130 case cast(TOK)ASMTKeven: 131 asm_token(); 132 s.asmalign = 2; 133 break; 134 135 case TOK.align_: 136 { 137 asm_token(); 138 uint _align = asm_getnum(); 139 if (ispow2(_align) == -1) 140 { 141 asmerr("`align %d` must be a power of 2", _align); 142 goto AFTER_EMIT; 143 } 144 else 145 s.asmalign = _align; 146 break; 147 } 148 149 // The following three convert the keywords 'int', 'in', 'out' 150 // to identifiers, since they are x86 instructions. 151 case TOK.int32: 152 o = asm_op_lookup(Id.__int.toChars()); 153 goto Lopcode; 154 155 case TOK.in_: 156 o = asm_op_lookup(Id.___in.toChars()); 157 goto Lopcode; 158 159 case TOK.out_: 160 o = asm_op_lookup(Id.___out.toChars()); 161 goto Lopcode; 162 163 case TOK.identifier: 164 o = asm_op_lookup(asmstate.tok.ident.toChars()); 165 if (!o) 166 goto OPCODE_EXPECTED; 167 168 Lopcode: 169 asmstate.ucItype = o.usNumops & ITMASK; 170 asm_token(); 171 if (o.usNumops > 4) 172 { 173 switch (asmstate.ucItype) 174 { 175 case ITdata: 176 s.asmcode = asm_db_parse(o); 177 goto AFTER_EMIT; 178 179 case ITaddr: 180 s.asmcode = asm_da_parse(o); 181 goto AFTER_EMIT; 182 183 default: 184 break; 185 } 186 } 187 // get the first part of an expr 188 if (asmstate.tokValue != TOK.endOfFile) 189 { 190 foreach (i; 0 .. 4) 191 { 192 asm_cond_exp(opnds[i]); 193 if (asmstate.errors) 194 goto AFTER_EMIT; 195 nOps = i + 1; 196 if (asmstate.tokValue != TOK.comma) 197 break; 198 asm_token(); 199 } 200 } 201 202 // match opcode and operands in ptrntab to verify legal inst and 203 // generate 204 205 ptb = asm_classify(o, opnds[0 .. nOps], usNumops); 206 if (asmstate.errors) 207 goto AFTER_EMIT; 208 209 assert(ptb.pptb0); 210 211 // 212 // The Multiply instruction takes 3 operands, but if only 2 are seen 213 // then the third should be the second and the second should 214 // be a duplicate of the first. 215 // 216 217 if (asmstate.ucItype == ITopt && 218 nOps == 2 && usNumops == 2 && 219 (ASM_GET_aopty(opnds[1].usFlags) == _imm) && 220 ((o.usNumops & ITSIZE) == 3)) 221 { 222 nOps = 3; 223 opnds[2] = opnds[1]; 224 opnds[1] = opnds[0]; 225 226 // Re-classify the opcode because the first classification 227 // assumed 2 operands. 228 229 ptb = asm_classify(o, opnds[0 .. nOps], usNumops); 230 } 231 else 232 { 233 version (none) 234 { 235 if (asmstate.ucItype == ITshift && (ptb.pptb2.usOp2 == 0 || 236 (ptb.pptb2.usOp2 & _cl))) 237 { 238 o2 = null; 239 usNumops = 1; 240 } 241 } 242 } 243 s.asmcode = asm_emit(s.loc, usNumops, ptb, o, opnds[0 .. nOps]); 244 break; 245 246 default: 247 OPCODE_EXPECTED: 248 asmerr("opcode expected, not `%s`", asmstate.tok.toChars()); 249 break; 250 } 251 252 AFTER_EMIT: 253 254 if (asmstate.tokValue != TOK.endOfFile) 255 { 256 asmerr("end of instruction expected, not `%s`", asmstate.tok.toChars()); // end of line expected 257 } 258 return asmstate.errors ? new ErrorStatement() : s; 259 } 260 261 /********************************** 262 * Called from back end. 263 * Params: bp = asm block 264 * Returns: mask of registers used by block bp. 265 */ 266 extern (C++) public regm_t iasm_regs(block *bp) 267 { 268 debug (debuga) 269 printf("Block iasm regs = 0x%X\n", bp.usIasmregs); 270 271 refparam |= bp.bIasmrefparam; 272 return bp.usIasmregs; 273 } 274 275 276 277 private: 278 279 enum ADDFWAIT = false; 280 281 282 // Additional tokens for the inline assembler 283 alias ASMTK = int; 284 enum 285 { 286 ASMTKlocalsize = TOK.max_ + 1, 287 ASMTKdword, 288 ASMTKeven, 289 ASMTKfar, 290 ASMTKnaked, 291 ASMTKnear, 292 ASMTKptr, 293 ASMTKqword, 294 ASMTKseg, 295 ASMTKword, 296 ASMTKmax = ASMTKword-(TOK.max_+1)+1 297 } 298 299 immutable char*[ASMTKmax] apszAsmtk = 300 [ 301 "__LOCAL_SIZE", 302 "dword", 303 "even", 304 "far", 305 "naked", 306 "near", 307 "ptr", 308 "qword", 309 "seg", 310 "word", 311 ]; 312 313 alias ucItype_t = ubyte; 314 enum 315 { 316 ITprefix = 0x10, /// special prefix 317 ITjump = 0x20, /// jump instructions CALL, Jxx and LOOPxx 318 ITimmed = 0x30, /// value of an immediate operand controls 319 /// code generation 320 ITopt = 0x40, /// not all operands are required 321 ITshift = 0x50, /// rotate and shift instructions 322 ITfloat = 0x60, /// floating point coprocessor instructions 323 ITdata = 0x70, /// DB, DW, DD, DQ, DT pseudo-ops 324 ITaddr = 0x80, /// DA (define addresss) pseudo-op 325 ITMASK = 0xF0, 326 ITSIZE = 0x0F, /// mask for size 327 } 328 329 struct ASM_STATE 330 { 331 ucItype_t ucItype; /// Instruction type 332 Loc loc; 333 bool bInit; 334 bool errors; /// true if semantic errors occurred 335 LabelDsymbol psDollar; 336 Dsymbol psLocalsize; 337 bool bReturnax; 338 InlineAsmStatement statement; 339 Scope* sc; 340 Token* tok; 341 TOK tokValue; 342 int lbracketNestCount; 343 } 344 345 __gshared ASM_STATE asmstate; 346 347 348 /** 349 * Describes a register 350 * 351 * This struct is only used for manifest constant 352 */ 353 struct REG 354 { 355 immutable: 356 string regstr; 357 ubyte val; 358 opflag_t ty; 359 360 bool isSIL_DIL_BPL_SPL() const 361 { 362 // Be careful as these have the same val's as AH CH DH BH 363 return ty == _r8 && 364 ((val == _SIL && regstr == "SIL") || 365 (val == _DIL && regstr == "DIL") || 366 (val == _BPL && regstr == "BPL") || 367 (val == _SPL && regstr == "SPL")); 368 } 369 } 370 371 immutable REG regFp = { "ST", 0, _st }; 372 373 immutable REG[8] aregFp = 374 [ 375 { "ST(0)", 0, _sti }, 376 { "ST(1)", 1, _sti }, 377 { "ST(2)", 2, _sti }, 378 { "ST(3)", 3, _sti }, 379 { "ST(4)", 4, _sti }, 380 { "ST(5)", 5, _sti }, 381 { "ST(6)", 6, _sti }, 382 { "ST(7)", 7, _sti } 383 ]; 384 385 386 enum // the x86 CPU numbers for these registers 387 { 388 _AL = 0, 389 _AH = 4, 390 _AX = 0, 391 _EAX = 0, 392 _BL = 3, 393 _BH = 7, 394 _BX = 3, 395 _EBX = 3, 396 _CL = 1, 397 _CH = 5, 398 _CX = 1, 399 _ECX = 1, 400 _DL = 2, 401 _DH = 6, 402 _DX = 2, 403 _EDX = 2, 404 _BP = 5, 405 _EBP = 5, 406 _SP = 4, 407 _ESP = 4, 408 _DI = 7, 409 _EDI = 7, 410 _SI = 6, 411 _ESI = 6, 412 _ES = 0, 413 _CS = 1, 414 _SS = 2, 415 _DS = 3, 416 _GS = 5, 417 _FS = 4, 418 } 419 420 immutable REG[71] regtab = 421 [ 422 {"AL", _AL, _r8 | _al}, 423 {"AH", _AH, _r8}, 424 {"AX", _AX, _r16 | _ax}, 425 {"EAX", _EAX, _r32 | _eax}, 426 {"BL", _BL, _r8}, 427 {"BH", _BH, _r8}, 428 {"BX", _BX, _r16}, 429 {"EBX", _EBX, _r32}, 430 {"CL", _CL, _r8 | _cl}, 431 {"CH", _CH, _r8}, 432 {"CX", _CX, _r16}, 433 {"ECX", _ECX, _r32}, 434 {"DL", _DL, _r8}, 435 {"DH", _DH, _r8}, 436 {"DX", _DX, _r16 | _dx}, 437 {"EDX", _EDX, _r32}, 438 {"BP", _BP, _r16}, 439 {"EBP", _EBP, _r32}, 440 {"SP", _SP, _r16}, 441 {"ESP", _ESP, _r32}, 442 {"DI", _DI, _r16}, 443 {"EDI", _EDI, _r32}, 444 {"SI", _SI, _r16}, 445 {"ESI", _ESI, _r32}, 446 {"ES", _ES, _seg | _es}, 447 {"CS", _CS, _seg | _cs}, 448 {"SS", _SS, _seg | _ss }, 449 {"DS", _DS, _seg | _ds}, 450 {"GS", _GS, _seg | _gs}, 451 {"FS", _FS, _seg | _fs}, 452 {"CR0", 0, _special | _crn}, 453 {"CR2", 2, _special | _crn}, 454 {"CR3", 3, _special | _crn}, 455 {"CR4", 4, _special | _crn}, 456 {"DR0", 0, _special | _drn}, 457 {"DR1", 1, _special | _drn}, 458 {"DR2", 2, _special | _drn}, 459 {"DR3", 3, _special | _drn}, 460 {"DR4", 4, _special | _drn}, 461 {"DR5", 5, _special | _drn}, 462 {"DR6", 6, _special | _drn}, 463 {"DR7", 7, _special | _drn}, 464 {"TR3", 3, _special | _trn}, 465 {"TR4", 4, _special | _trn}, 466 {"TR5", 5, _special | _trn}, 467 {"TR6", 6, _special | _trn}, 468 {"TR7", 7, _special | _trn}, 469 {"MM0", 0, _mm}, 470 {"MM1", 1, _mm}, 471 {"MM2", 2, _mm}, 472 {"MM3", 3, _mm}, 473 {"MM4", 4, _mm}, 474 {"MM5", 5, _mm}, 475 {"MM6", 6, _mm}, 476 {"MM7", 7, _mm}, 477 {"XMM0", 0, _xmm | _xmm0}, 478 {"XMM1", 1, _xmm}, 479 {"XMM2", 2, _xmm}, 480 {"XMM3", 3, _xmm}, 481 {"XMM4", 4, _xmm}, 482 {"XMM5", 5, _xmm}, 483 {"XMM6", 6, _xmm}, 484 {"XMM7", 7, _xmm}, 485 {"YMM0", 0, _ymm}, 486 {"YMM1", 1, _ymm}, 487 {"YMM2", 2, _ymm}, 488 {"YMM3", 3, _ymm}, 489 {"YMM4", 4, _ymm}, 490 {"YMM5", 5, _ymm}, 491 {"YMM6", 6, _ymm}, 492 {"YMM7", 7, _ymm}, 493 ]; 494 495 496 enum // 64 bit only registers 497 { 498 _RAX = 0, 499 _RBX = 3, 500 _RCX = 1, 501 _RDX = 2, 502 _RSI = 6, 503 _RDI = 7, 504 _RBP = 5, 505 _RSP = 4, 506 _R8 = 8, 507 _R9 = 9, 508 _R10 = 10, 509 _R11 = 11, 510 _R12 = 12, 511 _R13 = 13, 512 _R14 = 14, 513 _R15 = 15, 514 515 _R8D = 8, 516 _R9D = 9, 517 _R10D = 10, 518 _R11D = 11, 519 _R12D = 12, 520 _R13D = 13, 521 _R14D = 14, 522 _R15D = 15, 523 524 _R8W = 8, 525 _R9W = 9, 526 _R10W = 10, 527 _R11W = 11, 528 _R12W = 12, 529 _R13W = 13, 530 _R14W = 13, 531 _R15W = 15, 532 533 _SIL = 6, 534 _DIL = 7, 535 _BPL = 5, 536 _SPL = 4, 537 _R8B = 8, 538 _R9B = 9, 539 _R10B = 10, 540 _R11B = 11, 541 _R12B = 12, 542 _R13B = 13, 543 _R14B = 14, 544 _R15B = 15, 545 546 _RIP = 0xFF, // some unique value 547 } 548 549 immutable REG[65] regtab64 = 550 [ 551 {"RAX", _RAX, _r64 | _rax}, 552 {"RBX", _RBX, _r64}, 553 {"RCX", _RCX, _r64}, 554 {"RDX", _RDX, _r64}, 555 {"RSI", _RSI, _r64}, 556 {"RDI", _RDI, _r64}, 557 {"RBP", _RBP, _r64}, 558 {"RSP", _RSP, _r64}, 559 {"R8", _R8, _r64}, 560 {"R9", _R9, _r64}, 561 {"R10", _R10, _r64}, 562 {"R11", _R11, _r64}, 563 {"R12", _R12, _r64}, 564 {"R13", _R13, _r64}, 565 {"R14", _R14, _r64}, 566 {"R15", _R15, _r64}, 567 568 {"R8D", _R8D, _r32}, 569 {"R9D", _R9D, _r32}, 570 {"R10D", _R10D, _r32}, 571 {"R11D", _R11D, _r32}, 572 {"R12D", _R12D, _r32}, 573 {"R13D", _R13D, _r32}, 574 {"R14D", _R14D, _r32}, 575 {"R15D", _R15D, _r32}, 576 577 {"R8W", _R8W, _r16}, 578 {"R9W", _R9W, _r16}, 579 {"R10W", _R10W, _r16}, 580 {"R11W", _R11W, _r16}, 581 {"R12W", _R12W, _r16}, 582 {"R13W", _R13W, _r16}, 583 {"R14W", _R14W, _r16}, 584 {"R15W", _R15W, _r16}, 585 586 {"SIL", _SIL, _r8}, 587 {"DIL", _DIL, _r8}, 588 {"BPL", _BPL, _r8}, 589 {"SPL", _SPL, _r8}, 590 {"R8B", _R8B, _r8}, 591 {"R9B", _R9B, _r8}, 592 {"R10B", _R10B, _r8}, 593 {"R11B", _R11B, _r8}, 594 {"R12B", _R12B, _r8}, 595 {"R13B", _R13B, _r8}, 596 {"R14B", _R14B, _r8}, 597 {"R15B", _R15B, _r8}, 598 599 {"XMM8", 8, _xmm}, 600 {"XMM9", 9, _xmm}, 601 {"XMM10", 10, _xmm}, 602 {"XMM11", 11, _xmm}, 603 {"XMM12", 12, _xmm}, 604 {"XMM13", 13, _xmm}, 605 {"XMM14", 14, _xmm}, 606 {"XMM15", 15, _xmm}, 607 608 {"YMM8", 8, _ymm}, 609 {"YMM9", 9, _ymm}, 610 {"YMM10", 10, _ymm}, 611 {"YMM11", 11, _ymm}, 612 {"YMM12", 12, _ymm}, 613 {"YMM13", 13, _ymm}, 614 {"YMM14", 14, _ymm}, 615 {"YMM15", 15, _ymm}, 616 {"CR8", 8, _r64 | _special | _crn}, 617 {"RIP", _RIP, _r64}, 618 ]; 619 620 621 alias ASM_JUMPTYPE = int; 622 enum 623 { 624 ASM_JUMPTYPE_UNSPECIFIED, 625 ASM_JUMPTYPE_SHORT, 626 ASM_JUMPTYPE_NEAR, 627 ASM_JUMPTYPE_FAR 628 } 629 630 struct OPND 631 { 632 immutable(REG) *base; // if plain register 633 immutable(REG) *pregDisp1; // if [register1] 634 immutable(REG) *pregDisp2; 635 immutable(REG) *segreg; // if segment override 636 bool bOffset; // if 'offset' keyword 637 bool bSeg; // if 'segment' keyword 638 bool bPtr; // if 'ptr' keyword 639 bool bRIP; // if [RIP] addressing 640 uint uchMultiplier; // register multiplier; valid values are 0,1,2,4,8 641 opflag_t usFlags; 642 Dsymbol s; 643 targ_llong disp; 644 real_t vreal = 0.0; 645 Type ptype; 646 ASM_JUMPTYPE ajt; 647 } 648 649 650 /******************************* 651 */ 652 653 void asm_chktok(TOK toknum, const(char)* msg) 654 { 655 if (asmstate.tokValue != toknum) 656 { 657 /* When we run out of tokens, asmstate.tok is null. 658 * But when this happens when a ';' was hit. 659 */ 660 asmerr(msg, asmstate.tok ? asmstate.tok.toChars() : ";"); 661 } 662 asm_token(); // keep consuming tokens 663 } 664 665 666 /******************************* 667 */ 668 669 PTRNTAB asm_classify(OP *pop, OPND[] opnds, out int outNumops) 670 { 671 opflag_t[4] opflags; 672 bool bInvalid64bit = false; 673 674 bool bRetry = false; 675 676 // How many arguments are there? the parser is strictly left to right 677 // so this should work. 678 foreach (i, ref opnd; opnds) 679 { 680 opnd.usFlags = opflags[i] = asm_determine_operand_flags(opnd); 681 } 682 const usNumops = cast(int)opnds.length; 683 684 685 // Now check to insure that the number of operands is correct 686 auto usActual = (pop.usNumops & ITSIZE); 687 688 void paramError() 689 { 690 asmerr("%u operands found for `%s` instead of the expected %d", usNumops, asm_opstr(pop), usActual); 691 } 692 693 if (usActual != usNumops && asmstate.ucItype != ITopt && 694 asmstate.ucItype != ITfloat) 695 { 696 paramError(); 697 } 698 if (usActual < usNumops) 699 outNumops = usActual; 700 else 701 outNumops = usNumops; 702 703 704 void TYPE_SIZE_ERROR() 705 { 706 foreach (i, ref opnd; opnds) 707 { 708 if (ASM_GET_aopty(opnd.usFlags) == _reg) 709 continue; 710 711 opflags[i] = opnd.usFlags = (opnd.usFlags & ~0x1F) | OpndSize._anysize; 712 if(asmstate.ucItype != ITjump) 713 continue; 714 715 if (i == 0 && bRetry && opnd.s && !opnd.s.isLabel()) 716 { 717 asmerr("label expected", opnd.s.toChars()); 718 return; 719 } 720 opnd.usFlags |= CONSTRUCT_FLAGS(0, 0, 0, _fanysize); 721 } 722 if (bRetry) 723 { 724 if(bInvalid64bit) 725 asmerr("operand for `%s` invalid in 64bit mode", asm_opstr(pop)); 726 else 727 asmerr("bad type/size of operands `%s`", asm_opstr(pop)); 728 return; 729 } 730 bRetry = true; 731 } 732 733 PTRNTAB returnIt(PTRNTAB ret) 734 { 735 if (bRetry) 736 { 737 asmerr("bad type/size of operands `%s`", asm_opstr(pop)); 738 } 739 return ret; 740 } 741 742 void printMismatches(int usActual) 743 { 744 printOperands(pop, opnds); 745 printf("OPCODE mismatch = "); 746 foreach (i; 0 .. usActual) 747 { 748 if (i < opnds.length) 749 asm_output_flags(opnds[i].usFlags); 750 else 751 printf("NONE"); 752 } 753 printf("\n"); 754 } 755 756 // 757 // The number of arguments matches, now check to find the opcode 758 // in the associated opcode table 759 // 760 RETRY: 761 //printf("usActual = %d\n", usActual); 762 switch (usActual) 763 { 764 case 0: 765 if (global.params.is64bit && (pop.ptb.pptb0.usFlags & _i64_bit)) 766 { 767 asmerr("opcode `%s` is unavailable in 64bit mode", asm_opstr(pop)); // illegal opcode in 64bit mode 768 break; 769 } 770 if ((asmstate.ucItype == ITopt || 771 asmstate.ucItype == ITfloat) && 772 usNumops != 0) 773 { 774 paramError(); 775 break; 776 } 777 return returnIt(pop.ptb); 778 779 case 1: 780 { 781 enum log = false; 782 if (log) { printf("`%s`\n", asm_opstr(pop)); } 783 if (log) { printf("opflags1 = "); asm_output_flags(opflags[0]); printf("\n"); } 784 785 if (pop.ptb.pptb1.opcode == 0xE8 && 786 opnds[0].s == asmstate.psDollar && 787 (opnds[0].disp >= byte.min && opnds[0].disp <= byte.max) 788 ) 789 // Rewrite CALL $+disp from rel8 to rel32 790 opflags[0] = CONSTRUCT_FLAGS(OpndSize._32, _rel, _flbl, 0); 791 792 PTRNTAB1 *table1; 793 for (table1 = pop.ptb.pptb1; table1.opcode != ASM_END; 794 table1++) 795 { 796 if (log) { printf("table = "); asm_output_flags(table1.usOp1); printf("\n"); } 797 const bMatch1 = asm_match_flags(opflags[0], table1.usOp1); 798 if (log) { printf("bMatch1 = x%x\n", bMatch1); } 799 if (bMatch1) 800 { 801 if (table1.opcode == 0x68 && 802 table1.usOp1 == _imm16 803 ) 804 // Don't match PUSH imm16 in 32 bit code 805 continue; 806 807 // Check if match is invalid in 64bit mode 808 if (global.params.is64bit && (table1.usFlags & _i64_bit)) 809 { 810 bInvalid64bit = true; 811 continue; 812 } 813 814 // Check for ambiguous size 815 if (getOpndSize(opflags[0]) == OpndSize._anysize && 816 !opnds[0].bPtr && 817 (table1 + 1).opcode != ASM_END && 818 getOpndSize(table1.usOp1) == OpndSize._8) 819 { 820 asmerr("operand size for opcode `%s` is ambiguous, add `ptr byte/short/int/long` prefix", asm_opstr(pop)); 821 break RETRY; 822 } 823 824 break; 825 } 826 if ((asmstate.ucItype == ITimmed) && 827 asm_match_flags(opflags[0], 828 CONSTRUCT_FLAGS(OpndSize._32_16_8, _imm, _normal, 829 0)) && 830 opnds[0].disp == table1.usFlags) 831 break; 832 if (asmstate.ucItype == ITopt || 833 asmstate.ucItype == ITfloat) 834 { 835 switch (usNumops) 836 { 837 case 0: 838 if (!table1.usOp1) 839 goto Lfound1; 840 break; 841 case 1: 842 break; 843 default: 844 paramError(); 845 break RETRY; 846 } 847 } 848 } 849 Lfound1: 850 if (table1.opcode != ASM_END) 851 { 852 PTRNTAB ret = { pptb1 : table1 }; 853 return returnIt(ret); 854 } 855 debug (debuga) printMismatches(usActual); 856 TYPE_SIZE_ERROR(); 857 if (asmstate.errors) 858 break; 859 goto RETRY; 860 } 861 case 2: 862 { 863 enum log = false; 864 if (log) { printf("`%s`\n", asm_opstr(pop)); } 865 if (log) { printf("`%s`\n", asm_opstr(pop)); } 866 if (log) { printf("opflags1 = "); asm_output_flags(opflags[0]); printf("\n"); } 867 if (log) { printf("opflags2 = "); asm_output_flags(opflags[1]); printf("\n"); } 868 PTRNTAB2 *table2; 869 for (table2 = pop.ptb.pptb2; 870 table2.opcode != ASM_END; 871 table2++) 872 { 873 if (log) { printf("table1 = "); asm_output_flags(table2.usOp1); printf("\n"); } 874 if (log) { printf("table2 = "); asm_output_flags(table2.usOp2); printf("\n"); } 875 if (global.params.is64bit && (table2.usFlags & _i64_bit)) 876 asmerr("opcode `%s` is unavailable in 64bit mode", asm_opstr(pop)); 877 878 const bMatch1 = asm_match_flags(opflags[0], table2.usOp1); 879 const bMatch2 = asm_match_flags(opflags[1], table2.usOp2); 880 if (log) printf("match1 = %d, match2 = %d\n",bMatch1,bMatch2); 881 if (bMatch1 && bMatch2) 882 { 883 if (log) printf("match\n"); 884 885 /* Don't match if implicit sign-extension will 886 * change the value of the immediate operand 887 */ 888 if (!bRetry && ASM_GET_aopty(table2.usOp2) == _imm) 889 { 890 OpndSize op1size = getOpndSize(table2.usOp1); 891 if (!op1size) // implicit register operand 892 { 893 switch (ASM_GET_uRegmask(table2.usOp1)) 894 { 895 case ASM_GET_uRegmask(_al): 896 case ASM_GET_uRegmask(_cl): op1size = OpndSize._8; break; 897 case ASM_GET_uRegmask(_ax): 898 case ASM_GET_uRegmask(_dx): op1size = OpndSize._16; break; 899 case ASM_GET_uRegmask(_eax): op1size = OpndSize._32; break; 900 case ASM_GET_uRegmask(_rax): op1size = OpndSize._64; break; 901 default: 902 assert(0); 903 } 904 } 905 if (op1size > getOpndSize(table2.usOp2)) 906 { 907 switch(getOpndSize(table2.usOp2)) 908 { 909 case OpndSize._8: 910 if (opnds[1].disp > byte.max) 911 continue; 912 break; 913 case OpndSize._16: 914 if (opnds[1].disp > short.max) 915 continue; 916 break; 917 case OpndSize._32: 918 if (opnds[1].disp > int.max) 919 continue; 920 break; 921 default: 922 assert(0); 923 } 924 } 925 } 926 927 // Check for ambiguous size 928 if (asmstate.ucItype == ITopt && 929 getOpndSize(opflags[0]) == OpndSize._anysize && 930 !opnds[0].bPtr && 931 opflags[1] == 0 && 932 table2.usOp2 == 0 && 933 (table2 + 1).opcode != ASM_END && 934 getOpndSize(table2.usOp1) == OpndSize._8) 935 { 936 asmerr("operand size for opcode `%s` is ambiguous, add `ptr byte/short/int/long` prefix", asm_opstr(pop)); 937 break RETRY; 938 } 939 940 break; 941 } 942 943 if (asmstate.ucItype == ITopt || 944 asmstate.ucItype == ITfloat) 945 { 946 switch (usNumops) 947 { 948 case 0: 949 if (!table2.usOp1) 950 goto Lfound2; 951 break; 952 case 1: 953 if (bMatch1 && !table2.usOp2) 954 goto Lfound2; 955 break; 956 case 2: 957 break; 958 default: 959 paramError(); 960 break RETRY; 961 } 962 } 963 version (none) 964 { 965 if (asmstate.ucItype == ITshift && 966 !table2.usOp2 && 967 bMatch1 && opnds[1].disp == 1 && 968 asm_match_flags(opflags2, 969 CONSTRUCT_FLAGS(OpndSize._32_16_8, _imm,_normal,0)) 970 ) 971 break; 972 } 973 } 974 Lfound2: 975 if (table2.opcode != ASM_END) 976 { 977 PTRNTAB ret = { pptb2 : table2 }; 978 return returnIt(ret); 979 } 980 debug (debuga) printMismatches(usActual); 981 TYPE_SIZE_ERROR(); 982 if (asmstate.errors) 983 break; 984 goto RETRY; 985 } 986 case 3: 987 { 988 enum log = false; 989 if (log) { printf("`%s`\n", asm_opstr(pop)); } 990 if (log) { printf("opflags1 = "); asm_output_flags(opflags[0]); printf("\n"); } 991 if (log) { printf("opflags2 = "); asm_output_flags(opflags[1]); printf("\n"); } 992 if (log) { printf("opflags3 = "); asm_output_flags(opflags[2]); printf("\n"); } 993 PTRNTAB3 *table3; 994 for (table3 = pop.ptb.pptb3; 995 table3.opcode != ASM_END; 996 table3++) 997 { 998 if (log) { printf("table1 = "); asm_output_flags(table3.usOp1); printf("\n"); } 999 if (log) { printf("table2 = "); asm_output_flags(table3.usOp2); printf("\n"); } 1000 if (log) { printf("table3 = "); asm_output_flags(table3.usOp3); printf("\n"); } 1001 const bMatch1 = asm_match_flags(opflags[0], table3.usOp1); 1002 const bMatch2 = asm_match_flags(opflags[1], table3.usOp2); 1003 const bMatch3 = asm_match_flags(opflags[2], table3.usOp3); 1004 if (bMatch1 && bMatch2 && bMatch3) 1005 { 1006 if (log) printf("match\n"); 1007 1008 // Check for ambiguous size 1009 if (asmstate.ucItype == ITopt && 1010 getOpndSize(opflags[0]) == OpndSize._anysize && 1011 !opnds[0].bPtr && 1012 opflags[1] == 0 && 1013 opflags[2] == 0 && 1014 table3.usOp2 == 0 && 1015 table3.usOp3 == 0 && 1016 (table3 + 1).opcode != ASM_END && 1017 getOpndSize(table3.usOp1) == OpndSize._8) 1018 { 1019 asmerr("operand size for opcode `%s` is ambiguous, add `ptr byte/short/int/long` prefix", asm_opstr(pop)); 1020 break RETRY; 1021 } 1022 1023 goto Lfound3; 1024 } 1025 if (asmstate.ucItype == ITopt) 1026 { 1027 switch (usNumops) 1028 { 1029 case 0: 1030 if (!table3.usOp1) 1031 goto Lfound3; 1032 break; 1033 case 1: 1034 if (bMatch1 && !table3.usOp2) 1035 goto Lfound3; 1036 break; 1037 case 2: 1038 if (bMatch1 && bMatch2 && !table3.usOp3) 1039 goto Lfound3; 1040 break; 1041 case 3: 1042 break; 1043 default: 1044 paramError(); 1045 break RETRY; 1046 } 1047 } 1048 } 1049 Lfound3: 1050 if (table3.opcode != ASM_END) 1051 { 1052 PTRNTAB ret = { pptb3 : table3 }; 1053 return returnIt(ret); 1054 } 1055 debug (debuga) printMismatches(usActual); 1056 TYPE_SIZE_ERROR(); 1057 if (asmstate.errors) 1058 break; 1059 goto RETRY; 1060 } 1061 case 4: 1062 { 1063 PTRNTAB4 *table4; 1064 for (table4 = pop.ptb.pptb4; 1065 table4.opcode != ASM_END; 1066 table4++) 1067 { 1068 const bMatch1 = asm_match_flags(opflags[0], table4.usOp1); 1069 const bMatch2 = asm_match_flags(opflags[1], table4.usOp2); 1070 const bMatch3 = asm_match_flags(opflags[2], table4.usOp3); 1071 const bMatch4 = asm_match_flags(opflags[3], table4.usOp4); 1072 if (bMatch1 && bMatch2 && bMatch3 && bMatch4) 1073 goto Lfound4; 1074 if (asmstate.ucItype == ITopt) 1075 { 1076 switch (usNumops) 1077 { 1078 case 0: 1079 if (!table4.usOp1) 1080 goto Lfound4; 1081 break; 1082 case 1: 1083 if (bMatch1 && !table4.usOp2) 1084 goto Lfound4; 1085 break; 1086 case 2: 1087 if (bMatch1 && bMatch2 && !table4.usOp3) 1088 goto Lfound4; 1089 break; 1090 case 3: 1091 if (bMatch1 && bMatch2 && bMatch3 && !table4.usOp4) 1092 goto Lfound4; 1093 break; 1094 case 4: 1095 break; 1096 default: 1097 paramError(); 1098 break RETRY; 1099 } 1100 } 1101 } 1102 Lfound4: 1103 if (table4.opcode != ASM_END) 1104 { 1105 PTRNTAB ret = { pptb4 : table4 }; 1106 return returnIt(ret); 1107 } 1108 debug (debuga) printMismatches(usActual); 1109 TYPE_SIZE_ERROR(); 1110 if (asmstate.errors) 1111 break; 1112 goto RETRY; 1113 } 1114 default: 1115 break; 1116 } 1117 1118 return returnIt(PTRNTAB(null)); 1119 } 1120 1121 /******************************* 1122 */ 1123 1124 opflag_t asm_determine_float_flags(ref OPND popnd) 1125 { 1126 //printf("asm_determine_float_flags()\n"); 1127 1128 opflag_t us, usFloat; 1129 1130 // Insure that if it is a register, that it is not a normal processor 1131 // register. 1132 1133 if (popnd.base && 1134 !popnd.s && !popnd.disp && !popnd.vreal 1135 && !isOneOf(getOpndSize(popnd.base.ty), OpndSize._32_16_8)) 1136 { 1137 return popnd.base.ty; 1138 } 1139 if (popnd.pregDisp1 && !popnd.base) 1140 { 1141 us = asm_float_type_size(popnd.ptype, &usFloat); 1142 //printf("us = x%x, usFloat = x%x\n", us, usFloat); 1143 if (getOpndSize(popnd.pregDisp1.ty) == OpndSize._16) 1144 return CONSTRUCT_FLAGS(us, _m, _addr16, usFloat); 1145 else 1146 return CONSTRUCT_FLAGS(us, _m, _addr32, usFloat); 1147 } 1148 else if (popnd.s !is null) 1149 { 1150 us = asm_float_type_size(popnd.ptype, &usFloat); 1151 return CONSTRUCT_FLAGS(us, _m, _normal, usFloat); 1152 } 1153 1154 if (popnd.segreg) 1155 { 1156 us = asm_float_type_size(popnd.ptype, &usFloat); 1157 return(CONSTRUCT_FLAGS(us, _m, _addr32, usFloat)); 1158 } 1159 1160 version (none) 1161 { 1162 if (popnd.vreal) 1163 { 1164 switch (popnd.ptype.ty) 1165 { 1166 case Tfloat32: 1167 popnd.s = fconst(popnd.vreal); 1168 return(CONSTRUCT_FLAGS(_32, _m, _normal, 0)); 1169 1170 case Tfloat64: 1171 popnd.s = dconst(popnd.vreal); 1172 return(CONSTRUCT_FLAGS(0, _m, _normal, _f64)); 1173 1174 case Tfloat80: 1175 popnd.s = ldconst(popnd.vreal); 1176 return(CONSTRUCT_FLAGS(0, _m, _normal, _f80)); 1177 } 1178 } 1179 } 1180 1181 asmerr("unknown operand for floating point instruction"); 1182 return 0; 1183 } 1184 1185 /******************************* 1186 */ 1187 1188 opflag_t asm_determine_operand_flags(ref OPND popnd) 1189 { 1190 //printf("asm_determine_operand_flags()\n"); 1191 Dsymbol ps; 1192 int ty; 1193 opflag_t us; 1194 opflag_t sz; 1195 ASM_OPERAND_TYPE opty; 1196 ASM_MODIFIERS amod; 1197 1198 // If specified 'offset' or 'segment' but no symbol 1199 if ((popnd.bOffset || popnd.bSeg) && !popnd.s) 1200 { 1201 asmerr("specified 'offset' or 'segment' but no symbol"); 1202 return 0; 1203 } 1204 1205 if (asmstate.ucItype == ITfloat) 1206 return asm_determine_float_flags(popnd); 1207 1208 // If just a register 1209 if (popnd.base && !popnd.s && !popnd.disp && !popnd.vreal) 1210 return popnd.base.ty; 1211 debug (debuga) 1212 printf("popnd.base = %s\n, popnd.pregDisp1 = %p\n", (popnd.base ? popnd.base.regstr : "NONE").ptr, popnd.pregDisp1); 1213 1214 ps = popnd.s; 1215 Declaration ds = ps ? ps.isDeclaration() : null; 1216 if (ds && ds.storage_class & STC.lazy_) 1217 sz = OpndSize._anysize; 1218 else 1219 { 1220 auto ptype = (ds && ds.storage_class & (STC.out_ | STC.ref_)) ? popnd.ptype.pointerTo() : popnd.ptype; 1221 sz = asm_type_size(ptype, popnd.bPtr); 1222 } 1223 1224 if (popnd.bRIP) 1225 return CONSTRUCT_FLAGS(sz, _m, _addr32, 0); 1226 else if (popnd.pregDisp1 && !popnd.base) 1227 { 1228 if (ps && ps.isLabel() && sz == OpndSize._anysize) 1229 sz = OpndSize._32; 1230 return getOpndSize(popnd.pregDisp1.ty) == OpndSize._16 1231 ? CONSTRUCT_FLAGS(sz, _m, _addr16, 0) 1232 : CONSTRUCT_FLAGS(sz, _m, _addr32, 0); 1233 } 1234 else if (ps) 1235 { 1236 if (popnd.bOffset || popnd.bSeg || ps == asmstate.psLocalsize) 1237 return CONSTRUCT_FLAGS(OpndSize._32, _imm, _normal, 0); 1238 1239 if (ps.isLabel()) 1240 { 1241 switch (popnd.ajt) 1242 { 1243 case ASM_JUMPTYPE_UNSPECIFIED: 1244 if (ps == asmstate.psDollar) 1245 { 1246 if (popnd.disp >= byte.min && 1247 popnd.disp <= byte.max) 1248 us = CONSTRUCT_FLAGS(OpndSize._8, _rel, _flbl,0); 1249 //else if (popnd.disp >= short.min && 1250 //popnd.disp <= short.max && global.params.is16bit) 1251 //us = CONSTRUCT_FLAGS(OpndSize._16, _rel, _flbl,0); 1252 else 1253 us = CONSTRUCT_FLAGS(OpndSize._32, _rel, _flbl,0); 1254 } 1255 else if (asmstate.ucItype != ITjump) 1256 { 1257 if (sz == OpndSize._8) 1258 { 1259 us = CONSTRUCT_FLAGS(OpndSize._8,_rel,_flbl,0); 1260 break; 1261 } 1262 goto case_near; 1263 } 1264 else 1265 us = CONSTRUCT_FLAGS(OpndSize._32_8, _rel, _flbl,0); 1266 break; 1267 1268 case ASM_JUMPTYPE_NEAR: 1269 case_near: 1270 us = CONSTRUCT_FLAGS(OpndSize._32, _rel, _flbl, 0); 1271 break; 1272 case ASM_JUMPTYPE_SHORT: 1273 us = CONSTRUCT_FLAGS(OpndSize._8, _rel, _flbl, 0); 1274 break; 1275 case ASM_JUMPTYPE_FAR: 1276 us = CONSTRUCT_FLAGS(OpndSize._48, _rel, _flbl, 0); 1277 break; 1278 default: 1279 assert(0); 1280 } 1281 return us; 1282 } 1283 if (!popnd.ptype) 1284 return CONSTRUCT_FLAGS(sz, _m, _normal, 0); 1285 ty = popnd.ptype.ty; 1286 if (ty == Tpointer && popnd.ptype.nextOf().ty == Tfunction && 1287 !ps.isVarDeclaration()) 1288 { 1289 return CONSTRUCT_FLAGS(OpndSize._32, _m, _fn16, 0); 1290 } 1291 else if (ty == Tfunction) 1292 { 1293 return CONSTRUCT_FLAGS(OpndSize._32, _rel, _fn16, 0); 1294 } 1295 else if (asmstate.ucItype == ITjump) 1296 { 1297 amod = _normal; 1298 goto L1; 1299 } 1300 else 1301 return CONSTRUCT_FLAGS(sz, _m, _normal, 0); 1302 } 1303 1304 if (popnd.segreg /*|| popnd.bPtr*/) 1305 { 1306 amod = _addr32; 1307 if (asmstate.ucItype == ITjump) 1308 { 1309 L1: 1310 opty = _m; 1311 if (sz == OpndSize._48) 1312 opty = _mnoi; 1313 us = CONSTRUCT_FLAGS(sz,opty,amod,0); 1314 } 1315 else 1316 us = CONSTRUCT_FLAGS(sz, 1317 // _rel, amod, 0); 1318 _m, amod, 0); 1319 } 1320 else if (popnd.ptype) 1321 us = CONSTRUCT_FLAGS(sz, _imm, _normal, 0); 1322 else if (popnd.disp >= byte.min && popnd.disp <= ubyte.max) 1323 us = CONSTRUCT_FLAGS( OpndSize._64_32_16_8, _imm, _normal, 0); 1324 else if (popnd.disp >= short.min && popnd.disp <= ushort.max) 1325 us = CONSTRUCT_FLAGS( OpndSize._64_32_16, _imm, _normal, 0); 1326 else if (popnd.disp >= int.min && popnd.disp <= uint.max) 1327 us = CONSTRUCT_FLAGS( OpndSize._64_32, _imm, _normal, 0); 1328 else 1329 us = CONSTRUCT_FLAGS( OpndSize._64, _imm, _normal, 0); 1330 return us; 1331 } 1332 1333 /****************************** 1334 * Convert assembly instruction into a code, and append 1335 * it to the code generated for this block. 1336 */ 1337 1338 code *asm_emit(Loc loc, 1339 uint usNumops, PTRNTAB ptb, 1340 OP *pop, OPND[] opnds) 1341 { 1342 ubyte[16] instruction = void; 1343 size_t insIdx = 0; 1344 debug 1345 { 1346 void emit(ubyte op) { instruction[insIdx++] = op; } 1347 } 1348 else 1349 { 1350 void emit(ubyte op) { } 1351 } 1352 // uint us; 1353 code *pc = null; 1354 OPND *popndTmp = null; 1355 //ASM_OPERAND_TYPE aopty1 = _reg , aopty2 = 0, aopty3 = 0; 1356 ASM_MODIFIERS[2] amods = _normal; 1357 OpndSize[3] uSizemaskTable; 1358 ASM_OPERAND_TYPE[3] aoptyTable = _reg; 1359 ASM_MODIFIERS[2] amodTable = _normal; 1360 uint[2] uRegmaskTable = 0; 1361 1362 pc = code_calloc(); 1363 pc.Iflags |= CFpsw; // assume we want to keep the flags 1364 1365 1366 void setImmediateFlags(size_t i) 1367 { 1368 emit(0x67); 1369 pc.Iflags |= CFaddrsize; 1370 if (!global.params.is64bit) 1371 amods[i] = _addr16; 1372 else 1373 amods[i] = _addr32; 1374 opnds[i].usFlags &= ~CONSTRUCT_FLAGS(0,0,7,0); 1375 opnds[i].usFlags |= CONSTRUCT_FLAGS(0,0,amods[i],0); 1376 } 1377 1378 void setCodeForImmediate(ref OPND opnd, uint sizeMask){ 1379 Declaration d = opnd.s ? opnd.s.isDeclaration() : null; 1380 if (opnd.bSeg) 1381 { 1382 if (!(d && d.isDataseg())) 1383 { 1384 asmerr("bad addr mode"); 1385 return; 1386 } 1387 } 1388 switch (sizeMask) 1389 { 1390 case OpndSize._8: 1391 case OpndSize._16: 1392 case OpndSize._32: 1393 case OpndSize._64: 1394 if (opnd.s == asmstate.psLocalsize) 1395 { 1396 pc.IFL2 = FLlocalsize; 1397 pc.IEV2.Vdsym = null; 1398 pc.Iflags |= CFoff; 1399 pc.IEV2.Voffset = opnd.disp; 1400 } 1401 else if (d) 1402 { 1403 //if ((pc.IFL2 = d.Sfl) == 0) 1404 pc.IFL2 = FLdsymbol; 1405 pc.Iflags &= ~(CFseg | CFoff); 1406 if (opnd.bSeg) 1407 pc.Iflags |= CFseg; 1408 else 1409 pc.Iflags |= CFoff; 1410 pc.IEV2.Voffset = opnd.disp; 1411 pc.IEV2.Vdsym = cast(_Declaration*)d; 1412 } 1413 else 1414 { 1415 pc.IEV2.Vllong = opnd.disp; 1416 pc.IFL2 = FLconst; 1417 } 1418 break; 1419 1420 default: 1421 break; 1422 } 1423 } 1424 1425 static code* finalizeCode(Loc loc, code* pc, PTRNTAB ptb) 1426 { 1427 if ((pc.Iop & ~7) == 0xD8 && 1428 ADDFWAIT && 1429 !(ptb.pptb0.usFlags & _nfwait)) 1430 pc.Iflags |= CFwait; 1431 else if ((ptb.pptb0.usFlags & _fwait) && 1432 config.target_cpu >= TARGET_80386) 1433 pc.Iflags |= CFwait; 1434 1435 debug (debuga) 1436 { 1437 foreach (u; instruction[0 .. insIdx]) 1438 printf(" %02X", u); 1439 1440 printOperands(pop, opnds); 1441 } 1442 1443 CodeBuilder cdb; 1444 cdb.ctor(); 1445 1446 if (global.params.symdebug) 1447 { 1448 cdb.genlinnum(Srcpos.create(loc.filename, loc.linnum, loc.charnum)); 1449 } 1450 1451 cdb.append(pc); 1452 return cdb.finish(); 1453 } 1454 1455 if (opnds.length >= 1) 1456 { 1457 amods[0] = ASM_GET_amod(opnds[0].usFlags); 1458 1459 uSizemaskTable[0] = getOpndSize(ptb.pptb1.usOp1); 1460 aoptyTable[0] = ASM_GET_aopty(ptb.pptb1.usOp1); 1461 amodTable[0] = ASM_GET_amod(ptb.pptb1.usOp1); 1462 uRegmaskTable[0] = ASM_GET_uRegmask(ptb.pptb1.usOp1); 1463 1464 } 1465 if (opnds.length >= 2) 1466 { 1467 version (none) 1468 { 1469 printf("\nasm_emit:\nop: "); 1470 asm_output_flags(opnds[1].usFlags); 1471 printf("\ntb: "); 1472 asm_output_flags(ptb.pptb2.usOp2); 1473 printf("\n"); 1474 } 1475 1476 amods[1] = ASM_GET_amod(opnds[1].usFlags); 1477 1478 uSizemaskTable[1] = getOpndSize(ptb.pptb2.usOp2); 1479 aoptyTable[1] = ASM_GET_aopty(ptb.pptb2.usOp2); 1480 amodTable[1] = ASM_GET_amod(ptb.pptb2.usOp2); 1481 uRegmaskTable[1] = ASM_GET_uRegmask(ptb.pptb2.usOp2); 1482 } 1483 if (opnds.length >= 3) 1484 { 1485 uSizemaskTable[2] = getOpndSize(ptb.pptb3.usOp3); 1486 aoptyTable[2] = ASM_GET_aopty(ptb.pptb3.usOp3); 1487 } 1488 1489 asmstate.statement.regs |= asm_modify_regs(ptb, opnds); 1490 1491 if (ptb.pptb0.usFlags & _64_bit && !global.params.is64bit) 1492 asmerr("use -m64 to compile 64 bit instructions"); 1493 1494 if (global.params.is64bit && (ptb.pptb0.usFlags & _64_bit)) 1495 { 1496 emit(REX | REX_W); 1497 pc.Irex |= REX_W; 1498 } 1499 1500 final switch (usNumops) 1501 { 1502 case 0: 1503 if (ptb.pptb0.usFlags & _16_bit) 1504 { 1505 emit(0x66); 1506 pc.Iflags |= CFopsize; 1507 } 1508 break; 1509 1510 // vex adds 4 operand instructions, but already provides 1511 // encoded operation size 1512 case 4: 1513 break; 1514 1515 // 3 and 2 are the same because the third operand is always 1516 // an immediate and does not affect operation size 1517 case 3: 1518 case 2: 1519 if ((!global.params.is64bit && 1520 (amods[1] == _addr16 || 1521 (isOneOf(OpndSize._16, uSizemaskTable[1]) && aoptyTable[1] == _rel ) || 1522 (isOneOf(OpndSize._32, uSizemaskTable[1]) && aoptyTable[1] == _mnoi) || 1523 (ptb.pptb2.usFlags & _16_bit_addr) 1524 ) 1525 ) 1526 ) 1527 setImmediateFlags(1); 1528 1529 /* Fall through, operand 1 controls the opsize, but the 1530 address size can be in either operand 1 or operand 2, 1531 hence the extra checking the flags tested for SHOULD 1532 be mutex on operand 1 and operand 2 because there is 1533 only one MOD R/M byte 1534 */ 1535 goto case; 1536 1537 case 1: 1538 if ((!global.params.is64bit && 1539 (amods[0] == _addr16 || 1540 (isOneOf(OpndSize._16, uSizemaskTable[0]) && aoptyTable[0] == _rel ) || 1541 (isOneOf(OpndSize._32, uSizemaskTable[0]) && aoptyTable[0] == _mnoi) || 1542 (ptb.pptb1.usFlags & _16_bit_addr)))) 1543 setImmediateFlags(0); 1544 1545 // If the size of the operand is unknown, assume that it is 1546 // the default size 1547 if (ptb.pptb0.usFlags & _16_bit) 1548 { 1549 //if (asmstate.ucItype != ITjump) 1550 { 1551 emit(0x66); 1552 pc.Iflags |= CFopsize; 1553 } 1554 } 1555 1556 const(REG) *pregSegment; 1557 if (opnds[0].segreg != null) 1558 { 1559 popndTmp = &opnds[0]; 1560 pregSegment = opnds[0].segreg; 1561 } 1562 if (!pregSegment) 1563 { 1564 popndTmp = opnds.length >= 2 ? &opnds[1] : null; 1565 pregSegment = popndTmp ? popndTmp.segreg : null; 1566 } 1567 if (pregSegment) 1568 { 1569 uint usDefaultseg; 1570 if ((popndTmp.pregDisp1 && 1571 popndTmp.pregDisp1.val == _BP) || 1572 popndTmp.pregDisp2 && 1573 popndTmp.pregDisp2.val == _BP) 1574 usDefaultseg = _SS; 1575 else if (asmstate.ucItype == ITjump) 1576 usDefaultseg = _CS; 1577 else 1578 usDefaultseg = _DS; 1579 if (pregSegment.val != usDefaultseg) 1580 { 1581 if (asmstate.ucItype == ITjump) 1582 asmerr("Cannot generate a segment prefix for a branching instruction"); 1583 else 1584 switch (pregSegment.val) 1585 { 1586 case _CS: 1587 emit(SEGCS); 1588 pc.Iflags |= CFcs; 1589 break; 1590 case _SS: 1591 emit(SEGSS); 1592 pc.Iflags |= CFss; 1593 break; 1594 case _DS: 1595 emit(SEGDS); 1596 pc.Iflags |= CFds; 1597 break; 1598 case _ES: 1599 emit(SEGES); 1600 pc.Iflags |= CFes; 1601 break; 1602 case _FS: 1603 emit(SEGFS); 1604 pc.Iflags |= CFfs; 1605 break; 1606 case _GS: 1607 emit(SEGGS); 1608 pc.Iflags |= CFgs; 1609 break; 1610 default: 1611 assert(0); 1612 } 1613 } 1614 } 1615 break; 1616 } 1617 uint opcode = ptb.pptb0.opcode; 1618 1619 pc.Iop = opcode; 1620 if (pc.Ivex.pfx == 0xC4) 1621 { 1622 debug const oIdx = insIdx; 1623 ASM_OPERAND_TYPE aoptyTmp; 1624 OpndSize uSizemaskTmp; 1625 1626 // vvvv 1627 switch (pc.Ivex.vvvv) 1628 { 1629 case VEX_NOO: 1630 pc.Ivex.vvvv = 0xF; // not used 1631 1632 if ((aoptyTable[0] == _m || aoptyTable[0] == _rm) && 1633 aoptyTable[1] == _reg) 1634 asm_make_modrm_byte( 1635 &emit, 1636 pc, 1637 ptb.pptb1.usFlags, 1638 opnds[0 .. opnds.length >= 2 ? 2 : 1]); 1639 else if (usNumops == 2 || usNumops == 3 && aoptyTable[2] == _imm) 1640 asm_make_modrm_byte( 1641 &emit, 1642 pc, 1643 ptb.pptb1.usFlags, 1644 [opnds[1], opnds[0]]); 1645 else 1646 assert(!usNumops); // no operands 1647 1648 if (usNumops == 3) 1649 { 1650 popndTmp = &opnds[2]; 1651 aoptyTmp = ASM_GET_aopty(ptb.pptb3.usOp3); 1652 uSizemaskTmp = getOpndSize(ptb.pptb3.usOp3); 1653 assert(aoptyTmp == _imm); 1654 } 1655 break; 1656 1657 case VEX_NDD: 1658 pc.Ivex.vvvv = cast(ubyte) ~int(opnds[0].base.val); 1659 1660 asm_make_modrm_byte( 1661 &emit, 1662 pc, 1663 ptb.pptb1.usFlags, 1664 [opnds[1]]); 1665 1666 if (usNumops == 3) 1667 { 1668 popndTmp = &opnds[2]; 1669 aoptyTmp = ASM_GET_aopty(ptb.pptb3.usOp3); 1670 uSizemaskTmp = getOpndSize(ptb.pptb3.usOp3); 1671 assert(aoptyTmp == _imm); 1672 } 1673 break; 1674 1675 case VEX_DDS: 1676 assert(usNumops == 3); 1677 pc.Ivex.vvvv = cast(ubyte) ~int(opnds[1].base.val); 1678 1679 asm_make_modrm_byte( 1680 &emit, 1681 pc, 1682 ptb.pptb1.usFlags, 1683 [opnds[2], opnds[0]]); 1684 break; 1685 1686 case VEX_NDS: 1687 pc.Ivex.vvvv = cast(ubyte) ~int(opnds[1].base.val); 1688 1689 if (aoptyTable[0] == _m || aoptyTable[0] == _rm) 1690 asm_make_modrm_byte( 1691 &emit, 1692 pc, 1693 ptb.pptb1.usFlags, 1694 [opnds[0], opnds[2]]); 1695 else 1696 asm_make_modrm_byte( 1697 &emit, 1698 pc, 1699 ptb.pptb1.usFlags, 1700 [opnds[2], opnds[0]]); 1701 1702 if (usNumops == 4) 1703 { 1704 popndTmp = &opnds[3]; 1705 aoptyTmp = ASM_GET_aopty(ptb.pptb4.usOp4); 1706 uSizemaskTmp = getOpndSize(ptb.pptb4.usOp4); 1707 assert(aoptyTmp == _imm); 1708 } 1709 break; 1710 1711 default: 1712 assert(0); 1713 } 1714 1715 // REX 1716 // REX_W is solely taken from WO/W1/WIG 1717 // pc.Ivex.w = !!(pc.Irex & REX_W); 1718 pc.Ivex.b = !(pc.Irex & REX_B); 1719 pc.Ivex.x = !(pc.Irex & REX_X); 1720 pc.Ivex.r = !(pc.Irex & REX_R); 1721 1722 /* Check if a 3-byte vex is needed. 1723 */ 1724 checkSetVex3(pc); 1725 if (pc.Iflags & CFvex3) 1726 { 1727 debug 1728 { 1729 memmove(&instruction[oIdx+3], &instruction[oIdx], insIdx-oIdx); 1730 insIdx = oIdx; 1731 } 1732 emit(0xC4); 1733 emit(cast(ubyte)VEX3_B1(pc.Ivex)); 1734 emit(cast(ubyte)VEX3_B2(pc.Ivex)); 1735 pc.Iflags |= CFvex3; 1736 } 1737 else 1738 { 1739 debug 1740 { 1741 memmove(&instruction[oIdx+2], &instruction[oIdx], insIdx-oIdx); 1742 insIdx = oIdx; 1743 } 1744 emit(0xC5); 1745 emit(cast(ubyte)VEX2_B1(pc.Ivex)); 1746 } 1747 pc.Iflags |= CFvex; 1748 emit(pc.Ivex.op); 1749 if (popndTmp && aoptyTmp == _imm) 1750 setCodeForImmediate(*popndTmp, uSizemaskTmp); 1751 return finalizeCode(loc, pc, ptb); 1752 } 1753 1754 else if ((opcode & 0xFFFD00) == 0x0F3800) // SSSE3, SSE4 1755 { 1756 emit(0xFF); 1757 emit(0xFD); 1758 emit(0x00); 1759 goto L3; 1760 } 1761 1762 switch (opcode & 0xFF0000) 1763 { 1764 case 0: 1765 break; 1766 1767 case 0x660000: 1768 opcode &= 0xFFFF; 1769 goto L3; 1770 1771 case 0xF20000: // REPNE 1772 case 0xF30000: // REP/REPE 1773 // BUG: What if there's an address size prefix or segment 1774 // override prefix? Must the REP be adjacent to the rest 1775 // of the opcode? 1776 opcode &= 0xFFFF; 1777 goto L3; 1778 1779 case 0x0F0000: // an AMD instruction 1780 const puc = (cast(ubyte *) &opcode); 1781 emit(puc[2]); 1782 emit(puc[1]); 1783 emit(puc[0]); 1784 pc.Iop >>= 8; 1785 if (puc[1] == 0x0F) // if AMD instruction 0x0F0F 1786 { 1787 pc.IEV2.Vint = puc[0]; 1788 pc.IFL2 = FLconst; 1789 } 1790 else 1791 pc.Irm = puc[0]; 1792 goto L3; 1793 1794 default: 1795 const puc = (cast(ubyte *) &opcode); 1796 emit(puc[2]); 1797 emit(puc[1]); 1798 emit(puc[0]); 1799 pc.Iop >>= 8; 1800 pc.Irm = puc[0]; 1801 goto L3; 1802 } 1803 if (opcode & 0xff00) 1804 { 1805 const puc = (cast(ubyte *) &(opcode)); 1806 emit(puc[1]); 1807 emit(puc[0]); 1808 pc.Iop = puc[1]; 1809 if (pc.Iop == 0x0f) 1810 { 1811 pc.Iop = 0x0F00 | puc[0]; 1812 } 1813 else 1814 { 1815 if (opcode == 0xDFE0) // FSTSW AX 1816 { 1817 pc.Irm = puc[0]; 1818 return finalizeCode(loc, pc, ptb); 1819 } 1820 if (asmstate.ucItype == ITfloat) 1821 { 1822 pc.Irm = puc[0]; 1823 } 1824 else if (opcode == PAUSE) 1825 { 1826 pc.Iop = PAUSE; 1827 } 1828 else 1829 { 1830 pc.IEV2.Vint = puc[0]; 1831 pc.IFL2 = FLconst; 1832 } 1833 } 1834 } 1835 else 1836 { 1837 emit(cast(ubyte)opcode); 1838 } 1839 L3: 1840 1841 // If CALL, Jxx or LOOPx to a symbolic location 1842 if (/*asmstate.ucItype == ITjump &&*/ 1843 opnds.length >= 1 && opnds[0].s && opnds[0].s.isLabel()) 1844 { 1845 Dsymbol s = opnds[0].s; 1846 if (s == asmstate.psDollar) 1847 { 1848 pc.IFL2 = FLconst; 1849 if (isOneOf(OpndSize._8, uSizemaskTable[0]) || 1850 isOneOf(OpndSize._16, uSizemaskTable[0])) 1851 pc.IEV2.Vint = cast(int)opnds[0].disp; 1852 else if (isOneOf(OpndSize._32, uSizemaskTable[0])) 1853 pc.IEV2.Vpointer = cast(targ_size_t) opnds[0].disp; 1854 } 1855 else 1856 { 1857 LabelDsymbol label = s.isLabel(); 1858 if (label) 1859 { 1860 if ((pc.Iop & ~0x0F) == 0x70) 1861 pc.Iflags |= CFjmp16; 1862 if (usNumops == 1) 1863 { 1864 pc.IFL2 = FLblock; 1865 pc.IEV2.Vlsym = cast(_LabelDsymbol*)label; 1866 } 1867 else 1868 { 1869 pc.IFL1 = FLblock; 1870 pc.IEV1.Vlsym = cast(_LabelDsymbol*)label; 1871 } 1872 } 1873 } 1874 } 1875 1876 final switch (usNumops) 1877 { 1878 case 0: 1879 break; 1880 case 1: 1881 if (((aoptyTable[0] == _reg || aoptyTable[0] == _float) && 1882 amodTable[0] == _normal && (uRegmaskTable[0] & _rplus_r))) 1883 { 1884 uint reg = opnds[0].base.val; 1885 if (reg & 8) 1886 { 1887 reg &= 7; 1888 pc.Irex |= REX_B; 1889 assert(global.params.is64bit); 1890 } 1891 if (asmstate.ucItype == ITfloat) 1892 pc.Irm += reg; 1893 else 1894 pc.Iop += reg; 1895 debug instruction[insIdx-1] += reg; 1896 } 1897 else 1898 { 1899 asm_make_modrm_byte( 1900 &emit, 1901 pc, 1902 ptb.pptb1.usFlags, 1903 [opnds[0]]); 1904 } 1905 if (aoptyTable[0] == _imm) 1906 setCodeForImmediate(opnds[0], uSizemaskTable[0]); 1907 break; 1908 case 2: 1909 // 1910 // If there are two immediate operands then 1911 // 1912 if (aoptyTable[0] == _imm && 1913 aoptyTable[1] == _imm) 1914 { 1915 pc.IEV1.Vint = cast(int)opnds[0].disp; 1916 pc.IFL1 = FLconst; 1917 pc.IEV2.Vint = cast(int)opnds[1].disp; 1918 pc.IFL2 = FLconst; 1919 break; 1920 } 1921 if (aoptyTable[1] == _m || 1922 aoptyTable[1] == _rel || 1923 // If not MMX register (_mm) or XMM register (_xmm) 1924 (amodTable[0] == _rspecial && !(uRegmaskTable[0] & (0x08 | 0x10)) && !uSizemaskTable[0]) || 1925 aoptyTable[1] == _rm || 1926 (opnds[0].usFlags == _r32 && opnds[1].usFlags == _xmm) || 1927 (opnds[0].usFlags == _r32 && opnds[1].usFlags == _mm)) 1928 { 1929 version (none) 1930 { 1931 printf("test4 %d,%d,%d,%d\n", 1932 (aoptyTable[1] == _m), 1933 (aoptyTable[1] == _rel), 1934 (amodTable[0] == _rspecial && !(uRegmaskTable[0] & (0x08 | 0x10))), 1935 (aoptyTable[1] == _rm) 1936 ); 1937 printf("opcode = %x\n", opcode); 1938 } 1939 if (ptb.pptb0.opcode == 0x0F7E || // MOVD _rm32,_mm 1940 ptb.pptb0.opcode == 0x660F7E // MOVD _rm32,_xmm 1941 ) 1942 { 1943 asm_make_modrm_byte( 1944 &emit, 1945 pc, 1946 ptb.pptb1.usFlags, 1947 opnds[0 .. 2]); 1948 } 1949 else 1950 { 1951 asm_make_modrm_byte( 1952 &emit, 1953 pc, 1954 ptb.pptb1.usFlags, 1955 [opnds[1], opnds[0]]); 1956 } 1957 if(aoptyTable[0] == _imm) 1958 setCodeForImmediate(opnds[0], uSizemaskTable[0]); 1959 } 1960 else 1961 { 1962 if (((aoptyTable[0] == _reg || aoptyTable[0] == _float) && 1963 amodTable[0] == _normal && 1964 (uRegmaskTable[0] & _rplus_r))) 1965 { 1966 uint reg = opnds[0].base.val; 1967 if (reg & 8) 1968 { 1969 reg &= 7; 1970 pc.Irex |= REX_B; 1971 assert(global.params.is64bit); 1972 } 1973 else if (opnds[0].base.isSIL_DIL_BPL_SPL()) 1974 { 1975 pc.Irex |= REX; 1976 assert(global.params.is64bit); 1977 } 1978 if (asmstate.ucItype == ITfloat) 1979 pc.Irm += reg; 1980 else 1981 pc.Iop += reg; 1982 debug instruction[insIdx-1] += reg; 1983 } 1984 else if (((aoptyTable[1] == _reg || aoptyTable[1] == _float) && 1985 amodTable[1] == _normal && 1986 (uRegmaskTable[1] & _rplus_r))) 1987 { 1988 uint reg = opnds[1].base.val; 1989 if (reg & 8) 1990 { 1991 reg &= 7; 1992 pc.Irex |= REX_B; 1993 assert(global.params.is64bit); 1994 } 1995 else if (opnds[0].base.isSIL_DIL_BPL_SPL()) 1996 { 1997 pc.Irex |= REX; 1998 assert(global.params.is64bit); 1999 } 2000 if (asmstate.ucItype == ITfloat) 2001 pc.Irm += reg; 2002 else 2003 pc.Iop += reg; 2004 debug instruction[insIdx-1] += reg; 2005 } 2006 else if (ptb.pptb0.opcode == 0xF30FD6 || 2007 ptb.pptb0.opcode == 0x0F12 || 2008 ptb.pptb0.opcode == 0x0F16 || 2009 ptb.pptb0.opcode == 0x660F50 || 2010 ptb.pptb0.opcode == 0x0F50 || 2011 ptb.pptb0.opcode == 0x660FD7 || 2012 ptb.pptb0.opcode == MOVDQ2Q || 2013 ptb.pptb0.opcode == 0x0FD7) 2014 { 2015 asm_make_modrm_byte( 2016 &emit, 2017 pc, 2018 ptb.pptb1.usFlags, 2019 [opnds[1], opnds[0]]); 2020 } 2021 else 2022 { 2023 asm_make_modrm_byte( 2024 &emit, 2025 pc, 2026 ptb.pptb1.usFlags, 2027 opnds[0 .. 2]); 2028 2029 } 2030 if (aoptyTable[0] == _imm) 2031 { 2032 setCodeForImmediate(opnds[0], uSizemaskTable[0]); 2033 } 2034 else if(aoptyTable[1] == _imm) 2035 { 2036 setCodeForImmediate(opnds[1], uSizemaskTable[1]); 2037 } 2038 } 2039 break; 2040 2041 case 3: 2042 if (aoptyTable[1] == _m || aoptyTable[1] == _rm || 2043 opcode == 0x0FC5 || // pextrw _r32, _mm, _imm8 2044 opcode == 0x660FC5 || // pextrw _r32, _xmm, _imm8 2045 opcode == 0x660F3A20 || // pinsrb _xmm, _r32/m8, _imm8 2046 opcode == 0x660F3A22 || // pinsrd _xmm, _rm32, _imm8 2047 opcode == VEX_128_WIG(0x660FC5) // vpextrw _r32, _mm, _imm8 2048 ) 2049 { 2050 asm_make_modrm_byte( 2051 &emit, 2052 pc, 2053 ptb.pptb1.usFlags, 2054 [opnds[1], opnds[0]]); // swap operands 2055 } 2056 else 2057 { 2058 2059 bool setRegisterProperties(int i) 2060 { 2061 if (((aoptyTable[i] == _reg || aoptyTable[i] == _float) && 2062 amodTable[i] == _normal && 2063 (uRegmaskTable[i] &_rplus_r))) 2064 { 2065 uint reg = opnds[i].base.val; 2066 if (reg & 8) 2067 { 2068 reg &= 7; 2069 pc.Irex |= REX_B; 2070 assert(global.params.is64bit); 2071 } 2072 if (asmstate.ucItype == ITfloat) 2073 pc.Irm += reg; 2074 else 2075 pc.Iop += reg; 2076 debug instruction[insIdx-1] += reg; 2077 return true; 2078 } 2079 return false; 2080 } 2081 2082 if(!setRegisterProperties(0) && !setRegisterProperties(1)) 2083 asm_make_modrm_byte( 2084 &emit, 2085 pc, 2086 ptb.pptb1.usFlags, 2087 opnds[0 .. 2]); 2088 } 2089 if (aoptyTable[2] == _imm) 2090 setCodeForImmediate(opnds[2], uSizemaskTable[2]); 2091 break; 2092 } 2093 return finalizeCode(loc, pc, ptb); 2094 } 2095 2096 2097 /******************************* 2098 */ 2099 2100 void asmerr(const(char)* format, ...) 2101 { 2102 if (asmstate.errors) 2103 return; 2104 2105 va_list ap; 2106 va_start(ap, format); 2107 verror(asmstate.loc, format, ap); 2108 va_end(ap); 2109 2110 asmstate.errors = true; 2111 } 2112 2113 /******************************* 2114 */ 2115 2116 opflag_t asm_float_type_size(Type ptype, opflag_t *pusFloat) 2117 { 2118 *pusFloat = 0; 2119 2120 //printf("asm_float_type_size('%s')\n", ptype.toChars()); 2121 if (ptype && ptype.isscalar()) 2122 { 2123 int sz = cast(int)ptype.size(); 2124 if (sz == target.realsize) 2125 { 2126 *pusFloat = _f80; 2127 return 0; 2128 } 2129 switch (sz) 2130 { 2131 case 2: 2132 return OpndSize._16; 2133 case 4: 2134 return OpndSize._32; 2135 case 8: 2136 *pusFloat = _f64; 2137 return 0; 2138 case 10: 2139 *pusFloat = _f80; 2140 return 0; 2141 default: 2142 break; 2143 } 2144 } 2145 *pusFloat = _fanysize; 2146 return OpndSize._anysize; 2147 } 2148 2149 /******************************* 2150 */ 2151 2152 private @safe pure bool asm_isint(const ref OPND o) 2153 { 2154 if (o.base || o.s) 2155 return false; 2156 return true; 2157 } 2158 2159 private @safe pure bool asm_isNonZeroInt(const ref OPND o) 2160 { 2161 if (o.base || o.s) 2162 return false; 2163 return o.disp != 0; 2164 } 2165 2166 /******************************* 2167 */ 2168 2169 private @safe pure bool asm_is_fpreg(const(char)[] szReg) 2170 { 2171 return szReg == "ST"; 2172 } 2173 2174 /******************************* 2175 * Merge operands o1 and o2 into a single operand, o1. 2176 */ 2177 2178 private void asm_merge_opnds(ref OPND o1, ref OPND o2) 2179 { 2180 void illegalAddressError(string debugWhy) 2181 { 2182 debug (debuga) printf("Invalid addr because /%.s/\n", 2183 debugWhy.ptr, cast(int)debugWhy.length); 2184 asmerr("cannot have two symbols in addressing mode"); 2185 } 2186 2187 //printf("asm_merge_opnds()\n"); 2188 debug (EXTRA_DEBUG) debug (debuga) 2189 { 2190 printf("asm_merge_opnds(o1 = "); 2191 asm_output_popnd(&o1); 2192 printf(", o2 = "); 2193 asm_output_popnd(&o2); 2194 printf(")\n"); 2195 } 2196 debug (EXTRA_DEBUG) 2197 printf("Combining Operands: mult1 = %d, mult2 = %d", 2198 o1.uchMultiplier, o2.uchMultiplier); 2199 /* combine the OPND's disp field */ 2200 if (o2.segreg) 2201 { 2202 if (o1.segreg) 2203 return illegalAddressError("o1.segment && o2.segreg"); 2204 else 2205 o1.segreg = o2.segreg; 2206 } 2207 2208 // combine the OPND's symbol field 2209 if (o1.s && o2.s) 2210 { 2211 return illegalAddressError("o1.s && os.s"); 2212 } 2213 else if (o2.s) 2214 { 2215 o1.s = o2.s; 2216 } 2217 else if (o1.s && o1.s.isTupleDeclaration()) 2218 { 2219 TupleDeclaration tup = o1.s.isTupleDeclaration(); 2220 size_t index = cast(int)o2.disp; 2221 if (index >= tup.objects.dim) 2222 { 2223 asmerr("tuple index %llu exceeds length %llu", 2224 cast(ulong) index, cast(ulong) tup.objects.dim); 2225 } 2226 else 2227 { 2228 RootObject o = (*tup.objects)[index]; 2229 if (o.dyncast() == DYNCAST.dsymbol) 2230 { 2231 o1.s = cast(Dsymbol)o; 2232 return; 2233 } 2234 else if (o.dyncast() == DYNCAST.expression) 2235 { 2236 Expression e = cast(Expression)o; 2237 if (e.op == TOK.variable) 2238 { 2239 o1.s = (cast(VarExp)e).var; 2240 return; 2241 } 2242 else if (e.op == TOK.function_) 2243 { 2244 o1.s = (cast(FuncExp)e).fd; 2245 return; 2246 } 2247 } 2248 asmerr("invalid asm operand `%s`", o1.s.toChars()); 2249 } 2250 } 2251 2252 if (o1.disp && o2.disp) 2253 o1.disp += o2.disp; 2254 else if (o2.disp) 2255 o1.disp = o2.disp; 2256 2257 /* combine the OPND's base field */ 2258 if (o1.base != null && o2.base != null) 2259 return illegalAddressError("o1.base != null && o2.base != null"); 2260 else if (o2.base) 2261 o1.base = o2.base; 2262 2263 /* Combine the displacement register fields */ 2264 if (o2.pregDisp1) 2265 { 2266 if (o1.pregDisp2) 2267 return illegalAddressError("o2.pregDisp1 && o1.pregDisp2"); 2268 else if (o1.pregDisp1) 2269 { 2270 if (o1.uchMultiplier || 2271 (o2.pregDisp1.val == _ESP && 2272 (getOpndSize(o2.pregDisp1.ty) == OpndSize._32) && 2273 !o2.uchMultiplier)) 2274 { 2275 o1.pregDisp2 = o1.pregDisp1; 2276 o1.pregDisp1 = o2.pregDisp1; 2277 } 2278 else 2279 o1.pregDisp2 = o2.pregDisp1; 2280 } 2281 else 2282 o1.pregDisp1 = o2.pregDisp1; 2283 } 2284 if (o2.pregDisp2) 2285 { 2286 if (o1.pregDisp2) 2287 return illegalAddressError("o1.pregDisp2 && o2.pregDisp2"); 2288 else 2289 o1.pregDisp2 = o2.pregDisp2; 2290 } 2291 2292 if (o1.bRIP && (o1.pregDisp1 || o2.bRIP || o1.base)) 2293 return illegalAddressError("o1.pregDisp1 && RIP"); 2294 o1.bRIP |= o2.bRIP; 2295 2296 if (o1.base && o1.pregDisp1) 2297 { 2298 asmerr("operand cannot have both %s and [%s]", o1.base.regstr.ptr, o1.pregDisp1.regstr.ptr); 2299 return; 2300 } 2301 2302 if (o1.base && o1.disp) 2303 { 2304 asmerr("operand cannot have both %s and 0x%llx", o1.base.regstr.ptr, o1.disp); 2305 return; 2306 } 2307 2308 if (o2.uchMultiplier) 2309 { 2310 if (o1.uchMultiplier) 2311 return illegalAddressError("o1.uchMultiplier && o2.uchMultiplier"); 2312 else 2313 o1.uchMultiplier = o2.uchMultiplier; 2314 } 2315 if (o2.ptype && !o1.ptype) 2316 o1.ptype = o2.ptype; 2317 if (o2.bOffset) 2318 o1.bOffset = o2.bOffset; 2319 if (o2.bSeg) 2320 o1.bSeg = o2.bSeg; 2321 2322 if (o2.ajt && !o1.ajt) 2323 o1.ajt = o2.ajt; 2324 2325 debug (EXTRA_DEBUG) 2326 printf("Result = %d\n", o1.uchMultiplier); 2327 debug (debuga) 2328 { 2329 printf("Merged result = /"); 2330 asm_output_popnd(o1); 2331 printf("/\n"); 2332 } 2333 } 2334 2335 /*************************************** 2336 */ 2337 2338 void asm_merge_symbol(ref OPND o1, Dsymbol s) 2339 { 2340 EnumMember em; 2341 2342 //printf("asm_merge_symbol(s = %s %s)\n", s.kind(), s.toChars()); 2343 s = s.toAlias(); 2344 //printf("s = %s %s\n", s.kind(), s.toChars()); 2345 if (s.isLabel()) 2346 { 2347 o1.s = s; 2348 return; 2349 } 2350 2351 if (auto v = s.isVarDeclaration()) 2352 { 2353 if (auto fd = asmstate.sc.func) 2354 { 2355 /* https://issues.dlang.org/show_bug.cgi?id=6166 2356 * We could leave it on unless fd.nrvo_var==v, 2357 * but fd.nrvo_var isn't set yet 2358 */ 2359 fd.nrvo_can = false; 2360 } 2361 2362 if (v.isParameter()) 2363 asmstate.statement.refparam = true; 2364 2365 v.checkNestedReference(asmstate.sc, asmstate.loc); 2366 if (v.isField()) 2367 { 2368 o1.disp += v.offset; 2369 goto L2; 2370 } 2371 2372 if (!v.type.isfloating() && v.type.ty != Tvector) 2373 { 2374 if (auto e = expandVar(WANTexpand, v)) 2375 { 2376 if (e.isErrorExp()) 2377 return; 2378 o1.disp = e.toInteger(); 2379 return; 2380 } 2381 } 2382 2383 if (v.isThreadlocal()) 2384 { 2385 asmerr("cannot directly load TLS variable `%s`", v.toChars()); 2386 return; 2387 } 2388 else if (v.isDataseg() && global.params.pic != PIC.fixed) 2389 { 2390 asmerr("cannot directly load global variable `%s` with PIC or PIE code", v.toChars()); 2391 return; 2392 } 2393 } 2394 em = s.isEnumMember(); 2395 if (em) 2396 { 2397 o1.disp = em.value().toInteger(); 2398 return; 2399 } 2400 o1.s = s; // a C identifier 2401 L2: 2402 Declaration d = s.isDeclaration(); 2403 if (!d) 2404 { 2405 asmerr("%s `%s` is not a declaration", s.kind(), s.toChars()); 2406 } 2407 else if (d.getType()) 2408 asmerr("cannot use type `%s` as an operand", d.getType().toChars()); 2409 else if (d.isTupleDeclaration()) 2410 { 2411 } 2412 else 2413 o1.ptype = d.type.toBasetype(); 2414 } 2415 2416 /**************************** 2417 * Fill in the modregrm and sib bytes of code. 2418 * Params: 2419 * emit = where to store instruction bytes generated (for debugging) 2420 * pc = instruction to be filled in 2421 * usFlags = opflag_t value from ptrntab 2422 * opnds = one for each operand 2423 */ 2424 2425 void asm_make_modrm_byte( 2426 void delegate(ubyte) emit, 2427 code *pc, 2428 opflag_t usFlags, 2429 scope OPND[] opnds) 2430 { 2431 struct MODRM_BYTE 2432 { 2433 uint rm; 2434 uint reg; 2435 uint mod; 2436 uint auchOpcode() 2437 { 2438 assert(rm < 8); 2439 assert(reg < 8); 2440 assert(mod < 4); 2441 return (mod << 6) | (reg << 3) | rm; 2442 } 2443 } 2444 2445 struct SIB_BYTE 2446 { 2447 uint base; 2448 uint index; 2449 uint ss; 2450 uint auchOpcode() 2451 { 2452 assert(base < 8); 2453 assert(index < 8); 2454 assert(ss < 4); 2455 return (ss << 6) | (index << 3) | base; 2456 } 2457 } 2458 2459 MODRM_BYTE mrmb = { 0, 0, 0 }; 2460 SIB_BYTE sib = { 0, 0, 0 }; 2461 bool bSib = false; 2462 bool bDisp = false; 2463 debug ubyte *puc; 2464 Dsymbol s; 2465 2466 bool bOffsetsym = false; 2467 2468 version (none) 2469 { 2470 printf("asm_make_modrm_byte(usFlags = x%x)\n", usFlags); 2471 printf("op1: "); 2472 asm_output_flags(opnds[0].usFlags); 2473 printf("\n"); 2474 if (opnds.length == 2) 2475 { 2476 printf("op2: "); 2477 asm_output_flags(opnds[1].usFlags); 2478 } 2479 printf("\n"); 2480 } 2481 2482 const OpndSize uSizemask = getOpndSize(opnds[0].usFlags); 2483 auto aopty = ASM_GET_aopty(opnds[0].usFlags); 2484 const amod = ASM_GET_amod(opnds[0].usFlags); 2485 s = opnds[0].s; 2486 if (s) 2487 { 2488 Declaration d = s.isDeclaration(); 2489 2490 if ((amod == _fn16 || amod == _flbl) && aopty == _rel && opnds.length == 2) 2491 { 2492 aopty = _m; 2493 goto L1; 2494 } 2495 2496 if (amod == _fn16 || amod == _fn32) 2497 { 2498 pc.Iflags |= CFoff; 2499 debug 2500 { 2501 emit(0); 2502 emit(0); 2503 } 2504 if (aopty == _m || aopty == _mnoi) 2505 { 2506 pc.IFL1 = FLdata; 2507 pc.IEV1.Vdsym = cast(_Declaration*)d; 2508 pc.IEV1.Voffset = 0; 2509 } 2510 else 2511 { 2512 if (aopty == _p) 2513 pc.Iflags |= CFseg; 2514 2515 debug 2516 { 2517 if (aopty == _p || aopty == _rel) 2518 { 2519 emit(0); 2520 emit(0); 2521 } 2522 } 2523 2524 pc.IFL2 = FLfunc; 2525 pc.IEV2.Vdsym = cast(_Declaration*)d; 2526 pc.IEV2.Voffset = 0; 2527 //return; 2528 } 2529 } 2530 else 2531 { 2532 L1: 2533 LabelDsymbol label = s.isLabel(); 2534 if (label) 2535 { 2536 if (s == asmstate.psDollar) 2537 { 2538 pc.IFL1 = FLconst; 2539 if (isOneOf(uSizemask, OpndSize._16_8)) 2540 pc.IEV1.Vint = cast(int)opnds[0].disp; 2541 else if (isOneOf(uSizemask, OpndSize._32)) 2542 pc.IEV1.Vpointer = cast(targ_size_t) opnds[0].disp; 2543 } 2544 else 2545 { 2546 pc.IFL1 = global.params.is64bit ? FLblock : FLblockoff; 2547 pc.IEV1.Vlsym = cast(_LabelDsymbol*)label; 2548 } 2549 pc.Iflags |= CFoff; 2550 } 2551 else if (s == asmstate.psLocalsize) 2552 { 2553 pc.IFL1 = FLlocalsize; 2554 pc.IEV1.Vdsym = null; 2555 pc.Iflags |= CFoff; 2556 pc.IEV1.Voffset = opnds[0].disp; 2557 } 2558 else if (s.isFuncDeclaration()) 2559 { 2560 pc.IFL1 = FLfunc; 2561 pc.IEV1.Vdsym = cast(_Declaration*)d; 2562 pc.Iflags |= CFoff; 2563 pc.IEV1.Voffset = opnds[0].disp; 2564 } 2565 else 2566 { 2567 debug (debuga) 2568 printf("Setting up symbol %s\n", d.ident.toChars()); 2569 pc.IFL1 = FLdsymbol; 2570 pc.IEV1.Vdsym = cast(_Declaration*)d; 2571 pc.Iflags |= CFoff; 2572 pc.IEV1.Voffset = opnds[0].disp; 2573 } 2574 } 2575 } 2576 mrmb.reg = usFlags & NUM_MASK; 2577 2578 if (s && (aopty == _m || aopty == _mnoi)) 2579 { 2580 if (s.isLabel) 2581 { 2582 mrmb.rm = BPRM; 2583 mrmb.mod = 0x0; 2584 } 2585 else if (s == asmstate.psLocalsize) 2586 { 2587 DATA_REF: 2588 mrmb.rm = BPRM; 2589 if (amod == _addr16 || amod == _addr32) 2590 mrmb.mod = 0x2; 2591 else 2592 mrmb.mod = 0x0; 2593 } 2594 else 2595 { 2596 Declaration d = s.isDeclaration(); 2597 assert(d); 2598 if (d.isDataseg() || d.isCodeseg()) 2599 { 2600 if (!global.params.is64bit && amod == _addr16) 2601 { 2602 asmerr("cannot have 16 bit addressing mode in 32 bit code"); 2603 return; 2604 } 2605 goto DATA_REF; 2606 } 2607 mrmb.rm = BPRM; 2608 mrmb.mod = 0x2; 2609 } 2610 } 2611 2612 if (aopty == _reg || amod == _rspecial) 2613 { 2614 mrmb.mod = 0x3; 2615 mrmb.rm |= opnds[0].base.val & NUM_MASK; 2616 if (opnds[0].base.val & NUM_MASKR) 2617 pc.Irex |= REX_B; 2618 else if (opnds[0].base.isSIL_DIL_BPL_SPL()) 2619 pc.Irex |= REX; 2620 } 2621 else if (amod == _addr16) 2622 { 2623 uint rm; 2624 2625 debug (debuga) 2626 printf("This is an ADDR16\n"); 2627 if (!opnds[0].pregDisp1) 2628 { 2629 rm = 0x6; 2630 if (!s) 2631 bDisp = true; 2632 } 2633 else 2634 { 2635 uint r1r2; 2636 static uint X(uint r1, uint r2) { return (r1 * 16) + r2; } 2637 static uint Y(uint r1) { return X(r1,9); } 2638 2639 2640 if (opnds[0].pregDisp2) 2641 r1r2 = X(opnds[0].pregDisp1.val,opnds[0].pregDisp2.val); 2642 else 2643 r1r2 = Y(opnds[0].pregDisp1.val); 2644 switch (r1r2) 2645 { 2646 case X(_BX,_SI): rm = 0; break; 2647 case X(_BX,_DI): rm = 1; break; 2648 case Y(_BX): rm = 7; break; 2649 2650 case X(_BP,_SI): rm = 2; break; 2651 case X(_BP,_DI): rm = 3; break; 2652 case Y(_BP): rm = 6; bDisp = true; break; 2653 2654 case X(_SI,_BX): rm = 0; break; 2655 case X(_SI,_BP): rm = 2; break; 2656 case Y(_SI): rm = 4; break; 2657 2658 case X(_DI,_BX): rm = 1; break; 2659 case X(_DI,_BP): rm = 3; break; 2660 case Y(_DI): rm = 5; break; 2661 2662 default: 2663 asmerr("bad 16 bit index address mode"); 2664 return; 2665 } 2666 } 2667 mrmb.rm = rm; 2668 2669 debug (debuga) 2670 printf("This is an mod = %d, opnds[0].s =%p, opnds[0].disp = %lld\n", 2671 mrmb.mod, s, cast(long)opnds[0].disp); 2672 if (!s || (!mrmb.mod && opnds[0].disp)) 2673 { 2674 if ((!opnds[0].disp && !bDisp) || 2675 !opnds[0].pregDisp1) 2676 mrmb.mod = 0x0; 2677 else if (opnds[0].disp >= byte.min && 2678 opnds[0].disp <= byte.max) 2679 mrmb.mod = 0x1; 2680 else 2681 mrmb.mod = 0X2; 2682 } 2683 else 2684 bOffsetsym = true; 2685 2686 } 2687 else if (amod == _addr32 || (amod == _flbl && !global.params.is64bit)) 2688 { 2689 bool bModset = false; 2690 2691 debug (debuga) 2692 printf("This is an ADDR32\n"); 2693 if (!opnds[0].pregDisp1) 2694 mrmb.rm = 0x5; 2695 else if (opnds[0].pregDisp2 || 2696 opnds[0].uchMultiplier || 2697 (opnds[0].pregDisp1.val & NUM_MASK) == _ESP) 2698 { 2699 if (opnds[0].pregDisp2) 2700 { 2701 if (opnds[0].pregDisp2.val == _ESP) 2702 { 2703 asmerr("`ESP` cannot be scaled index register"); 2704 return; 2705 } 2706 } 2707 else 2708 { 2709 if (opnds[0].uchMultiplier && 2710 opnds[0].pregDisp1.val ==_ESP) 2711 { 2712 asmerr("`ESP` cannot be scaled index register"); 2713 return; 2714 } 2715 bDisp = true; 2716 } 2717 2718 mrmb.rm = 0x4; 2719 bSib = true; 2720 if (bDisp) 2721 { 2722 if (!opnds[0].uchMultiplier && 2723 (opnds[0].pregDisp1.val & NUM_MASK) == _ESP) 2724 { 2725 sib.base = 4; // _ESP or _R12 2726 sib.index = 0x4; 2727 if (opnds[0].pregDisp1.val & NUM_MASKR) 2728 pc.Irex |= REX_B; 2729 } 2730 else 2731 { 2732 debug (debuga) 2733 printf("Resetting the mod to 0\n"); 2734 if (opnds[0].pregDisp2) 2735 { 2736 if (opnds[0].pregDisp2.val != _EBP) 2737 { 2738 asmerr("`EBP` cannot be base register"); 2739 return; 2740 } 2741 } 2742 else 2743 { 2744 mrmb.mod = 0x0; 2745 bModset = true; 2746 } 2747 2748 sib.base = 0x5; 2749 sib.index = opnds[0].pregDisp1.val & NUM_MASK; 2750 if (opnds[0].pregDisp1.val & NUM_MASKR) 2751 pc.Irex |= REX_X; 2752 } 2753 } 2754 else 2755 { 2756 sib.base = opnds[0].pregDisp1.val & NUM_MASK; 2757 if (opnds[0].pregDisp1.val & NUM_MASKR) 2758 pc.Irex |= REX_B; 2759 // 2760 // This is to handle the special case 2761 // of using the EBP (or R13) register and no 2762 // displacement. You must put in an 2763 // 8 byte displacement in order to 2764 // get the correct opcodes. 2765 // 2766 if ((opnds[0].pregDisp1.val == _EBP || 2767 opnds[0].pregDisp1.val == _R13) && 2768 (!opnds[0].disp && !s)) 2769 { 2770 debug (debuga) 2771 printf("Setting the mod to 1 in the _EBP case\n"); 2772 mrmb.mod = 0x1; 2773 bDisp = true; // Need a 2774 // displacement 2775 bModset = true; 2776 } 2777 2778 sib.index = opnds[0].pregDisp2.val & NUM_MASK; 2779 if (opnds[0].pregDisp2.val & NUM_MASKR) 2780 pc.Irex |= REX_X; 2781 2782 } 2783 switch (opnds[0].uchMultiplier) 2784 { 2785 case 0: sib.ss = 0; break; 2786 case 1: sib.ss = 0; break; 2787 case 2: sib.ss = 1; break; 2788 case 4: sib.ss = 2; break; 2789 case 8: sib.ss = 3; break; 2790 2791 default: 2792 asmerr("scale factor must be one of 0,1,2,4,8"); 2793 return; 2794 } 2795 } 2796 else 2797 { 2798 uint rm; 2799 2800 if (opnds[0].uchMultiplier) 2801 { 2802 asmerr("scale factor not allowed"); 2803 return; 2804 } 2805 switch (opnds[0].pregDisp1.val & (NUM_MASKR | NUM_MASK)) 2806 { 2807 case _EBP: 2808 if (!opnds[0].disp && !s) 2809 { 2810 mrmb.mod = 0x1; 2811 bDisp = true; // Need a displacement 2812 bModset = true; 2813 } 2814 rm = 5; 2815 break; 2816 2817 case _ESP: 2818 asmerr("`[ESP]` addressing mode not allowed"); 2819 return; 2820 2821 default: 2822 rm = opnds[0].pregDisp1.val & NUM_MASK; 2823 break; 2824 } 2825 if (opnds[0].pregDisp1.val & NUM_MASKR) 2826 pc.Irex |= REX_B; 2827 mrmb.rm = rm; 2828 } 2829 2830 if (!bModset && (!s || 2831 (!mrmb.mod && opnds[0].disp))) 2832 { 2833 if ((!opnds[0].disp && !mrmb.mod) || 2834 (!opnds[0].pregDisp1 && !opnds[0].pregDisp2)) 2835 { 2836 mrmb.mod = 0x0; 2837 bDisp = true; 2838 } 2839 else if (opnds[0].disp >= byte.min && 2840 opnds[0].disp <= byte.max) 2841 mrmb.mod = 0x1; 2842 else 2843 mrmb.mod = 0x2; 2844 } 2845 else 2846 bOffsetsym = true; 2847 } 2848 if (opnds.length == 2 && !mrmb.reg && 2849 asmstate.ucItype != ITshift && 2850 (ASM_GET_aopty(opnds[1].usFlags) == _reg || 2851 ASM_GET_amod(opnds[1].usFlags) == _rseg || 2852 ASM_GET_amod(opnds[1].usFlags) == _rspecial)) 2853 { 2854 if (opnds[1].base.isSIL_DIL_BPL_SPL()) 2855 pc.Irex |= REX; 2856 mrmb.reg = opnds[1].base.val & NUM_MASK; 2857 if (opnds[1].base.val & NUM_MASKR) 2858 pc.Irex |= REX_R; 2859 } 2860 debug emit(cast(ubyte)mrmb.auchOpcode()); 2861 pc.Irm = cast(ubyte)mrmb.auchOpcode(); 2862 //printf("Irm = %02x\n", pc.Irm); 2863 if (bSib) 2864 { 2865 debug emit(cast(ubyte)sib.auchOpcode()); 2866 pc.Isib= cast(ubyte)sib.auchOpcode(); 2867 } 2868 if ((!s || (opnds[0].pregDisp1 && !bOffsetsym)) && 2869 aopty != _imm && 2870 (opnds[0].disp || bDisp)) 2871 { 2872 if (opnds[0].usFlags & _a16) 2873 { 2874 debug 2875 { 2876 puc = (cast(ubyte *) &(opnds[0].disp)); 2877 emit(puc[1]); 2878 emit(puc[0]); 2879 } 2880 if (usFlags & (_modrm | NUM_MASK)) 2881 { 2882 debug (debuga) 2883 printf("Setting up value %lld\n", cast(long)opnds[0].disp); 2884 pc.IEV1.Vint = cast(int)opnds[0].disp; 2885 pc.IFL1 = FLconst; 2886 } 2887 else 2888 { 2889 pc.IEV2.Vint = cast(int)opnds[0].disp; 2890 pc.IFL2 = FLconst; 2891 } 2892 } 2893 else 2894 { 2895 debug 2896 { 2897 puc = (cast(ubyte *) &(opnds[0].disp)); 2898 emit(puc[3]); 2899 emit(puc[2]); 2900 emit(puc[1]); 2901 emit(puc[0]); 2902 } 2903 if (usFlags & (_modrm | NUM_MASK)) 2904 { 2905 debug (debuga) 2906 printf("Setting up value %lld\n", cast(long)opnds[0].disp); 2907 pc.IEV1.Vpointer = cast(targ_size_t) opnds[0].disp; 2908 pc.IFL1 = FLconst; 2909 } 2910 else 2911 { 2912 pc.IEV2.Vpointer = cast(targ_size_t) opnds[0].disp; 2913 pc.IFL2 = FLconst; 2914 } 2915 2916 } 2917 } 2918 } 2919 2920 /******************************* 2921 */ 2922 2923 regm_t asm_modify_regs(PTRNTAB ptb, scope OPND[] opnds) 2924 { 2925 regm_t usRet = 0; 2926 2927 switch (ptb.pptb0.usFlags & MOD_MASK) 2928 { 2929 case _modsi: 2930 usRet |= mSI; 2931 break; 2932 case _moddx: 2933 usRet |= mDX; 2934 break; 2935 case _mod2: 2936 if (opnds.length >= 2) 2937 usRet |= asm_modify_regs(ptb, opnds[1 .. 2]); 2938 break; 2939 case _modax: 2940 usRet |= mAX; 2941 break; 2942 case _modnot1: 2943 opnds = []; 2944 break; 2945 case _modaxdx: 2946 usRet |= (mAX | mDX); 2947 break; 2948 case _moddi: 2949 usRet |= mDI; 2950 break; 2951 case _modsidi: 2952 usRet |= (mSI | mDI); 2953 break; 2954 case _modcx: 2955 usRet |= mCX; 2956 break; 2957 case _modes: 2958 /*usRet |= mES;*/ 2959 break; 2960 case _modall: 2961 asmstate.bReturnax = true; 2962 return /*mES |*/ ALLREGS; 2963 case _modsiax: 2964 usRet |= (mSI | mAX); 2965 break; 2966 case _modsinot1: 2967 usRet |= mSI; 2968 opnds = []; 2969 break; 2970 case _modcxr11: 2971 usRet |= (mCX | mR11); 2972 break; 2973 case _modxmm0: 2974 usRet |= mXMM0; 2975 break; 2976 default: 2977 break; 2978 } 2979 if (opnds.length >= 1 && ASM_GET_aopty(opnds[0].usFlags) == _reg) 2980 { 2981 switch (ASM_GET_amod(opnds[0].usFlags)) 2982 { 2983 default: 2984 usRet |= 1 << opnds[0].base.val; 2985 usRet &= ~(mBP | mSP); // ignore changing these 2986 break; 2987 2988 case _rseg: 2989 //if (popnd1.base.val == _ES) 2990 //usRet |= mES; 2991 break; 2992 2993 case _rspecial: 2994 break; 2995 } 2996 } 2997 if (usRet & mAX) 2998 asmstate.bReturnax = true; 2999 3000 return usRet; 3001 } 3002 3003 /******************************* 3004 * Match flags in operand against flags in opcode table. 3005 * Returns: 3006 * true if match 3007 */ 3008 3009 bool asm_match_flags(opflag_t usOp, opflag_t usTable) 3010 { 3011 ASM_OPERAND_TYPE aoptyTable; 3012 ASM_OPERAND_TYPE aoptyOp; 3013 ASM_MODIFIERS amodTable; 3014 ASM_MODIFIERS amodOp; 3015 uint uRegmaskTable; 3016 uint uRegmaskOp; 3017 ubyte bRegmatch; 3018 bool bRetval = false; 3019 uint bSizematch; 3020 3021 //printf("asm_match_flags(usOp = x%x, usTable = x%x)\n", usOp, usTable); 3022 //printf("usOp : "); asm_output_flags(usOp ); printf("\n"); 3023 //printf("usTable: "); asm_output_flags(usTable); printf("\n"); 3024 if (asmstate.ucItype == ITfloat) 3025 { 3026 return asm_match_float_flags(usOp, usTable); 3027 } 3028 3029 const OpndSize uSizemaskOp = getOpndSize(usOp); 3030 const OpndSize uSizemaskTable = getOpndSize(usTable); 3031 3032 // Check #1, if the sizes do not match, NO match 3033 bSizematch = isOneOf(uSizemaskOp, uSizemaskTable); 3034 3035 amodOp = ASM_GET_amod(usOp); 3036 3037 aoptyTable = ASM_GET_aopty(usTable); 3038 aoptyOp = ASM_GET_aopty(usOp); 3039 3040 // _mmm64 matches with a 64 bit mem or an MMX register 3041 if (usTable == _mmm64) 3042 { 3043 if (usOp == _mm) 3044 goto Lmatch; 3045 if (aoptyOp == _m && (bSizematch || uSizemaskOp == OpndSize._anysize)) 3046 goto Lmatch; 3047 goto EXIT; 3048 } 3049 3050 // _xmm_m32, _xmm_m64, _xmm_m128 match with XMM register or memory 3051 if (usTable == _xmm_m16 || 3052 usTable == _xmm_m32 || 3053 usTable == _xmm_m64 || 3054 usTable == _xmm_m128) 3055 { 3056 if (usOp == _xmm || usOp == (_xmm|_xmm0)) 3057 goto Lmatch; 3058 if (aoptyOp == _m && (bSizematch || uSizemaskOp == OpndSize._anysize)) 3059 goto Lmatch; 3060 } 3061 3062 if (usTable == _ymm_m256) 3063 { 3064 if (usOp == _ymm) 3065 goto Lmatch; 3066 if (aoptyOp == _m && (bSizematch || uSizemaskOp == OpndSize._anysize)) 3067 goto Lmatch; 3068 } 3069 3070 if (!bSizematch && uSizemaskTable) 3071 { 3072 //printf("no size match\n"); 3073 goto EXIT; 3074 } 3075 3076 3077 // 3078 // The operand types must match, otherwise return false. 3079 // There is one exception for the _rm which is a table entry which matches 3080 // _reg or _m 3081 // 3082 if (aoptyTable != aoptyOp) 3083 { 3084 if (aoptyTable == _rm && (aoptyOp == _reg || 3085 aoptyOp == _m || 3086 aoptyOp == _rel)) 3087 goto Lok; 3088 if (aoptyTable == _mnoi && aoptyOp == _m && 3089 (uSizemaskOp == OpndSize._32 && amodOp == _addr16 || 3090 uSizemaskOp == OpndSize._48 && amodOp == _addr32 || 3091 uSizemaskOp == OpndSize._48 && amodOp == _normal) 3092 ) 3093 goto Lok; 3094 goto EXIT; 3095 } 3096 Lok: 3097 3098 // 3099 // Looks like a match so far, check to see if anything special is going on 3100 // 3101 amodTable = ASM_GET_amod(usTable); 3102 uRegmaskOp = ASM_GET_uRegmask(usOp); 3103 uRegmaskTable = ASM_GET_uRegmask(usTable); 3104 bRegmatch = ((!uRegmaskTable && !uRegmaskOp) || 3105 (uRegmaskTable & uRegmaskOp)); 3106 3107 switch (amodTable) 3108 { 3109 case _normal: // Normal's match with normals 3110 switch(amodOp) 3111 { 3112 case _normal: 3113 case _addr16: 3114 case _addr32: 3115 case _fn16: 3116 case _fn32: 3117 case _flbl: 3118 bRetval = (bSizematch || bRegmatch); 3119 goto EXIT; 3120 default: 3121 goto EXIT; 3122 } 3123 case _rseg: 3124 case _rspecial: 3125 bRetval = (amodOp == amodTable && bRegmatch); 3126 goto EXIT; 3127 default: 3128 assert(0); 3129 } 3130 EXIT: 3131 version(none) 3132 { 3133 printf("OP : "); 3134 asm_output_flags(usOp); 3135 printf("\nTBL: "); 3136 asm_output_flags(usTable); 3137 printf(": %s\n", bRetval ? "MATCH" : "NOMATCH"); 3138 } 3139 return bRetval; 3140 3141 Lmatch: 3142 //printf("match\n"); 3143 return true; 3144 } 3145 3146 /******************************* 3147 */ 3148 3149 bool asm_match_float_flags(opflag_t usOp, opflag_t usTable) 3150 { 3151 ASM_OPERAND_TYPE aoptyTable; 3152 ASM_OPERAND_TYPE aoptyOp; 3153 ASM_MODIFIERS amodTable; 3154 ASM_MODIFIERS amodOp; 3155 uint uRegmaskTable; 3156 uint uRegmaskOp; 3157 uint bRegmatch; 3158 3159 3160 // 3161 // Check #1, if the sizes do not match, NO match 3162 // 3163 uRegmaskOp = ASM_GET_uRegmask(usOp); 3164 uRegmaskTable = ASM_GET_uRegmask(usTable); 3165 bRegmatch = (uRegmaskTable & uRegmaskOp); 3166 3167 if (!(isOneOf(getOpndSize(usOp), getOpndSize(usTable)) || 3168 bRegmatch)) 3169 return false; 3170 3171 aoptyTable = ASM_GET_aopty(usTable); 3172 aoptyOp = ASM_GET_aopty(usOp); 3173 // 3174 // The operand types must match, otherwise return false. 3175 // There is one exception for the _rm which is a table entry which matches 3176 // _reg or _m 3177 // 3178 if (aoptyTable != aoptyOp) 3179 { 3180 if (aoptyOp != _float) 3181 return false; 3182 } 3183 3184 // 3185 // Looks like a match so far, check to see if anything special is going on 3186 // 3187 amodOp = ASM_GET_amod(usOp); 3188 amodTable = ASM_GET_amod(usTable); 3189 switch (amodTable) 3190 { 3191 // Normal's match with normals 3192 case _normal: 3193 switch(amodOp) 3194 { 3195 case _normal: 3196 case _addr16: 3197 case _addr32: 3198 case _fn16: 3199 case _fn32: 3200 case _flbl: 3201 return true; 3202 default: 3203 return false; 3204 } 3205 case _rseg: 3206 case _rspecial: 3207 return false; 3208 default: 3209 assert(0); 3210 } 3211 } 3212 3213 3214 /******************************* 3215 */ 3216 3217 //debug 3218 void asm_output_flags(opflag_t opflags) 3219 { 3220 ASM_OPERAND_TYPE aopty = ASM_GET_aopty(opflags); 3221 ASM_MODIFIERS amod = ASM_GET_amod(opflags); 3222 uint uRegmask = ASM_GET_uRegmask(opflags); 3223 const OpndSize uSizemask = getOpndSize(opflags); 3224 3225 const(char)* s; 3226 with (OpndSize) 3227 switch (uSizemask) 3228 { 3229 case none: s = "none"; break; 3230 case _8: s = "_8"; break; 3231 case _16: s = "_16"; break; 3232 case _32: s = "_32"; break; 3233 case _48: s = "_48"; break; 3234 case _64: s = "_64"; break; 3235 case _128: s = "_128"; break; 3236 case _16_8: s = "_16_8"; break; 3237 case _32_8: s = "_32_8"; break; 3238 case _32_16: s = "_32_16"; break; 3239 case _32_16_8: s = "_32_16_8"; break; 3240 case _48_32: s = "_48_32"; break; 3241 case _48_32_16_8: s = "_48_32_16_8"; break; 3242 case _64_32: s = "_64_32"; break; 3243 case _64_32_8: s = "_64_32_8"; break; 3244 case _64_32_16: s = "_64_32_16"; break; 3245 case _64_32_16_8: s = "_64_32_16_8"; break; 3246 case _64_48_32_16_8: s = "_64_48_32_16_8"; break; 3247 case _anysize: s = "_anysize"; break; 3248 3249 default: 3250 printf("uSizemask = x%x\n", uSizemask); 3251 assert(0); 3252 } 3253 printf("%s ", s); 3254 3255 printf("_"); 3256 switch (aopty) 3257 { 3258 case _reg: 3259 printf("reg "); 3260 break; 3261 case _m: 3262 printf("m "); 3263 break; 3264 case _imm: 3265 printf("imm "); 3266 break; 3267 case _rel: 3268 printf("rel "); 3269 break; 3270 case _mnoi: 3271 printf("mnoi "); 3272 break; 3273 case _p: 3274 printf("p "); 3275 break; 3276 case _rm: 3277 printf("rm "); 3278 break; 3279 case _float: 3280 printf("float "); 3281 break; 3282 default: 3283 printf(" UNKNOWN "); 3284 } 3285 3286 printf("_"); 3287 switch (amod) 3288 { 3289 case _normal: 3290 printf("normal "); 3291 if (uRegmask & 1) printf("_al "); 3292 if (uRegmask & 2) printf("_ax "); 3293 if (uRegmask & 4) printf("_eax "); 3294 if (uRegmask & 8) printf("_dx "); 3295 if (uRegmask & 0x10) printf("_cl "); 3296 if (uRegmask & 0x40) printf("_rax "); 3297 if (uRegmask & 0x20) printf("_rplus_r "); 3298 return; 3299 case _rseg: 3300 printf("rseg "); 3301 break; 3302 case _rspecial: 3303 printf("rspecial "); 3304 break; 3305 case _addr16: 3306 printf("addr16 "); 3307 break; 3308 case _addr32: 3309 printf("addr32 "); 3310 break; 3311 case _fn16: 3312 printf("fn16 "); 3313 break; 3314 case _fn32: 3315 printf("fn32 "); 3316 break; 3317 case _flbl: 3318 printf("flbl "); 3319 break; 3320 default: 3321 printf("UNKNOWN "); 3322 break; 3323 } 3324 printf("uRegmask=x%02x", uRegmask); 3325 3326 } 3327 3328 /******************************* 3329 */ 3330 3331 //debug 3332 void asm_output_popnd(const ref OPND popnd) 3333 { 3334 if (popnd.segreg) 3335 printf("%s:", popnd.segreg.regstr.ptr); 3336 3337 if (popnd.s) 3338 printf("%s", popnd.s.ident.toChars()); 3339 3340 if (popnd.base) 3341 printf("%s", popnd.base.regstr.ptr); 3342 if (popnd.pregDisp1) 3343 { 3344 if (popnd.pregDisp2) 3345 { 3346 if (popnd.usFlags & _a32) 3347 { 3348 if (popnd.uchMultiplier) 3349 printf("[%s][%s*%d]", 3350 popnd.pregDisp1.regstr.ptr, 3351 popnd.pregDisp2.regstr.ptr, 3352 popnd.uchMultiplier); 3353 else 3354 printf("[%s][%s]", 3355 popnd.pregDisp1.regstr.ptr, 3356 popnd.pregDisp2.regstr.ptr); 3357 } 3358 else 3359 printf("[%s+%s]", 3360 popnd.pregDisp1.regstr.ptr, 3361 popnd.pregDisp2.regstr.ptr); 3362 } 3363 else 3364 { 3365 if (popnd.uchMultiplier) 3366 printf("[%s*%d]", 3367 popnd.pregDisp1.regstr.ptr, 3368 popnd.uchMultiplier); 3369 else 3370 printf("[%s]", 3371 popnd.pregDisp1.regstr.ptr); 3372 } 3373 } 3374 if (ASM_GET_aopty(popnd.usFlags) == _imm) 3375 printf("%llxh", cast(long)popnd.disp); 3376 else if (popnd.disp) 3377 printf("+%llxh", cast(long)popnd.disp); 3378 } 3379 3380 void printOperands(OP* pop, scope OPND[] opnds) 3381 { 3382 printf("\t%s\t", asm_opstr(pop)); 3383 foreach (i, ref opnd; opnds) 3384 { 3385 asm_output_popnd(opnd); 3386 if (i != opnds.length - 1) 3387 printf(","); 3388 } 3389 printf("\n"); 3390 } 3391 3392 3393 3394 /******************************* 3395 */ 3396 3397 immutable(REG)* asm_reg_lookup(const(char)[] s) 3398 { 3399 //dbg_printf("asm_reg_lookup('%s')\n",s); 3400 3401 for (int i = 0; i < regtab.length; i++) 3402 { 3403 if (s == regtab[i].regstr) 3404 { 3405 return ®tab[i]; 3406 } 3407 } 3408 if (global.params.is64bit) 3409 { 3410 for (int i = 0; i < regtab64.length; i++) 3411 { 3412 if (s == regtab64[i].regstr) 3413 { 3414 return ®tab64[i]; 3415 } 3416 } 3417 } 3418 return null; 3419 } 3420 3421 3422 /******************************* 3423 */ 3424 3425 void asm_token() 3426 { 3427 if (asmstate.tok) 3428 asmstate.tok = asmstate.tok.next; 3429 asm_token_trans(asmstate.tok); 3430 } 3431 3432 /******************************* 3433 */ 3434 3435 void asm_token_trans(Token *tok) 3436 { 3437 asmstate.tokValue = TOK.endOfFile; 3438 if (tok) 3439 { 3440 asmstate.tokValue = tok.value; 3441 if (asmstate.tokValue == TOK.identifier) 3442 { 3443 const id = tok.ident.toString(); 3444 if (id.length < 20) 3445 { 3446 ASMTK asmtk = cast(ASMTK) binary(id.ptr, cast(const(char)**)apszAsmtk.ptr, ASMTKmax); 3447 if (cast(int)asmtk >= 0) 3448 asmstate.tokValue = cast(TOK) (asmtk + TOK.max_ + 1); 3449 } 3450 } 3451 } 3452 } 3453 3454 /******************************* 3455 */ 3456 3457 OpndSize asm_type_size(Type ptype, bool bPtr) 3458 { 3459 OpndSize u; 3460 3461 //if (ptype) printf("asm_type_size('%s') = %d\n", ptype.toChars(), (int)ptype.size()); 3462 u = OpndSize._anysize; 3463 if (ptype && ptype.ty != Tfunction /*&& ptype.isscalar()*/) 3464 { 3465 switch (cast(int)ptype.size()) 3466 { 3467 case 0: asmerr("bad type/size of operands `%s`", "0 size".ptr); break; 3468 case 1: u = OpndSize._8; break; 3469 case 2: u = OpndSize._16; break; 3470 case 4: u = OpndSize._32; break; 3471 case 6: u = OpndSize._48; break; 3472 3473 case 8: if (global.params.is64bit || bPtr) 3474 u = OpndSize._64; 3475 break; 3476 3477 case 16: u = OpndSize._128; break; 3478 default: break; 3479 } 3480 } 3481 return u; 3482 } 3483 3484 /******************************* 3485 * start of inline assemblers expression parser 3486 * NOTE: functions in call order instead of alphabetical 3487 */ 3488 3489 /******************************************* 3490 * Parse DA expression 3491 * 3492 * Very limited define address to place a code 3493 * address in the assembly 3494 * Problems: 3495 * o Should use dw offset and dd offset instead, 3496 * for near/far support. 3497 * o Should be able to add an offset to the label address. 3498 * o Blocks addressed by DA should get their Bpred set correctly 3499 * for optimizer. 3500 */ 3501 3502 code *asm_da_parse(OP *pop) 3503 { 3504 CodeBuilder cdb; 3505 cdb.ctor(); 3506 while (1) 3507 { 3508 if (asmstate.tokValue == TOK.identifier) 3509 { 3510 LabelDsymbol label = asmstate.sc.func.searchLabel(asmstate.tok.ident); 3511 if (!label) 3512 { 3513 asmerr("label `%s` not found", asmstate.tok.ident.toChars()); 3514 break; 3515 } 3516 else 3517 label.iasm = true; 3518 3519 if (global.params.symdebug) 3520 cdb.genlinnum(Srcpos.create(asmstate.loc.filename, asmstate.loc.linnum, asmstate.loc.charnum)); 3521 cdb.genasm(cast(_LabelDsymbol*)label); 3522 } 3523 else 3524 { 3525 asmerr("label expected as argument to DA pseudo-op"); // illegal addressing mode 3526 break; 3527 } 3528 asm_token(); 3529 if (asmstate.tokValue != TOK.comma) 3530 break; 3531 asm_token(); 3532 } 3533 3534 asmstate.statement.regs |= mES|ALLREGS; 3535 asmstate.bReturnax = true; 3536 3537 return cdb.finish(); 3538 } 3539 3540 /******************************************* 3541 * Parse DB, DW, DD, DQ and DT expressions. 3542 */ 3543 3544 code *asm_db_parse(OP *pop) 3545 { 3546 union DT 3547 { 3548 targ_ullong ul; 3549 targ_float f; 3550 targ_double d; 3551 targ_ldouble ld; 3552 byte[10] value; 3553 } 3554 DT dt; 3555 3556 static const ubyte[7] opsize = [ 1,2,4,8,4,8,10 ]; 3557 3558 uint op = pop.usNumops & ITSIZE; 3559 size_t usSize = opsize[op]; 3560 3561 size_t usBytes = 0; 3562 size_t usMaxbytes = 0; 3563 byte *bytes = null; 3564 3565 while (1) 3566 { 3567 size_t len; 3568 ubyte *q; 3569 ubyte *qstart = null; 3570 3571 if (usBytes+usSize > usMaxbytes) 3572 { 3573 usMaxbytes = usBytes + usSize + 10; 3574 bytes = cast(byte *)mem.xrealloc(bytes, usMaxbytes); 3575 } 3576 switch (asmstate.tokValue) 3577 { 3578 case TOK.int32Literal: 3579 dt.ul = cast(d_int32)asmstate.tok.intvalue; 3580 goto L1; 3581 case TOK.uns32Literal: 3582 dt.ul = cast(d_uns32)asmstate.tok.unsvalue; 3583 goto L1; 3584 case TOK.int64Literal: 3585 dt.ul = asmstate.tok.intvalue; 3586 goto L1; 3587 case TOK.uns64Literal: 3588 dt.ul = asmstate.tok.unsvalue; 3589 goto L1; 3590 L1: 3591 switch (op) 3592 { 3593 case OPdb: 3594 case OPds: 3595 case OPdi: 3596 case OPdl: 3597 break; 3598 default: 3599 asmerr("floating point expected"); 3600 } 3601 goto L2; 3602 3603 case TOK.float32Literal: 3604 case TOK.float64Literal: 3605 case TOK.float80Literal: 3606 switch (op) 3607 { 3608 case OPdf: 3609 dt.f = cast(float) asmstate.tok.floatvalue; 3610 break; 3611 case OPdd: 3612 dt.d = cast(double) asmstate.tok.floatvalue; 3613 break; 3614 case OPde: 3615 dt.ld = asmstate.tok.floatvalue; 3616 break; 3617 default: 3618 asmerr("integer expected"); 3619 } 3620 goto L2; 3621 3622 L2: 3623 memcpy(bytes + usBytes, &dt, usSize); 3624 usBytes += usSize; 3625 break; 3626 3627 case TOK.string_: 3628 len = asmstate.tok.len; 3629 q = cast(ubyte*)asmstate.tok.ustring; 3630 L3: 3631 if (len) 3632 { 3633 usMaxbytes += len * usSize; 3634 bytes = cast(byte *)mem.xrealloc(bytes, usMaxbytes); 3635 memcpy(bytes + usBytes, asmstate.tok.ustring, len); 3636 3637 auto p = bytes + usBytes; 3638 for (size_t i = 0; i < len; i++) 3639 { 3640 // Be careful that this works 3641 memset(p, 0, usSize); 3642 switch (op) 3643 { 3644 case OPdb: 3645 *p = cast(ubyte)*q; 3646 if (*p != *q) 3647 asmerr("character is truncated"); 3648 break; 3649 3650 case OPds: 3651 *cast(short *)p = *cast(ubyte *)q; 3652 if (*cast(short *)p != *q) 3653 asmerr("character is truncated"); 3654 break; 3655 3656 case OPdi: 3657 case OPdl: 3658 *cast(int *)p = *q; 3659 break; 3660 3661 default: 3662 asmerr("floating point expected"); 3663 } 3664 q++; 3665 p += usSize; 3666 } 3667 3668 usBytes += len * usSize; 3669 } 3670 if (qstart) 3671 { 3672 mem.xfree(qstart); 3673 qstart = null; 3674 } 3675 break; 3676 3677 case TOK.identifier: 3678 { 3679 Expression e = IdentifierExp.create(asmstate.loc, asmstate.tok.ident); 3680 Scope *sc = asmstate.sc.startCTFE(); 3681 e = e.expressionSemantic(sc); 3682 sc.endCTFE(); 3683 e = e.ctfeInterpret(); 3684 if (e.op == TOK.int64) 3685 { 3686 dt.ul = e.toInteger(); 3687 goto L2; 3688 } 3689 else if (e.op == TOK.float64) 3690 { 3691 switch (op) 3692 { 3693 case OPdf: 3694 dt.f = cast(float) e.toReal(); 3695 break; 3696 case OPdd: 3697 dt.d = cast(double) e.toReal(); 3698 break; 3699 case OPde: 3700 dt.ld = e.toReal(); 3701 break; 3702 default: 3703 asmerr("integer expected"); 3704 } 3705 goto L2; 3706 } 3707 else if (auto se = e.isStringExp()) 3708 { 3709 len = se.numberOfCodeUnits(); 3710 q = cast(ubyte *)se.peekString().ptr; 3711 if (!q) 3712 { 3713 qstart = cast(ubyte *)mem.xmalloc(len * se.sz); 3714 se.writeTo(qstart, false); 3715 q = qstart; 3716 } 3717 goto L3; 3718 } 3719 goto default; 3720 } 3721 3722 default: 3723 asmerr("constant initializer expected"); // constant initializer 3724 break; 3725 } 3726 3727 asm_token(); 3728 if (asmstate.tokValue != TOK.comma || 3729 asmstate.errors) 3730 break; 3731 asm_token(); 3732 } 3733 3734 CodeBuilder cdb; 3735 cdb.ctor(); 3736 if (global.params.symdebug) 3737 cdb.genlinnum(Srcpos.create(asmstate.loc.filename, asmstate.loc.linnum, asmstate.loc.charnum)); 3738 cdb.genasm(cast(char*)bytes, cast(uint)usBytes); 3739 code *c = cdb.finish(); 3740 mem.xfree(bytes); 3741 3742 asmstate.statement.regs |= /* mES| */ ALLREGS; 3743 asmstate.bReturnax = true; 3744 3745 return c; 3746 } 3747 3748 /********************************** 3749 * Parse and get integer expression. 3750 */ 3751 3752 int asm_getnum() 3753 { 3754 int v; 3755 dinteger_t i; 3756 3757 switch (asmstate.tokValue) 3758 { 3759 case TOK.int32Literal: 3760 v = cast(d_int32)asmstate.tok.intvalue; 3761 break; 3762 3763 case TOK.uns32Literal: 3764 v = cast(d_uns32)asmstate.tok.unsvalue; 3765 break; 3766 3767 case TOK.identifier: 3768 { 3769 Expression e = IdentifierExp.create(asmstate.loc, asmstate.tok.ident); 3770 Scope *sc = asmstate.sc.startCTFE(); 3771 e = e.expressionSemantic(sc); 3772 sc.endCTFE(); 3773 e = e.ctfeInterpret(); 3774 i = e.toInteger(); 3775 v = cast(int) i; 3776 if (v != i) 3777 asmerr("integer expected"); 3778 break; 3779 } 3780 default: 3781 asmerr("integer expected"); 3782 v = 0; // no uninitialized values 3783 break; 3784 } 3785 asm_token(); 3786 return v; 3787 } 3788 3789 /******************************* 3790 */ 3791 3792 void asm_cond_exp(out OPND o1) 3793 { 3794 //printf("asm_cond_exp()\n"); 3795 asm_log_or_exp(o1); 3796 if (asmstate.tokValue == TOK.question) 3797 { 3798 asm_token(); 3799 OPND o2; 3800 asm_cond_exp(o2); 3801 asm_chktok(TOK.colon,"colon"); 3802 OPND o3; 3803 asm_cond_exp(o3); 3804 if (o1.disp) 3805 o1 = o2; 3806 else 3807 o1 = o3; 3808 } 3809 } 3810 3811 /******************************* 3812 */ 3813 3814 void asm_log_or_exp(out OPND o1) 3815 { 3816 asm_log_and_exp(o1); 3817 while (asmstate.tokValue == TOK.orOr) 3818 { 3819 asm_token(); 3820 OPND o2; 3821 asm_log_and_exp(o2); 3822 if (asm_isint(o1) && asm_isint(o2)) 3823 o1.disp = o1.disp || o2.disp; 3824 else 3825 asmerr("bad integral operand"); 3826 o1.disp = 0; 3827 asm_merge_opnds(o1, o2); 3828 } 3829 } 3830 3831 /******************************* 3832 */ 3833 3834 void asm_log_and_exp(out OPND o1) 3835 { 3836 asm_inc_or_exp(o1); 3837 while (asmstate.tokValue == TOK.andAnd) 3838 { 3839 asm_token(); 3840 OPND o2; 3841 asm_inc_or_exp(o2); 3842 if (asm_isint(o1) && asm_isint(o2)) 3843 o1.disp = o1.disp && o2.disp; 3844 else 3845 asmerr("bad integral operand"); 3846 o2.disp = 0; 3847 asm_merge_opnds(o1, o2); 3848 } 3849 } 3850 3851 /******************************* 3852 */ 3853 3854 void asm_inc_or_exp(out OPND o1) 3855 { 3856 asm_xor_exp(o1); 3857 while (asmstate.tokValue == TOK.or) 3858 { 3859 asm_token(); 3860 OPND o2; 3861 asm_xor_exp(o2); 3862 if (asm_isint(o1) && asm_isint(o2)) 3863 o1.disp |= o2.disp; 3864 else 3865 asmerr("bad integral operand"); 3866 o2.disp = 0; 3867 asm_merge_opnds(o1, o2); 3868 } 3869 } 3870 3871 /******************************* 3872 */ 3873 3874 void asm_xor_exp(out OPND o1) 3875 { 3876 asm_and_exp(o1); 3877 while (asmstate.tokValue == TOK.xor) 3878 { 3879 asm_token(); 3880 OPND o2; 3881 asm_and_exp(o2); 3882 if (asm_isint(o1) && asm_isint(o2)) 3883 o1.disp ^= o2.disp; 3884 else 3885 asmerr("bad integral operand"); 3886 o2.disp = 0; 3887 asm_merge_opnds(o1, o2); 3888 } 3889 } 3890 3891 /******************************* 3892 */ 3893 3894 void asm_and_exp(out OPND o1) 3895 { 3896 asm_equal_exp(o1); 3897 while (asmstate.tokValue == TOK.and) 3898 { 3899 asm_token(); 3900 OPND o2; 3901 asm_equal_exp(o2); 3902 if (asm_isint(o1) && asm_isint(o2)) 3903 o1.disp &= o2.disp; 3904 else 3905 asmerr("bad integral operand"); 3906 o2.disp = 0; 3907 asm_merge_opnds(o1, o2); 3908 } 3909 } 3910 3911 /******************************* 3912 */ 3913 3914 void asm_equal_exp(out OPND o1) 3915 { 3916 asm_rel_exp(o1); 3917 while (1) 3918 { 3919 switch (asmstate.tokValue) 3920 { 3921 case TOK.equal: 3922 { 3923 asm_token(); 3924 OPND o2; 3925 asm_rel_exp(o2); 3926 if (asm_isint(o1) && asm_isint(o2)) 3927 o1.disp = o1.disp == o2.disp; 3928 else 3929 asmerr("bad integral operand"); 3930 o2.disp = 0; 3931 asm_merge_opnds(o1, o2); 3932 break; 3933 } 3934 3935 case TOK.notEqual: 3936 { 3937 asm_token(); 3938 OPND o2; 3939 asm_rel_exp(o2); 3940 if (asm_isint(o1) && asm_isint(o2)) 3941 o1.disp = o1.disp != o2.disp; 3942 else 3943 asmerr("bad integral operand"); 3944 o2.disp = 0; 3945 asm_merge_opnds(o1, o2); 3946 break; 3947 } 3948 3949 default: 3950 return; 3951 } 3952 } 3953 } 3954 3955 /******************************* 3956 */ 3957 3958 void asm_rel_exp(out OPND o1) 3959 { 3960 asm_shift_exp(o1); 3961 while (1) 3962 { 3963 switch (asmstate.tokValue) 3964 { 3965 case TOK.greaterThan: 3966 case TOK.greaterOrEqual: 3967 case TOK.lessThan: 3968 case TOK.lessOrEqual: 3969 auto tok_save = asmstate.tokValue; 3970 asm_token(); 3971 OPND o2; 3972 asm_shift_exp(o2); 3973 if (asm_isint(o1) && asm_isint(o2)) 3974 { 3975 switch (tok_save) 3976 { 3977 case TOK.greaterThan: 3978 o1.disp = o1.disp > o2.disp; 3979 break; 3980 case TOK.greaterOrEqual: 3981 o1.disp = o1.disp >= o2.disp; 3982 break; 3983 case TOK.lessThan: 3984 o1.disp = o1.disp < o2.disp; 3985 break; 3986 case TOK.lessOrEqual: 3987 o1.disp = o1.disp <= o2.disp; 3988 break; 3989 default: 3990 assert(0); 3991 } 3992 } 3993 else 3994 asmerr("bad integral operand"); 3995 o2.disp = 0; 3996 asm_merge_opnds(o1, o2); 3997 break; 3998 3999 default: 4000 return; 4001 } 4002 } 4003 } 4004 4005 /******************************* 4006 */ 4007 4008 void asm_shift_exp(out OPND o1) 4009 { 4010 asm_add_exp(o1); 4011 while (asmstate.tokValue == TOK.leftShift || asmstate.tokValue == TOK.rightShift || asmstate.tokValue == TOK.unsignedRightShift) 4012 { 4013 auto tk = asmstate.tokValue; 4014 asm_token(); 4015 OPND o2; 4016 asm_add_exp(o2); 4017 if (asm_isint(o1) && asm_isint(o2)) 4018 { 4019 if (tk == TOK.leftShift) 4020 o1.disp <<= o2.disp; 4021 else if (tk == TOK.unsignedRightShift) 4022 o1.disp = cast(uint)o1.disp >> o2.disp; 4023 else 4024 o1.disp >>= o2.disp; 4025 } 4026 else 4027 asmerr("bad integral operand"); 4028 o2.disp = 0; 4029 asm_merge_opnds(o1, o2); 4030 } 4031 } 4032 4033 /******************************* 4034 */ 4035 4036 void asm_add_exp(out OPND o1) 4037 { 4038 asm_mul_exp(o1); 4039 while (1) 4040 { 4041 switch (asmstate.tokValue) 4042 { 4043 case TOK.add: 4044 { 4045 asm_token(); 4046 OPND o2; 4047 asm_mul_exp(o2); 4048 asm_merge_opnds(o1, o2); 4049 break; 4050 } 4051 4052 case TOK.min: 4053 { 4054 asm_token(); 4055 OPND o2; 4056 asm_mul_exp(o2); 4057 if (o2.base || o2.pregDisp1 || o2.pregDisp2) 4058 asmerr("cannot subtract register"); 4059 if (asm_isint(o1) && asm_isint(o2)) 4060 { 4061 o1.disp -= o2.disp; 4062 o2.disp = 0; 4063 } 4064 else 4065 o2.disp = - o2.disp; 4066 asm_merge_opnds(o1, o2); 4067 break; 4068 } 4069 4070 default: 4071 return; 4072 } 4073 } 4074 } 4075 4076 /******************************* 4077 */ 4078 4079 void asm_mul_exp(out OPND o1) 4080 { 4081 //printf("+asm_mul_exp()\n"); 4082 asm_br_exp(o1); 4083 while (1) 4084 { 4085 switch (asmstate.tokValue) 4086 { 4087 case TOK.mul: 4088 { 4089 asm_token(); 4090 OPND o2; 4091 asm_br_exp(o2); 4092 debug (EXTRA_DEBUG) printf("Star o1.isint=%d, o2.isint=%d, lbra_seen=%d\n", 4093 asm_isint(o1), asm_isint(o2), asmstate.lbracketNestCount ); 4094 if (asm_isNonZeroInt(o1) && asm_isNonZeroInt(o2)) 4095 o1.disp *= o2.disp; 4096 else if (asmstate.lbracketNestCount && o1.pregDisp1 && asm_isNonZeroInt(o2)) 4097 { 4098 o1.uchMultiplier = cast(uint)o2.disp; 4099 debug (EXTRA_DEBUG) printf("Multiplier: %d\n", o1.uchMultiplier); 4100 } 4101 else if (asmstate.lbracketNestCount && o2.pregDisp1 && asm_isNonZeroInt(o1)) 4102 { 4103 OPND popndTmp = o2; 4104 o2 = o1; 4105 o1 = popndTmp; 4106 o1.uchMultiplier = cast(uint)o2.disp; 4107 debug (EXTRA_DEBUG) printf("Multiplier: %d\n", 4108 o1.uchMultiplier); 4109 } 4110 else if (asm_isint(o1) && asm_isint(o2)) 4111 o1.disp *= o2.disp; 4112 else 4113 asmerr("bad operand"); 4114 o2.disp = 0; 4115 asm_merge_opnds(o1, o2); 4116 break; 4117 } 4118 4119 case TOK.div: 4120 { 4121 asm_token(); 4122 OPND o2; 4123 asm_br_exp(o2); 4124 if (asm_isint(o1) && asm_isint(o2)) 4125 o1.disp /= o2.disp; 4126 else 4127 asmerr("bad integral operand"); 4128 o2.disp = 0; 4129 asm_merge_opnds(o1, o2); 4130 break; 4131 } 4132 4133 case TOK.mod: 4134 { 4135 asm_token(); 4136 OPND o2; 4137 asm_br_exp(o2); 4138 if (asm_isint(o1) && asm_isint(o2)) 4139 o1.disp %= o2.disp; 4140 else 4141 asmerr("bad integral operand"); 4142 o2.disp = 0; 4143 asm_merge_opnds(o1, o2); 4144 break; 4145 } 4146 4147 default: 4148 return; 4149 } 4150 } 4151 } 4152 4153 /******************************* 4154 */ 4155 4156 void asm_br_exp(out OPND o1) 4157 { 4158 //printf("asm_br_exp()\n"); 4159 if (asmstate.tokValue != TOK.leftBracket) 4160 asm_una_exp(o1); 4161 while (1) 4162 { 4163 switch (asmstate.tokValue) 4164 { 4165 case TOK.leftBracket: 4166 { 4167 debug (EXTRA_DEBUG) printf("Saw a left bracket\n"); 4168 asm_token(); 4169 asmstate.lbracketNestCount++; 4170 OPND o2; 4171 asm_cond_exp(o2); 4172 asmstate.lbracketNestCount--; 4173 asm_chktok(TOK.rightBracket,"`]` expected instead of `%s`"); 4174 debug (EXTRA_DEBUG) printf("Saw a right bracket\n"); 4175 asm_merge_opnds(o1, o2); 4176 if (asmstate.tokValue == TOK.identifier) 4177 { 4178 asm_una_exp(o2); 4179 asm_merge_opnds(o1, o2); 4180 } 4181 break; 4182 } 4183 default: 4184 return; 4185 } 4186 } 4187 } 4188 4189 /******************************* 4190 */ 4191 4192 void asm_una_exp(ref OPND o1) 4193 { 4194 Type ptype; 4195 4196 static void type_ref(ref OPND o1, Type ptype) 4197 { 4198 asm_token(); 4199 // try: <BasicType>.<min/max etc> 4200 if (asmstate.tokValue == TOK.dot) 4201 { 4202 asm_token(); 4203 if (asmstate.tokValue == TOK.identifier) 4204 { 4205 TypeExp te = new TypeExp(asmstate.loc, ptype); 4206 DotIdExp did = new DotIdExp(asmstate.loc, te, asmstate.tok.ident); 4207 Dsymbol s; 4208 tryExpressionToOperand(did, o1, s); 4209 } 4210 else 4211 { 4212 asmerr("property of basic type `%s` expected", ptype.toChars()); 4213 } 4214 asm_token(); 4215 return; 4216 } 4217 // else: ptr <BasicType> 4218 asm_chktok(cast(TOK) ASMTKptr, "ptr expected"); 4219 asm_cond_exp(o1); 4220 o1.ptype = ptype; 4221 o1.bPtr = true; 4222 } 4223 4224 static void jump_ref(ref OPND o1, ASM_JUMPTYPE ajt, bool readPtr) 4225 { 4226 if (readPtr) 4227 { 4228 asm_token(); 4229 asm_chktok(cast(TOK) ASMTKptr, "ptr expected".ptr); 4230 } 4231 asm_cond_exp(o1); 4232 o1.ajt = ajt; 4233 } 4234 4235 switch (cast(int)asmstate.tokValue) 4236 { 4237 case TOK.add: 4238 asm_token(); 4239 asm_una_exp(o1); 4240 break; 4241 4242 case TOK.min: 4243 asm_token(); 4244 asm_una_exp(o1); 4245 if (o1.base || o1.pregDisp1 || o1.pregDisp2) 4246 asmerr("cannot negate register"); 4247 if (asm_isint(o1)) 4248 o1.disp = -o1.disp; 4249 break; 4250 4251 case TOK.not: 4252 asm_token(); 4253 asm_una_exp(o1); 4254 if (asm_isint(o1)) 4255 o1.disp = !o1.disp; 4256 break; 4257 4258 case TOK.tilde: 4259 asm_token(); 4260 asm_una_exp(o1); 4261 if (asm_isint(o1)) 4262 o1.disp = ~o1.disp; 4263 break; 4264 4265 version (none) 4266 { 4267 case TOK.leftParentheses: 4268 // stoken() is called directly here because we really 4269 // want the INT token to be an INT. 4270 stoken(); 4271 if (type_specifier(&ptypeSpec)) /* if type_name */ 4272 { 4273 4274 ptype = declar_abstract(ptypeSpec); 4275 /* read abstract_declarator */ 4276 fixdeclar(ptype);/* fix declarator */ 4277 type_free(ptypeSpec);/* the declar() function 4278 allocates the typespec again */ 4279 chktok(TOK.rightParentheses,"`)` expected instead of `%s`"); 4280 ptype.Tcount--; 4281 goto CAST_REF; 4282 } 4283 else 4284 { 4285 type_free(ptypeSpec); 4286 asm_cond_exp(o1); 4287 chktok(TOK.rightParentheses, "`)` expected instead of `%s`"); 4288 } 4289 break; 4290 } 4291 4292 case TOK.identifier: 4293 // Check for offset keyword 4294 if (asmstate.tok.ident == Id.offset) 4295 { 4296 asmerr("use offsetof instead of offset"); 4297 goto Loffset; 4298 } 4299 if (asmstate.tok.ident == Id.offsetof) 4300 { 4301 Loffset: 4302 asm_token(); 4303 asm_cond_exp(o1); 4304 o1.bOffset = true; 4305 } 4306 else 4307 asm_primary_exp(o1); 4308 break; 4309 4310 case ASMTKseg: 4311 asm_token(); 4312 asm_cond_exp(o1); 4313 o1.bSeg = true; 4314 break; 4315 4316 case TOK.int16: 4317 if (asmstate.ucItype != ITjump) 4318 { 4319 return type_ref(o1, Type.tint16); 4320 } 4321 asm_token(); 4322 return jump_ref(o1, ASM_JUMPTYPE_SHORT, false); 4323 4324 case ASMTKnear: 4325 return jump_ref(o1, ASM_JUMPTYPE_NEAR, true); 4326 4327 case ASMTKfar: 4328 return jump_ref(o1, ASM_JUMPTYPE_FAR, true); 4329 4330 case TOK.void_: 4331 return type_ref(o1, Type.tvoid); 4332 4333 case TOK.bool_: 4334 return type_ref(o1, Type.tbool); 4335 4336 case TOK.char_: 4337 return type_ref(o1, Type.tchar); 4338 case TOK.wchar_: 4339 return type_ref(o1, Type.twchar); 4340 case TOK.dchar_: 4341 return type_ref(o1, Type.tdchar); 4342 case TOK.uns8: 4343 return type_ref(o1, Type.tuns8); 4344 case TOK.uns16: 4345 return type_ref(o1, Type.tuns16); 4346 case TOK.uns32: 4347 return type_ref(o1, Type.tuns32); 4348 case TOK.uns64 : 4349 return type_ref(o1, Type.tuns64); 4350 4351 case TOK.int8: 4352 return type_ref(o1, Type.tint8); 4353 case ASMTKword: 4354 return type_ref(o1, Type.tint16); 4355 case TOK.int32: 4356 case ASMTKdword: 4357 return type_ref(o1, Type.tint32); 4358 case TOK.int64: 4359 case ASMTKqword: 4360 return type_ref(o1, Type.tint64); 4361 4362 case TOK.float32: 4363 return type_ref(o1, Type.tfloat32); 4364 case TOK.float64: 4365 return type_ref(o1, Type.tfloat64); 4366 case TOK.float80: 4367 return type_ref(o1, Type.tfloat80); 4368 4369 default: 4370 asm_primary_exp(o1); 4371 break; 4372 } 4373 } 4374 4375 /******************************* 4376 */ 4377 4378 void asm_primary_exp(out OPND o1) 4379 { 4380 switch (asmstate.tokValue) 4381 { 4382 case TOK.dollar: 4383 o1.s = asmstate.psDollar; 4384 asm_token(); 4385 break; 4386 4387 case TOK.this_: 4388 case TOK.identifier: 4389 const regp = asm_reg_lookup(asmstate.tok.ident.toString()); 4390 if (regp != null) 4391 { 4392 asm_token(); 4393 // see if it is segment override (like SS:) 4394 if (!asmstate.lbracketNestCount && 4395 (regp.ty & _seg) && 4396 asmstate.tokValue == TOK.colon) 4397 { 4398 o1.segreg = regp; 4399 asm_token(); 4400 OPND o2; 4401 asm_cond_exp(o2); 4402 if (o2.s && o2.s.isLabel()) 4403 o2.segreg = null; // The segment register was specified explicitly. 4404 asm_merge_opnds(o1, o2); 4405 } 4406 else if (asmstate.lbracketNestCount) 4407 { 4408 // should be a register 4409 if (regp.val == _RIP) 4410 o1.bRIP = true; 4411 else if (o1.pregDisp1) 4412 asmerr("bad operand"); 4413 else 4414 o1.pregDisp1 = regp; 4415 } 4416 else 4417 { 4418 if (o1.base == null) 4419 o1.base = regp; 4420 else 4421 asmerr("bad operand"); 4422 } 4423 break; 4424 } 4425 // If floating point instruction and id is a floating register 4426 else if (asmstate.ucItype == ITfloat && 4427 asm_is_fpreg(asmstate.tok.ident.toString())) 4428 { 4429 asm_token(); 4430 if (asmstate.tokValue == TOK.leftParentheses) 4431 { 4432 asm_token(); 4433 if (asmstate.tokValue == TOK.int32Literal) 4434 { 4435 uint n = cast(uint)asmstate.tok.unsvalue; 4436 if (n > 7) 4437 asmerr("bad operand"); 4438 else 4439 o1.base = &(aregFp[n]); 4440 } 4441 asm_chktok(TOK.int32Literal, "integer expected"); 4442 asm_chktok(TOK.rightParentheses, "`)` expected instead of `%s`"); 4443 } 4444 else 4445 o1.base = ®Fp; 4446 } 4447 else 4448 { 4449 Dsymbol s; 4450 if (asmstate.sc.func.labtab) 4451 s = asmstate.sc.func.labtab.lookup(asmstate.tok.ident); 4452 if (!s) 4453 s = asmstate.sc.search(Loc.initial, asmstate.tok.ident, null); 4454 if (!s) 4455 { 4456 // Assume it is a label, and define that label 4457 s = asmstate.sc.func.searchLabel(asmstate.tok.ident); 4458 } 4459 if (auto label = s.isLabel()) 4460 { 4461 // Use the following for non-FLAT memory models 4462 //o1.segreg = ®tab[25]; // use CS as a base for a label 4463 4464 label.iasm = true; 4465 } 4466 Identifier id = asmstate.tok.ident; 4467 asm_token(); 4468 if (asmstate.tokValue == TOK.dot) 4469 { 4470 Expression e = IdentifierExp.create(asmstate.loc, id); 4471 while (1) 4472 { 4473 asm_token(); 4474 if (asmstate.tokValue == TOK.identifier) 4475 { 4476 e = DotIdExp.create(asmstate.loc, e, asmstate.tok.ident); 4477 asm_token(); 4478 if (asmstate.tokValue != TOK.dot) 4479 break; 4480 } 4481 else 4482 { 4483 asmerr("identifier expected"); 4484 break; 4485 } 4486 } 4487 TOK e2o = tryExpressionToOperand(e, o1, s); 4488 if (e2o == TOK.error) 4489 return; 4490 if (e2o == TOK.const_) 4491 goto Lpost; 4492 } 4493 4494 asm_merge_symbol(o1,s); 4495 4496 /* This attempts to answer the question: is 4497 * char[8] foo; 4498 * of size 1 or size 8? Presume it is 8 if foo 4499 * is the last token of the operand. 4500 * Note that this can be turned on and off by the user by 4501 * adding a constant: 4502 * align(16) uint[4][2] constants = 4503 * [ [0,0,0,0],[0,0,0,0] ]; 4504 * asm { 4505 * movdqa XMM1,constants; // operand treated as size 32 4506 * movdqa XMM1,constants+0; // operand treated as size 16 4507 * } 4508 * This is an inexcusable hack, but can't 4509 * fix it due to backward compatibility. 4510 */ 4511 if (o1.ptype && asmstate.tokValue != TOK.comma && asmstate.tokValue != TOK.endOfFile) 4512 { 4513 // Peel off only one layer of the array 4514 if (o1.ptype.ty == Tsarray) 4515 o1.ptype = o1.ptype.nextOf(); 4516 } 4517 4518 Lpost: 4519 // for [] 4520 //if (asmstate.tokValue == TOK.leftBracket) 4521 //o1 = asm_prim_post(o1); 4522 return; 4523 } 4524 break; 4525 4526 case TOK.int32Literal: 4527 o1.disp = cast(d_int32)asmstate.tok.intvalue; 4528 asm_token(); 4529 break; 4530 4531 case TOK.uns32Literal: 4532 o1.disp = cast(d_uns32)asmstate.tok.unsvalue; 4533 asm_token(); 4534 break; 4535 4536 case TOK.int64Literal: 4537 case TOK.uns64Literal: 4538 o1.disp = asmstate.tok.intvalue; 4539 asm_token(); 4540 break; 4541 4542 case TOK.float32Literal: 4543 o1.vreal = asmstate.tok.floatvalue; 4544 o1.ptype = Type.tfloat32; 4545 asm_token(); 4546 break; 4547 4548 case TOK.float64Literal: 4549 o1.vreal = asmstate.tok.floatvalue; 4550 o1.ptype = Type.tfloat64; 4551 asm_token(); 4552 break; 4553 4554 case TOK.float80Literal: 4555 o1.vreal = asmstate.tok.floatvalue; 4556 o1.ptype = Type.tfloat80; 4557 asm_token(); 4558 break; 4559 4560 case cast(TOK)ASMTKlocalsize: 4561 o1.s = asmstate.psLocalsize; 4562 o1.ptype = Type.tint32; 4563 asm_token(); 4564 break; 4565 4566 default: 4567 asmerr("expression expected not `%s`", asmstate.tok ? asmstate.tok.toChars() : ";"); 4568 break; 4569 } 4570 } 4571 4572 /** 4573 * Using an expression, try to set an ASM operand as a constant or as an access 4574 * to a higher level variable. 4575 * 4576 * Params: 4577 * e = Input. The expression to evaluate. This can be an arbitrarily complex expression 4578 * but it must either represent a constant after CTFE or give a higher level variable. 4579 * o1 = if `e` turns out to be a constant, `o1` is set to reflect that 4580 * s = if `e` turns out to be a variable, `s` is set to reflect that 4581 * 4582 * Returns: 4583 * `TOK.variable` if `s` was set to a variable, 4584 * `TOK.const_` if `e` was evaluated to a valid constant, 4585 * `TOK.error` otherwise. 4586 */ 4587 TOK tryExpressionToOperand(Expression e, out OPND o1, out Dsymbol s) 4588 { 4589 Scope *sc = asmstate.sc.startCTFE(); 4590 e = e.expressionSemantic(sc); 4591 sc.endCTFE(); 4592 e = e.ctfeInterpret(); 4593 if (auto ve = e.isVarExp()) 4594 { 4595 s = ve.var; 4596 return TOK.variable; 4597 } 4598 if (e.isConst()) 4599 { 4600 if (e.type.isintegral()) 4601 { 4602 o1.disp = e.toInteger(); 4603 return TOK.const_; 4604 } 4605 if (e.type.isreal()) 4606 { 4607 o1.vreal = e.toReal(); 4608 o1.ptype = e.type; 4609 return TOK.const_; 4610 } 4611 } 4612 asmerr("bad type/size of operands `%s`", e.toChars()); 4613 return TOK.error; 4614 } 4615 4616 /********************** 4617 * If c is a power of 2, return that power else -1. 4618 */ 4619 4620 private int ispow2(uint c) 4621 { 4622 int i; 4623 4624 if (c == 0 || (c & (c - 1))) 4625 i = -1; 4626 else 4627 for (i = 0; c >>= 1; ++i) 4628 { } 4629 return i; 4630 } 4631 4632 4633 /************************************* 4634 * Returns: true if szop is one of the values in sztbl 4635 */ 4636 private 4637 bool isOneOf(OpndSize szop, OpndSize sztbl) 4638 { 4639 with (OpndSize) 4640 { 4641 immutable ubyte[OpndSize.max + 1] maskx = 4642 [ 4643 none : 0, 4644 4645 _8 : 1, 4646 _16 : 2, 4647 _32 : 4, 4648 _48 : 8, 4649 _64 : 16, 4650 _128 : 32, 4651 4652 _16_8 : 2 | 1, 4653 _32_8 : 4 | 1, 4654 _32_16 : 4 | 2, 4655 _32_16_8 : 4 | 2 | 1, 4656 _48_32 : 8 | 4, 4657 _48_32_16_8 : 8 | 4 | 2 | 1, 4658 _64_32 : 16 | 4, 4659 _64_32_8 : 16 | 4 | 1, 4660 _64_32_16 : 16 | 4 | 2, 4661 _64_32_16_8 : 16 | 4 | 2 | 1, 4662 _64_48_32_16_8 : 16 | 8 | 4 | 2 | 1, 4663 4664 _anysize : 32 | 16 | 8 | 4 | 2 | 1, 4665 ]; 4666 4667 return (maskx[szop] & maskx[sztbl]) != 0; 4668 } 4669 } 4670 4671 unittest 4672 { 4673 with (OpndSize) 4674 { 4675 assert( isOneOf(_8, _8)); 4676 assert(!isOneOf(_8, _16)); 4677 assert( isOneOf(_8, _16_8)); 4678 assert( isOneOf(_8, _32_8)); 4679 assert(!isOneOf(_8, _32_16)); 4680 assert( isOneOf(_8, _32_16_8)); 4681 assert(!isOneOf(_8, _64_32)); 4682 assert( isOneOf(_8, _64_32_8)); 4683 assert(!isOneOf(_8, _64_32_16)); 4684 assert( isOneOf(_8, _64_32_16_8)); 4685 assert( isOneOf(_8, _anysize)); 4686 } 4687 }