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