|
|
1.1 ! root 1: /* Subroutines for assembler code output on the NS32000. ! 2: Copyright (C) 1988 Free Software Foundation, Inc. ! 3: ! 4: This file is part of GNU CC. ! 5: ! 6: GNU CC is free software; you can redistribute it and/or modify ! 7: it under the terms of the GNU General Public License as published by ! 8: the Free Software Foundation; either version 2, or (at your option) ! 9: any later version. ! 10: ! 11: GNU CC is distributed in the hope that it will be useful, ! 12: but WITHOUT ANY WARRANTY; without even the implied warranty of ! 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ! 14: GNU General Public License for more details. ! 15: ! 16: You should have received a copy of the GNU General Public License ! 17: along with GNU CC; see the file COPYING. If not, write to ! 18: the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ ! 19: ! 20: /* Some output-actions in ns32k.md need these. */ ! 21: #include <stdio.h> ! 22: #include "config.h" ! 23: #include "rtl.h" ! 24: #include "regs.h" ! 25: #include "hard-reg-set.h" ! 26: #include "real.h" ! 27: #include "insn-config.h" ! 28: #include "conditions.h" ! 29: #include "insn-flags.h" ! 30: #include "output.h" ! 31: #include "insn-attr.h" ! 32: ! 33: #ifdef OSF_OS ! 34: int ns32k_num_files = 0; ! 35: #endif ! 36: ! 37: void ! 38: trace (s, s1, s2) ! 39: char *s, *s1, *s2; ! 40: { ! 41: fprintf (stderr, s, s1, s2); ! 42: } ! 43: ! 44: /* Value is 1 if hard register REGNO can hold a value of machine-mode MODE. */ ! 45: ! 46: int ! 47: hard_regno_mode_ok (regno, mode) ! 48: int regno; ! 49: enum machine_mode mode; ! 50: { ! 51: switch (mode) ! 52: { ! 53: case QImode: ! 54: case HImode: ! 55: case PSImode: ! 56: case SImode: ! 57: case PDImode: ! 58: case VOIDmode: ! 59: case BLKmode: ! 60: if (regno < 8 || regno == 16 || regno == 17) ! 61: return 1; ! 62: else ! 63: return 0; ! 64: ! 65: case DImode: ! 66: if (regno < 8 && (regno & 1) == 0) ! 67: return 1; ! 68: else ! 69: return 0; ! 70: ! 71: case SFmode: ! 72: case SCmode: ! 73: if (TARGET_32081) ! 74: { ! 75: if (regno < 16) ! 76: return 1; ! 77: else ! 78: return 0; ! 79: } ! 80: else ! 81: { ! 82: if (regno < 8) ! 83: return 1; ! 84: else ! 85: return 0; ! 86: } ! 87: ! 88: case DFmode: ! 89: case DCmode: ! 90: if ((regno & 1) == 0) ! 91: { ! 92: if (TARGET_32081) ! 93: { ! 94: if (regno < 16) ! 95: return 1; ! 96: else ! 97: return 0; ! 98: } ! 99: else ! 100: { ! 101: if (regno < 8) ! 102: return 1; ! 103: else ! 104: return 0; ! 105: } ! 106: } ! 107: else ! 108: return 0; ! 109: } ! 110: ! 111: /* Used to abort here, but simply saying "no" handles TImode ! 112: much better. */ ! 113: return 0; ! 114: } ! 115: ! 116: /* ADDRESS_COST calls this. This function is not optimal ! 117: for the 32032 & 32332, but it probably is better than ! 118: the default. */ ! 119: ! 120: int ! 121: calc_address_cost (operand) ! 122: rtx operand; ! 123: { ! 124: int i; ! 125: int cost = 0; ! 126: ! 127: if (GET_CODE (operand) == MEM) ! 128: cost += 3; ! 129: if (GET_CODE (operand) == MULT) ! 130: cost += 2; ! 131: #if 0 ! 132: if (GET_CODE (operand) == REG) ! 133: cost += 1; /* not really, but the documentation ! 134: says different amount of registers ! 135: shouldn't return the same costs */ ! 136: #endif ! 137: switch (GET_CODE (operand)) ! 138: { ! 139: case REG: ! 140: case CONST: ! 141: case CONST_INT: ! 142: case CONST_DOUBLE: ! 143: case SYMBOL_REF: ! 144: case LABEL_REF: ! 145: case POST_DEC: ! 146: case PRE_DEC: ! 147: break; ! 148: case MULT: ! 149: case MEM: ! 150: case PLUS: ! 151: for (i = 0; i < GET_RTX_LENGTH (GET_CODE (operand)); i++) ! 152: { ! 153: cost += calc_address_cost (XEXP (operand, i)); ! 154: } ! 155: default: ! 156: break; ! 157: } ! 158: return cost; ! 159: } ! 160: ! 161: /* Return the register class of a scratch register needed to copy IN into ! 162: or out of a register in CLASS in MODE. If it can be done directly, ! 163: NO_REGS is returned. */ ! 164: ! 165: enum reg_class ! 166: secondary_reload_class (class, mode, in) ! 167: enum reg_class class; ! 168: enum machine_mode mode; ! 169: rtx in; ! 170: { ! 171: int regno = true_regnum (in); ! 172: ! 173: if (regno >= FIRST_PSEUDO_REGISTER) ! 174: regno = -1; ! 175: ! 176: /* We can place anything into GENERAL_REGS and can put GENERAL_REGS ! 177: into anything. */ ! 178: if (class == GENERAL_REGS || (regno >= 0 && regno < 8)) ! 179: return NO_REGS; ! 180: ! 181: /* Constants, memory, and FP registers can go into FP registers. */ ! 182: if ((regno == -1 || (regno >= 8 && regno < 16)) && (class == FLOAT_REGS)) ! 183: return NO_REGS; ! 184: ! 185: #if 0 /* This isn't strictly true (can't move fp to sp or vice versa), ! 186: so it's cleaner to use PREFERRED_RELOAD_CLASS ! 187: to make the right things happen. */ ! 188: if (regno >= 16 && class == GEN_AND_MEM_REGS) ! 189: return NO_REGS; ! 190: #endif ! 191: ! 192: /* Otherwise, we need GENERAL_REGS. */ ! 193: return GENERAL_REGS; ! 194: } ! 195: /* Generate the rtx that comes from an address expression in the md file */ ! 196: /* The expression to be build is BASE[INDEX:SCALE]. To recognize this, ! 197: scale must be converted from an exponent (from ASHIFT) to a ! 198: multiplier (for MULT). */ ! 199: rtx ! 200: gen_indexed_expr (base, index, scale) ! 201: rtx base, index, scale; ! 202: { ! 203: rtx addr; ! 204: ! 205: /* This generates an illegal addressing mode, if BASE is ! 206: fp or sp. This is handled by PRINT_OPERAND_ADDRESS. */ ! 207: if (GET_CODE (base) != REG && GET_CODE (base) != CONST_INT) ! 208: base = gen_rtx (MEM, SImode, base); ! 209: addr = gen_rtx (MULT, SImode, index, ! 210: gen_rtx (CONST_INT, VOIDmode, 1 << INTVAL (scale))); ! 211: addr = gen_rtx (PLUS, SImode, base, addr); ! 212: return addr; ! 213: } ! 214: ! 215: /* Return 1 if OP is a valid operand of mode MODE. This ! 216: predicate rejects operands which do not have a mode ! 217: (such as CONST_INT which are VOIDmode). */ ! 218: int ! 219: reg_or_mem_operand (op, mode) ! 220: register rtx op; ! 221: enum machine_mode mode; ! 222: { ! 223: return (GET_MODE (op) == mode ! 224: && (GET_CODE (op) == REG ! 225: || GET_CODE (op) == SUBREG ! 226: || GET_CODE (op) == MEM)); ! 227: } ! 228: ! 229: /* Return the best assembler insn template ! 230: for moving operands[1] into operands[0] as a fullword. */ ! 231: ! 232: static char * ! 233: singlemove_string (operands) ! 234: rtx *operands; ! 235: { ! 236: if (GET_CODE (operands[1]) == CONST_INT ! 237: && INTVAL (operands[1]) <= 7 ! 238: && INTVAL (operands[1]) >= -8) ! 239: return "movqd %1,%0"; ! 240: return "movd %1,%0"; ! 241: } ! 242: ! 243: char * ! 244: output_move_double (operands) ! 245: rtx *operands; ! 246: { ! 247: enum anon1 { REGOP, OFFSOP, PUSHOP, CNSTOP, RNDOP } optype0, optype1; ! 248: rtx latehalf[2]; ! 249: ! 250: /* First classify both operands. */ ! 251: ! 252: if (REG_P (operands[0])) ! 253: optype0 = REGOP; ! 254: else if (offsettable_memref_p (operands[0])) ! 255: optype0 = OFFSOP; ! 256: else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC) ! 257: optype0 = PUSHOP; ! 258: else ! 259: optype0 = RNDOP; ! 260: ! 261: if (REG_P (operands[1])) ! 262: optype1 = REGOP; ! 263: else if (CONSTANT_ADDRESS_P (operands[1]) ! 264: || GET_CODE (operands[1]) == CONST_DOUBLE) ! 265: optype1 = CNSTOP; ! 266: else if (offsettable_memref_p (operands[1])) ! 267: optype1 = OFFSOP; ! 268: else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC) ! 269: optype1 = PUSHOP; ! 270: else ! 271: optype1 = RNDOP; ! 272: ! 273: /* Check for the cases that the operand constraints are not ! 274: supposed to allow to happen. Abort if we get one, ! 275: because generating code for these cases is painful. */ ! 276: ! 277: if (optype0 == RNDOP || optype1 == RNDOP) ! 278: abort (); ! 279: ! 280: /* Ok, we can do one word at a time. ! 281: Normally we do the low-numbered word first, ! 282: but if either operand is autodecrementing then we ! 283: do the high-numbered word first. ! 284: ! 285: In either case, set up in LATEHALF the operands to use ! 286: for the high-numbered word and in some cases alter the ! 287: operands in OPERANDS to be suitable for the low-numbered word. */ ! 288: ! 289: if (optype0 == REGOP) ! 290: latehalf[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1); ! 291: else if (optype0 == OFFSOP) ! 292: latehalf[0] = adj_offsettable_operand (operands[0], 4); ! 293: else ! 294: latehalf[0] = operands[0]; ! 295: ! 296: if (optype1 == REGOP) ! 297: latehalf[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1); ! 298: else if (optype1 == OFFSOP) ! 299: latehalf[1] = adj_offsettable_operand (operands[1], 4); ! 300: else if (optype1 == CNSTOP) ! 301: { ! 302: if (CONSTANT_ADDRESS_P (operands[1])) ! 303: latehalf[1] = const0_rtx; ! 304: else if (GET_CODE (operands[1]) == CONST_DOUBLE) ! 305: split_double (operands[1], &operands[1], &latehalf[1]); ! 306: } ! 307: else ! 308: latehalf[1] = operands[1]; ! 309: ! 310: /* If insn is effectively movd N(sp),tos then we will do the ! 311: high word first. We should use the adjusted operand 1 (which is N+4(sp)) ! 312: for the low word as well, to compensate for the first decrement of sp. ! 313: Given this, it doesn't matter which half we do "first". */ ! 314: if (optype0 == PUSHOP ! 315: && REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM ! 316: && reg_overlap_mentioned_p (stack_pointer_rtx, operands[1])) ! 317: operands[1] = latehalf[1]; ! 318: ! 319: /* If one or both operands autodecrementing, ! 320: do the two words, high-numbered first. */ ! 321: else if (optype0 == PUSHOP || optype1 == PUSHOP) ! 322: { ! 323: output_asm_insn (singlemove_string (latehalf), latehalf); ! 324: return singlemove_string (operands); ! 325: } ! 326: ! 327: /* If the first move would clobber the source of the second one, ! 328: do them in the other order. */ ! 329: ! 330: /* Overlapping registers. */ ! 331: if (optype0 == REGOP && optype1 == REGOP ! 332: && REGNO (operands[0]) == REGNO (latehalf[1])) ! 333: { ! 334: /* Do that word. */ ! 335: output_asm_insn (singlemove_string (latehalf), latehalf); ! 336: /* Do low-numbered word. */ ! 337: return singlemove_string (operands); ! 338: } ! 339: /* Loading into a register which overlaps a register used in the address. */ ! 340: else if (optype0 == REGOP && optype1 != REGOP ! 341: && reg_overlap_mentioned_p (operands[0], operands[1])) ! 342: { ! 343: if (reg_mentioned_p (operands[0], XEXP (operands[1], 0)) ! 344: && reg_mentioned_p (latehalf[0], XEXP (operands[1], 0))) ! 345: { ! 346: /* If both halves of dest are used in the src memory address, ! 347: add the two regs and put them in the low reg (operands[0]). ! 348: Then it works to load latehalf first. */ ! 349: rtx xops[2]; ! 350: xops[0] = latehalf[0]; ! 351: xops[1] = operands[0]; ! 352: output_asm_insn ("addd %0,%1", xops); ! 353: operands[1] = gen_rtx (MEM, DImode, operands[0]); ! 354: latehalf[1] = adj_offsettable_operand (operands[1], 4); ! 355: /* The first half has the overlap, Do the late half first. */ ! 356: output_asm_insn (singlemove_string (latehalf), latehalf); ! 357: /* Then clobber. */ ! 358: return singlemove_string (operands); ! 359: } ! 360: if (reg_mentioned_p (operands[0], XEXP (operands[1], 0))) ! 361: { ! 362: /* The first half has the overlap, Do the late half first. */ ! 363: output_asm_insn (singlemove_string (latehalf), latehalf); ! 364: /* Then clobber. */ ! 365: return singlemove_string (operands); ! 366: } ! 367: } ! 368: ! 369: /* Normal case. Do the two words, low-numbered first. */ ! 370: ! 371: output_asm_insn (singlemove_string (operands), operands); ! 372: ! 373: operands[0] = latehalf[0]; ! 374: operands[1] = latehalf[1]; ! 375: return singlemove_string (operands); ! 376: } ! 377: ! 378: int ! 379: check_reg (oper, reg) ! 380: rtx oper; ! 381: int reg; ! 382: { ! 383: register int i; ! 384: ! 385: if (oper == 0) ! 386: return 0; ! 387: switch (GET_CODE(oper)) ! 388: { ! 389: case REG: ! 390: return (REGNO(oper) == reg) ? 1 : 0; ! 391: case MEM: ! 392: return check_reg(XEXP(oper, 0), reg); ! 393: case PLUS: ! 394: case MULT: ! 395: return check_reg(XEXP(oper, 0), reg) || check_reg(XEXP(oper, 1), reg); ! 396: } ! 397: return 0; ! 398: } ! 399: ! 400: /* PRINT_OPERAND is defined to call this function, ! 401: which is easier to debug than putting all the code in ! 402: a macro definition in ns32k.h. */ ! 403: ! 404: void ! 405: print_operand (file, x, code) ! 406: FILE *file; ! 407: rtx x; ! 408: char code; ! 409: { ! 410: if (code == '$') ! 411: PUT_IMMEDIATE_PREFIX (file); ! 412: else if (code == '?') ! 413: PUT_EXTERNAL_PREFIX (file); ! 414: else if (GET_CODE (x) == REG) ! 415: fprintf (file, "%s", reg_names[REGNO (x)]); ! 416: else if (GET_CODE (x) == MEM) ! 417: { ! 418: rtx tmp = XEXP (x, 0); ! 419: #if ! (defined (PC_RELATIVE) || defined (NO_ABSOLUTE_PREFIX_IF_SYMBOLIC)) ! 420: if (GET_CODE (tmp) != CONST_INT) ! 421: { ! 422: char *out = XSTR (tmp, 0); ! 423: if (out[0] == '*') ! 424: { ! 425: PUT_ABSOLUTE_PREFIX (file); ! 426: fprintf (file, "%s", &out[1]); ! 427: } ! 428: else ! 429: ASM_OUTPUT_LABELREF (file, out); ! 430: } ! 431: else ! 432: #endif ! 433: output_address (XEXP (x, 0)); ! 434: } ! 435: else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) != DImode) ! 436: { ! 437: if (GET_MODE (x) == DFmode) ! 438: { ! 439: union { double d; int i[2]; } u; ! 440: u.i[0] = CONST_DOUBLE_LOW (x); u.i[1] = CONST_DOUBLE_HIGH (x); ! 441: PUT_IMMEDIATE_PREFIX(file); ! 442: #ifdef SEQUENT_ASM ! 443: /* Sequent likes it's floating point constants as integers */ ! 444: fprintf (file, "0Dx%08x%08x", u.i[1], u.i[0]); ! 445: #else ! 446: #ifdef ENCORE_ASM ! 447: fprintf (file, "0f%.20e", u.d); ! 448: #else ! 449: fprintf (file, "0d%.20e", u.d); ! 450: #endif ! 451: #endif ! 452: } ! 453: else ! 454: { ! 455: union { double d; int i[2]; } u; ! 456: u.i[0] = CONST_DOUBLE_LOW (x); u.i[1] = CONST_DOUBLE_HIGH (x); ! 457: PUT_IMMEDIATE_PREFIX (file); ! 458: #ifdef SEQUENT_ASM ! 459: /* We have no way of winning if we can't get the bits ! 460: for a sequent floating point number. */ ! 461: #if HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT ! 462: abort (); ! 463: #endif ! 464: { ! 465: union { float f; long l; } uu; ! 466: uu.f = u.d; ! 467: fprintf (file, "0Fx%08x", uu.l); ! 468: } ! 469: #else ! 470: fprintf (file, "0f%.20e", u.d); ! 471: #endif ! 472: } ! 473: } ! 474: else ! 475: { ! 476: #ifdef NO_IMMEDIATE_PREFIX_IF_SYMBOLIC ! 477: if (GET_CODE (x) == CONST_INT) ! 478: #endif ! 479: PUT_IMMEDIATE_PREFIX (file); ! 480: output_addr_const (file, x); ! 481: } ! 482: } ! 483: ! 484: /* PRINT_OPERAND_ADDRESS is defined to call this function, ! 485: which is easier to debug than putting all the code in ! 486: a macro definition in ns32k.h . */ ! 487: ! 488: /* Completely rewritten to get this to work with Gas for PC532 Mach. ! 489: This function didn't work and I just wasn't able (nor very willing) to ! 490: figure out how it worked. ! 491: 90-11-25 Tatu Yl|nen <[email protected]> */ ! 492: ! 493: print_operand_address (file, addr) ! 494: register FILE *file; ! 495: register rtx addr; ! 496: { ! 497: static char scales[] = { 'b', 'w', 'd', 0, 'q', }; ! 498: rtx offset, base, indexexp, tmp; ! 499: int scale; ! 500: ! 501: if (GET_CODE (addr) == PRE_DEC || GET_CODE (addr) == POST_DEC) ! 502: { ! 503: fprintf (file, "tos"); ! 504: return; ! 505: } ! 506: ! 507: offset = NULL; ! 508: base = NULL; ! 509: indexexp = NULL; ! 510: while (addr != NULL) ! 511: { ! 512: if (GET_CODE (addr) == PLUS) ! 513: { ! 514: if (GET_CODE (XEXP (addr, 0)) == PLUS) ! 515: { ! 516: tmp = XEXP (addr, 1); ! 517: addr = XEXP (addr, 0); ! 518: } ! 519: else ! 520: { ! 521: tmp = XEXP (addr,0); ! 522: addr = XEXP (addr,1); ! 523: } ! 524: } ! 525: else ! 526: { ! 527: tmp = addr; ! 528: addr = NULL; ! 529: } ! 530: switch (GET_CODE (tmp)) ! 531: { ! 532: case PLUS: ! 533: abort (); ! 534: case MEM: ! 535: if (base) ! 536: { ! 537: indexexp = base; ! 538: base = tmp; ! 539: } ! 540: else ! 541: base = tmp; ! 542: break; ! 543: case REG: ! 544: if (REGNO (tmp) < 8) ! 545: if (base) ! 546: { ! 547: indexexp = tmp; ! 548: } ! 549: else ! 550: base = tmp; ! 551: else ! 552: if (base) ! 553: { ! 554: indexexp = base; ! 555: base = tmp; ! 556: } ! 557: else ! 558: base = tmp; ! 559: break; ! 560: case MULT: ! 561: indexexp = tmp; ! 562: break; ! 563: case CONST: ! 564: case CONST_INT: ! 565: case SYMBOL_REF: ! 566: case LABEL_REF: ! 567: if (offset) ! 568: offset = gen_rtx (PLUS, SImode, tmp, offset); ! 569: else ! 570: offset = tmp; ! 571: break; ! 572: default: ! 573: abort (); ! 574: } ! 575: } ! 576: if (! offset) ! 577: offset = const0_rtx; ! 578: ! 579: #ifdef INDEX_RATHER_THAN_BASE ! 580: /* This is a re-implementation of the SEQUENT_ADDRESS_BUG fix. */ ! 581: if (base && !indexexp && GET_CODE (base) == REG ! 582: && REG_OK_FOR_INDEX_P (base)) ! 583: { ! 584: indexexp = base; ! 585: base = 0; ! 586: } ! 587: #endif ! 588: ! 589: /* now, offset, base and indexexp are set */ ! 590: if (! base) ! 591: { ! 592: #if defined (PC_RELATIVE) || defined (NO_ABSOLUTE_PREFIX_IF_SYMBOLIC) ! 593: if (GET_CODE (offset) == CONST_INT) ! 594: /* if (! (GET_CODE (offset) == LABEL_REF ! 595: || GET_CODE (offset) == SYMBOL_REF)) */ ! 596: #endif ! 597: PUT_ABSOLUTE_PREFIX (file); ! 598: } ! 599: ! 600: output_addr_const (file, offset); ! 601: if (base) /* base can be (REG ...) or (MEM ...) */ ! 602: switch (GET_CODE (base)) ! 603: { ! 604: /* now we must output base. Possible alternatives are: ! 605: (rN) (REG ...) ! 606: (sp) (REG ...) ! 607: (fp) (REG ...) ! 608: (pc) (REG ...) used for SYMBOL_REF and LABEL_REF, output ! 609: (disp(fp)) (MEM ...) just before possible [rX:y] ! 610: (disp(sp)) (MEM ...) ! 611: (disp(sb)) (MEM ...) ! 612: */ ! 613: case REG: ! 614: fprintf (file, "(%s)", reg_names[REGNO (base)]); ! 615: break; ! 616: case MEM: ! 617: addr = XEXP(base,0); ! 618: base = NULL; ! 619: offset = NULL; ! 620: while (addr != NULL) ! 621: { ! 622: if (GET_CODE (addr) == PLUS) ! 623: { ! 624: if (GET_CODE (XEXP (addr, 0)) == PLUS) ! 625: { ! 626: tmp = XEXP (addr, 1); ! 627: addr = XEXP (addr, 0); ! 628: } ! 629: else ! 630: { ! 631: tmp = XEXP (addr, 0); ! 632: addr = XEXP (addr, 1); ! 633: } ! 634: } ! 635: else ! 636: { ! 637: tmp = addr; ! 638: addr = NULL; ! 639: } ! 640: switch (GET_CODE (tmp)) ! 641: { ! 642: case REG: ! 643: base = tmp; ! 644: break; ! 645: case CONST: ! 646: case CONST_INT: ! 647: case SYMBOL_REF: ! 648: case LABEL_REF: ! 649: if (offset) ! 650: offset = gen_rtx (PLUS, SImode, tmp, offset); ! 651: else ! 652: offset = tmp; ! 653: break; ! 654: default: ! 655: abort (); ! 656: } ! 657: } ! 658: if (! offset) ! 659: offset = const0_rtx; ! 660: fprintf (file, "("); ! 661: output_addr_const (file, offset); ! 662: if (base) ! 663: fprintf (file, "(%s)", reg_names[REGNO (base)]); ! 664: #ifdef BASE_REG_NEEDED ! 665: else if (TARGET_SB) ! 666: fprintf (file, "(sb)"); ! 667: else ! 668: abort (); ! 669: #endif ! 670: fprintf (file, ")"); ! 671: break; ! 672: ! 673: default: ! 674: abort (); ! 675: } ! 676: #ifdef PC_RELATIVE ! 677: else /* no base */ ! 678: if (GET_CODE (offset) == LABEL_REF || GET_CODE (offset) == SYMBOL_REF) ! 679: fprintf (file, "(pc)"); ! 680: #endif ! 681: #ifdef BASE_REG_NEEDED /* this is defined if the assembler always ! 682: needs a base register */ ! 683: else if (TARGET_SB) ! 684: fprintf (file, "(sb)"); ! 685: else ! 686: abort (); ! 687: #endif ! 688: /* now print index if we have one */ ! 689: if (indexexp) ! 690: { ! 691: if (GET_CODE (indexexp) == MULT) ! 692: { ! 693: scale = INTVAL (XEXP (indexexp, 1)) >> 1; ! 694: indexexp = XEXP (indexexp, 0); ! 695: } ! 696: else ! 697: scale = 0; ! 698: if (GET_CODE (indexexp) != REG || REGNO (indexexp) >= 8) ! 699: abort (); ! 700: ! 701: #ifdef UTEK_ASM ! 702: fprintf (file, "[%c`%s]", ! 703: scales[scale], ! 704: reg_names[REGNO (indexexp)]); ! 705: #else ! 706: fprintf (file, "[%s:%c]", ! 707: reg_names[REGNO (indexexp)], ! 708: scales[scale]); ! 709: #endif ! 710: } ! 711: } ! 712: ! 713: /* National 32032 shifting is so bad that we can get ! 714: better performance in many common cases by using other ! 715: techniques. */ ! 716: char * ! 717: output_shift_insn (operands) ! 718: rtx *operands; ! 719: { ! 720: if (GET_CODE (operands[2]) == CONST_INT ! 721: && INTVAL (operands[2]) > 0 ! 722: && INTVAL (operands[2]) <= 3) ! 723: if (GET_CODE (operands[0]) == REG) ! 724: { ! 725: if (GET_CODE (operands[1]) == REG) ! 726: { ! 727: if (REGNO (operands[0]) == REGNO (operands[1])) ! 728: { ! 729: if (operands[2] == const1_rtx) ! 730: return "addd %0,%0"; ! 731: else if (INTVAL (operands[2]) == 2) ! 732: return "addd %0,%0\n\taddd %0,%0"; ! 733: } ! 734: if (operands[2] == const1_rtx) ! 735: return "movd %1,%0\n\taddd %0,%0"; ! 736: ! 737: operands[1] = gen_indexed_expr (const0_rtx, operands[1], operands[2]); ! 738: return "addr %a1,%0"; ! 739: } ! 740: if (operands[2] == const1_rtx) ! 741: return "movd %1,%0\n\taddd %0,%0"; ! 742: } ! 743: else if (GET_CODE (operands[1]) == REG) ! 744: { ! 745: operands[1] = gen_indexed_expr (const0_rtx, operands[1], operands[2]); ! 746: return "addr %a1,%0"; ! 747: } ! 748: else if (INTVAL (operands[2]) == 1 ! 749: && GET_CODE (operands[1]) == MEM ! 750: && rtx_equal_p (operands [0], operands[1])) ! 751: { ! 752: rtx temp = XEXP (operands[1], 0); ! 753: ! 754: if (GET_CODE (temp) == REG ! 755: || (GET_CODE (temp) == PLUS ! 756: && GET_CODE (XEXP (temp, 0)) == REG ! 757: && GET_CODE (XEXP (temp, 1)) == CONST_INT)) ! 758: return "addd %0,%0"; ! 759: } ! 760: else return "ashd %2,%0"; ! 761: return "ashd %2,%0"; ! 762: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.