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