|
|
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: int mode; ! 50: { ! 51: switch( mode ) { ! 52: case QImode: ! 53: case HImode: ! 54: case PSImode: ! 55: case SImode: ! 56: case PDImode: ! 57: case VOIDmode: ! 58: case BLKmode: ! 59: if( (regno < 8) || (regno == 16) || (regno == 17) ) { ! 60: return( 1 ); ! 61: } ! 62: else { ! 63: return( 0 ); ! 64: } ! 65: ! 66: case DImode: ! 67: if( (regno < 8) && ((regno & 1) == 0) ) { ! 68: return( 1 ); ! 69: } ! 70: else { ! 71: return( 0 ); ! 72: } ! 73: ! 74: ! 75: case SFmode: ! 76: case SCmode: ! 77: if( TARGET_32081 ) { ! 78: if( regno < 16 ) { ! 79: return( 1 ); ! 80: } ! 81: else { ! 82: return( 0 ); ! 83: } ! 84: } ! 85: else { ! 86: if( regno < 8 ) { ! 87: return( 1 ); ! 88: } ! 89: else { ! 90: return( 0 ); ! 91: } ! 92: } ! 93: ! 94: case DFmode: ! 95: case DCmode: ! 96: if( (regno & 1) == 0 ) { ! 97: if( TARGET_32081 ) { ! 98: if( regno < 16 ) { ! 99: return( 1 ); ! 100: } ! 101: else { ! 102: return( 0 ); ! 103: } ! 104: } ! 105: else { ! 106: if( regno < 8 ) { ! 107: return( 1 ); ! 108: } ! 109: else { ! 110: return( 0 ); ! 111: } ! 112: } ! 113: } ! 114: else { ! 115: return( 0 ); ! 116: } ! 117: ! 118: case XFmode: ! 119: abort( 0 ); ! 120: case CCmode: ! 121: abort( 0 ); ! 122: case TImode: ! 123: abort( 0 ); ! 124: case XCmode: ! 125: abort( 0 ); ! 126: case TFmode: ! 127: abort( 0 ); ! 128: case TCmode: ! 129: abort( 0 ); ! 130: ! 131: ! 132: default: ! 133: fprintf( stderr, "cant match mode %d\n", mode ); ! 134: abort( 0 ); ! 135: } ! 136: abort(0); ! 137: } ! 138: ! 139: /* ADDRESS_COST calls this. This function is not optimal ! 140: for the 32032 & 32332, but it probably is better than ! 141: the default. */ ! 142: ! 143: int ! 144: calc_address_cost (operand) ! 145: rtx operand; ! 146: { ! 147: int i; ! 148: int cost = 0; ! 149: ! 150: if (GET_CODE (operand) == MEM) ! 151: cost += 3; ! 152: if (GET_CODE (operand) == MULT) ! 153: cost += 2; ! 154: #if 0 ! 155: if (GET_CODE (operand) == REG) ! 156: cost += 1; /* not really, but the documentation ! 157: says different amount of registers ! 158: shouldn't return the same costs */ ! 159: #endif ! 160: switch (GET_CODE (operand)) ! 161: { ! 162: case REG: ! 163: case CONST: ! 164: case CONST_INT: ! 165: case CONST_DOUBLE: ! 166: case SYMBOL_REF: ! 167: case LABEL_REF: ! 168: case POST_DEC: ! 169: case PRE_DEC: ! 170: break; ! 171: case MULT: ! 172: case MEM: ! 173: case PLUS: ! 174: for (i = 0; i < GET_RTX_LENGTH (GET_CODE (operand)); i++) ! 175: { ! 176: cost += calc_address_cost (XEXP (operand, i)); ! 177: } ! 178: default: ! 179: break; ! 180: } ! 181: return cost; ! 182: } ! 183: ! 184: /* Return the register class of a scratch register needed to copy IN into ! 185: or out of a register in CLASS in MODE. If it can be done directly, ! 186: NO_REGS is returned. */ ! 187: ! 188: enum reg_class ! 189: secondary_reload_class (class, mode, in) ! 190: enum reg_class class; ! 191: enum machine_mode mode; ! 192: rtx in; ! 193: { ! 194: int regno = true_regnum (in); ! 195: ! 196: if (regno >= FIRST_PSEUDO_REGISTER) ! 197: regno = -1; ! 198: ! 199: /* We can place anything into GENERAL_REGS and can put GENERAL_REGS ! 200: into anything. */ ! 201: if (class == GENERAL_REGS || (regno >= 0 && regno < 8)) ! 202: return NO_REGS; ! 203: ! 204: /* Constants, memory, and FP registers can go into FP registers */ ! 205: if ((regno == -1 || (regno >= 8 && regno < 16)) && (class == FLOAT_REGS)) ! 206: return NO_REGS; ! 207: ! 208: /* Otherwise, we need GENERAL_REGS. */ ! 209: return GENERAL_REGS; ! 210: } ! 211: /* Generate the rtx that comes from an address expression in the md file */ ! 212: /* The expression to be build is BASE[INDEX:SCALE]. To recognize this, ! 213: scale must be converted from an exponent (from ASHIFT) to a ! 214: muliplier (for MULT). */ ! 215: rtx ! 216: gen_indexed_expr (base, index, scale) ! 217: rtx base, index, scale; ! 218: { ! 219: rtx addr; ! 220: ! 221: /* This generates an illegal addressing mode, if BASE is ! 222: fp or sp. This is handled by PRINT_OPERAND_ADDRESS. */ ! 223: if (GET_CODE (base) != REG && GET_CODE (base) != CONST_INT) ! 224: base = gen_rtx (MEM, SImode, base); ! 225: addr = gen_rtx (MULT, SImode, index, ! 226: gen_rtx (CONST_INT, VOIDmode, 1 << INTVAL (scale))); ! 227: addr = gen_rtx (PLUS, SImode, base, addr); ! 228: return addr; ! 229: } ! 230: ! 231: /* Return 1 if OP is a valid operand of mode MODE. This ! 232: predicate rejects operands which do not have a mode ! 233: (such as CONST_INT which are VOIDmode). */ ! 234: int ! 235: reg_or_mem_operand (op, mode) ! 236: register rtx op; ! 237: enum machine_mode mode; ! 238: { ! 239: return (GET_MODE (op) == mode ! 240: && (GET_CODE (op) == REG ! 241: || GET_CODE (op) == SUBREG ! 242: || GET_CODE (op) == MEM)); ! 243: } ! 244: ! 245: /* Return the best assembler insn template ! 246: for moving operands[1] into operands[0] as a fullword. */ ! 247: ! 248: static char * ! 249: singlemove_string (operands) ! 250: rtx *operands; ! 251: { ! 252: if (GET_CODE (operands[1]) == CONST_INT ! 253: && INTVAL (operands[1]) <= 7 ! 254: && INTVAL (operands[1]) >= -8) ! 255: return "movqd %1,%0"; ! 256: return "movd %1,%0"; ! 257: } ! 258: ! 259: char * ! 260: output_move_double (operands) ! 261: rtx *operands; ! 262: { ! 263: enum anon1 { REGOP, OFFSOP, POPOP, CNSTOP, RNDOP } optype0, optype1; ! 264: rtx latehalf[2]; ! 265: ! 266: /* First classify both operands. */ ! 267: ! 268: if (REG_P (operands[0])) ! 269: optype0 = REGOP; ! 270: else if (offsettable_memref_p (operands[0])) ! 271: optype0 = OFFSOP; ! 272: else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC) ! 273: optype0 = POPOP; ! 274: else ! 275: optype0 = RNDOP; ! 276: ! 277: if (REG_P (operands[1])) ! 278: optype1 = REGOP; ! 279: else if (CONSTANT_ADDRESS_P (operands[1]) ! 280: || GET_CODE (operands[1]) == CONST_DOUBLE) ! 281: optype1 = CNSTOP; ! 282: else if (offsettable_memref_p (operands[1])) ! 283: optype1 = OFFSOP; ! 284: else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC) ! 285: optype1 = POPOP; ! 286: else ! 287: optype1 = RNDOP; ! 288: ! 289: /* Check for the cases that the operand constraints are not ! 290: supposed to allow to happen. Abort if we get one, ! 291: because generating code for these cases is painful. */ ! 292: ! 293: if (optype0 == RNDOP || optype1 == RNDOP) ! 294: abort (); ! 295: ! 296: /* Ok, we can do one word at a time. ! 297: Normally we do the low-numbered word first, ! 298: but if either operand is autodecrementing then we ! 299: do the high-numbered word first. ! 300: ! 301: In either case, set up in LATEHALF the operands to use ! 302: for the high-numbered word and in some cases alter the ! 303: operands in OPERANDS to be suitable for the low-numbered word. */ ! 304: ! 305: if (optype0 == REGOP) ! 306: latehalf[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1); ! 307: else if (optype0 == OFFSOP) ! 308: latehalf[0] = adj_offsettable_operand (operands[0], 4); ! 309: else ! 310: latehalf[0] = operands[0]; ! 311: ! 312: if (optype1 == REGOP) ! 313: latehalf[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1); ! 314: else if (optype1 == OFFSOP) ! 315: latehalf[1] = adj_offsettable_operand (operands[1], 4); ! 316: else if (optype1 == CNSTOP) ! 317: { ! 318: if (CONSTANT_ADDRESS_P (operands[1])) ! 319: latehalf[1] = const0_rtx; ! 320: else if (GET_CODE (operands[1]) == CONST_DOUBLE) ! 321: split_double (operands[1], &operands[1], &latehalf[1]); ! 322: } ! 323: else ! 324: latehalf[1] = operands[1]; ! 325: ! 326: /* If one or both operands autodecrementing, ! 327: do the two words, high-numbered first. */ ! 328: ! 329: if (optype0 == POPOP || optype1 == POPOP) ! 330: { ! 331: output_asm_insn (singlemove_string (latehalf), latehalf); ! 332: return singlemove_string (operands); ! 333: } ! 334: ! 335: /* Not autodecrementing. Do the two words, low-numbered first. */ ! 336: ! 337: output_asm_insn (singlemove_string (operands), operands); ! 338: ! 339: operands[0] = latehalf[0]; ! 340: operands[1] = latehalf[1]; ! 341: return singlemove_string (operands); ! 342: } ! 343: ! 344: int ! 345: check_reg (oper, reg) ! 346: rtx oper; ! 347: int reg; ! 348: { ! 349: register int i; ! 350: ! 351: if (oper == 0) ! 352: return 0; ! 353: switch (GET_CODE(oper)) ! 354: { ! 355: case REG: ! 356: return (REGNO(oper) == reg) ? 1 : 0; ! 357: case MEM: ! 358: return check_reg(XEXP(oper, 0), reg); ! 359: case PLUS: ! 360: case MULT: ! 361: return check_reg(XEXP(oper, 0), reg) || check_reg(XEXP(oper, 1), reg); ! 362: } ! 363: return 0; ! 364: } ! 365: ! 366: /* PRINT_OPERAND is defined to call this function, ! 367: which is easier to debug than putting all the code in ! 368: a macro definition in ns32k.h. */ ! 369: ! 370: void ! 371: print_operand (file, x, code) ! 372: FILE *file; ! 373: rtx x; ! 374: char code; ! 375: { ! 376: if (code == '$') ! 377: PUT_IMMEDIATE_PREFIX(file); ! 378: else if (code == '?') ! 379: PUT_EXTERNAL_PREFIX(file); ! 380: else if (GET_CODE (x) == REG) ! 381: fprintf (file, "%s", reg_names[REGNO (x)]); ! 382: else if (GET_CODE (x) == MEM) ! 383: output_address (XEXP (x, 0)); ! 384: else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) != DImode) ! 385: if (GET_MODE (x) == DFmode) ! 386: { ! 387: union { double d; int i[2]; } u; ! 388: u.i[0] = CONST_DOUBLE_LOW (x); u.i[1] = CONST_DOUBLE_HIGH (x); ! 389: PUT_IMMEDIATE_PREFIX(file); ! 390: fprintf (file, "0d%.20e", u.d); ! 391: } ! 392: else ! 393: { ! 394: union { double d; int i[2]; } u; ! 395: u.i[0] = CONST_DOUBLE_LOW (x); u.i[1] = CONST_DOUBLE_HIGH (x); ! 396: PUT_IMMEDIATE_PREFIX(file); ! 397: fprintf (file, "0f%.20e", u.d); ! 398: } ! 399: else ! 400: { ! 401: PUT_IMMEDIATE_PREFIX(file); ! 402: output_addr_const (file, x); ! 403: } ! 404: } ! 405: ! 406: /* PRINT_OPERAND_ADDRESS is defined to call this function, ! 407: which is easier to debug than putting all the code in ! 408: a macro definition in ns32k.h . */ ! 409: ! 410: /* Completely rewritten to get this to work with Gas for PC532 Mach. ! 411: This function didn't work and I just wasn't able (nor very willing) to ! 412: figure out how it worked. ! 413: 90-11-25 Tatu Yl|nen <[email protected]> */ ! 414: ! 415: print_operand_address (file, addr) ! 416: register FILE *file; ! 417: register rtx addr; ! 418: { ! 419: static char scales[] = { 'b', 'w', 'd', 0, 'q', }; ! 420: rtx offset, base, indexexp, tmp; ! 421: int scale; ! 422: ! 423: if (GET_CODE (addr) == PRE_DEC || GET_CODE (addr) == POST_DEC) ! 424: { ! 425: fprintf (file, "tos"); ! 426: return; ! 427: } ! 428: ! 429: offset = NULL; ! 430: base = NULL; ! 431: indexexp = NULL; ! 432: while (addr != NULL) ! 433: { ! 434: if (GET_CODE (addr) == PLUS) ! 435: { ! 436: if (GET_CODE (XEXP (addr, 0)) == PLUS) ! 437: { ! 438: tmp = XEXP (addr, 1); ! 439: addr = XEXP (addr, 0); ! 440: } ! 441: else ! 442: { ! 443: tmp = XEXP (addr,0); ! 444: addr = XEXP (addr,1); ! 445: } ! 446: } ! 447: else ! 448: { ! 449: tmp = addr; ! 450: addr = NULL; ! 451: } ! 452: switch (GET_CODE (tmp)) ! 453: { ! 454: case PLUS: ! 455: abort (); ! 456: case MEM: ! 457: if (base) ! 458: { ! 459: indexexp = base; ! 460: base = tmp; ! 461: } ! 462: else ! 463: base = tmp; ! 464: break; ! 465: case REG: ! 466: if (REGNO (tmp) < 8) ! 467: if (base) ! 468: { ! 469: indexexp = tmp; ! 470: } ! 471: else ! 472: base = tmp; ! 473: else ! 474: if (base) ! 475: { ! 476: indexexp = base; ! 477: base = tmp; ! 478: } ! 479: else ! 480: base = tmp; ! 481: break; ! 482: case MULT: ! 483: indexexp = tmp; ! 484: break; ! 485: case CONST: ! 486: case CONST_INT: ! 487: case SYMBOL_REF: ! 488: case LABEL_REF: ! 489: if (offset) ! 490: offset = gen_rtx (PLUS, SImode, tmp, offset); ! 491: else ! 492: offset = tmp; ! 493: break; ! 494: default: ! 495: abort (); ! 496: } ! 497: } ! 498: if (! offset) ! 499: offset = const0_rtx; ! 500: /* now, offset, base and indexexp are set */ ! 501: if (! base) ! 502: #ifdef PC_RELATIVE ! 503: { ! 504: if (GET_CODE (offset) == LABEL_REF || GET_CODE (offset) == SYMBOL_REF) ! 505: ; ! 506: else ! 507: #endif ! 508: PUT_ABSOLUTE_PREFIX (file); ! 509: #ifdef PC_RELATIVE ! 510: } ! 511: #endif ! 512: ! 513: output_addr_const (file,offset); ! 514: if (base) /* base can be (REG ...) or (MEM ...) */ ! 515: switch (GET_CODE (base)) ! 516: { ! 517: /* now we must output base. Possible alternatives are: ! 518: (rN) (REG ...) ! 519: (sp) (REG ...) ! 520: (fp) (REG ...) ! 521: (pc) (REG ...) used for SYMBOL_REF and LABEL_REF, output ! 522: (disp(fp)) (MEM ...) just before possible [rX:y] ! 523: (disp(sp)) (MEM ...) ! 524: (disp(sb)) (MEM ...) ! 525: */ ! 526: case REG: ! 527: fprintf (file, "(%s)", reg_names[REGNO (base)]); ! 528: break; ! 529: case MEM: ! 530: addr = XEXP(base,0); ! 531: base = NULL; ! 532: offset = NULL; ! 533: while (addr != NULL) ! 534: { ! 535: if (GET_CODE (addr) == PLUS) ! 536: { ! 537: if (GET_CODE (XEXP (addr, 0)) == PLUS) ! 538: { ! 539: tmp = XEXP (addr, 1); ! 540: addr = XEXP (addr, 0); ! 541: } ! 542: else ! 543: { ! 544: tmp = XEXP (addr, 0); ! 545: addr = XEXP (addr, 1); ! 546: } ! 547: } ! 548: else ! 549: { ! 550: tmp = addr; ! 551: addr = NULL; ! 552: } ! 553: switch (GET_CODE (tmp)) ! 554: { ! 555: case REG: ! 556: base = tmp; ! 557: break; ! 558: case CONST: ! 559: case CONST_INT: ! 560: case SYMBOL_REF: ! 561: case LABEL_REF: ! 562: if (offset) ! 563: offset = gen_rtx (PLUS, SImode, tmp, offset); ! 564: else ! 565: offset = tmp; ! 566: break; ! 567: default: ! 568: abort (); ! 569: } ! 570: } ! 571: if (! offset) ! 572: offset = const0_rtx; ! 573: fprintf (file, "("); ! 574: output_addr_const (file, offset); ! 575: if (base) ! 576: fprintf (file, "(%s)", reg_names[REGNO (base)]); ! 577: else if (TARGET_SB) ! 578: fprintf (file, "(sb)"); ! 579: else ! 580: abort (); ! 581: fprintf (file, ")"); ! 582: break; ! 583: ! 584: default: ! 585: abort (); ! 586: } ! 587: #ifdef PC_RELATIVE ! 588: else /* no base */ ! 589: if (GET_CODE (offset) == LABEL_REF || GET_CODE (offset) == SYMBOL_REF) ! 590: fprintf (file, "(pc)"); ! 591: #endif ! 592: #ifdef BASE_REG_NEEDED /* this is defined if the assembler always ! 593: needs a base register */ ! 594: else if (TARGET_SB) ! 595: fprintf (file, "(sb)"); ! 596: else ! 597: abort (); ! 598: #endif ! 599: /* now print index if we have one */ ! 600: if (indexexp) ! 601: { ! 602: if (GET_CODE (indexexp) == MULT) ! 603: { ! 604: scale = INTVAL (XEXP (indexexp, 1)) >> 1; ! 605: indexexp = XEXP (indexexp, 0); ! 606: } ! 607: else ! 608: scale = 0; ! 609: if (GET_CODE (indexexp) != REG || REGNO (indexexp) >= 8) ! 610: abort (); ! 611: ! 612: fprintf (file, "[%s:%c]", ! 613: reg_names[REGNO (indexexp)], ! 614: scales[scale]); ! 615: } ! 616: } ! 617: ! 618: /* National 32032 shifting is so bad that we can get ! 619: better performance in many common cases by using other ! 620: techniques. */ ! 621: char * ! 622: output_shift_insn (operands) ! 623: rtx *operands; ! 624: { ! 625: if (GET_CODE (operands[2]) == CONST_INT ! 626: && INTVAL (operands[2]) > 0 ! 627: && INTVAL (operands[2]) <= 3) ! 628: if (GET_CODE (operands[0]) == REG) ! 629: { ! 630: if (GET_CODE (operands[1]) == REG) ! 631: { ! 632: if (REGNO (operands[0]) == REGNO (operands[1])) ! 633: { ! 634: if (operands[2] == const1_rtx) ! 635: return "addd %0,%0"; ! 636: else if (INTVAL (operands[2]) == 2) ! 637: return "addd %0,%0\n\taddd %0,%0"; ! 638: } ! 639: if (operands[2] == const1_rtx) ! 640: return "movd %1,%0\n\taddd %0,%0"; ! 641: ! 642: operands[1] = gen_indexed_expr (const0_rtx, operands[1], operands[2]); ! 643: return "addr %a1,%0"; ! 644: } ! 645: if (operands[2] == const1_rtx) ! 646: return "movd %1,%0\n\taddd %0,%0"; ! 647: } ! 648: else if (GET_CODE (operands[1]) == REG) ! 649: { ! 650: operands[1] = gen_indexed_expr (const0_rtx, operands[1], operands[2]); ! 651: return "addr %a1,%0"; ! 652: } ! 653: else if (INTVAL (operands[2]) == 1 ! 654: && GET_CODE (operands[1]) == MEM ! 655: && rtx_equal_p (operands [0], operands[1])) ! 656: { ! 657: rtx temp = XEXP (operands[1], 0); ! 658: ! 659: if (GET_CODE (temp) == REG ! 660: || (GET_CODE (temp) == PLUS ! 661: && GET_CODE (XEXP (temp, 0)) == REG ! 662: && GET_CODE (XEXP (temp, 1)) == CONST_INT)) ! 663: return "addd %0,%0"; ! 664: } ! 665: else return "ashd %2,%0"; ! 666: return "ashd %2,%0"; ! 667: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.