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