|
|
1.1 ! root 1: /* Subroutines for insn-output.c for Motorola 88000. ! 2: Copyright (C) 1987 Free Software Foundation, Inc. ! 3: Contributed by Michael Tiemann ([email protected]) ! 4: ! 5: This file is part of GNU CC. ! 6: ! 7: GNU CC is distributed in the hope that it will be useful, ! 8: but WITHOUT ANY WARRANTY. No author or distributor ! 9: accepts responsibility to anyone for the consequences of using it ! 10: or for whether it serves any particular purpose or works at all, ! 11: unless he says so in writing. Refer to the GNU CC General Public ! 12: License for full details. ! 13: ! 14: Everyone is granted permission to copy, modify and redistribute ! 15: GNU CC, but only under the conditions described in the ! 16: GNU CC General Public License. A copy of this license is ! 17: supposed to have been given to you along with GNU CC so you ! 18: can know your rights and responsibilities. It should be in a ! 19: file named COPYING. Among other things, the copyright notice ! 20: and this notice must be preserved on all copies. */ ! 21: ! 22: #ifndef FILE ! 23: #include <stdio.h> ! 24: #endif ! 25: ! 26: /* This is where the condition code register lives. */ ! 27: rtx cc0_reg_rtx; ! 28: ! 29: static rtx find_addr_reg (); ! 30: ! 31: #if 0 ! 32: char * ! 33: output_compare (operands, opcode, exchange_opcode) ! 34: rtx *operands; ! 35: char *opcode; ! 36: char *exchange_opcode; ! 37: { ! 38: static char buf[40]; ! 39: rtx op1, op2; ! 40: ! 41: if (GET_CODE (cc_prev_status.value2) == MINUS) ! 42: { ! 43: op1 = XEXP (cc_prev_status.value2, 0); ! 44: op2 = XEXP (cc_prev_status.value2, 1); ! 45: } ! 46: else ! 47: { ! 48: op1 = cc_prev_status.value2; ! 49: op2 = const0_rtx; ! 50: } ! 51: if (GET_CODE (op1) == CONST_INT) ! 52: { ! 53: operands[2] = op1; ! 54: operands[1] = op2; ! 55: opcode = exchange_opcode; ! 56: } ! 57: else ! 58: { ! 59: operands[1] = op1; ! 60: operands[2] = op2; ! 61: } ! 62: sprintf (buf, "cmp r25,%%1,%%2\n\tbcnd %s,r25,%%l0", opcode); ! 63: return buf; ! 64: } ! 65: ! 66: char * ! 67: output_fcompare (operands, opcode, exchange_opcode) ! 68: rtx *operands; ! 69: char *opcode; ! 70: char *exchange_opcode; ! 71: { ! 72: static char buf[40]; ! 73: ! 74: rtx op1, op2; ! 75: ! 76: if (GET_CODE (cc_prev_status.value2) == MINUS) ! 77: { ! 78: op1 = XEXP (cc_prev_status.value2, 0); ! 79: op2 = XEXP (cc_prev_status.value2, 1); ! 80: } ! 81: else ! 82: { ! 83: op1 = cc_prev_status.value2; ! 84: op2 = const0_rtx; ! 85: } ! 86: if (GET_CODE (op1) == CONST_DOUBLE) ! 87: { ! 88: operands[2] = op1; ! 89: operands[1] = op2; ! 90: opcode = exchange_opcode; ! 91: } ! 92: else ! 93: { ! 94: operands[1] = op1; ! 95: operands[2] = op2; ! 96: } ! 97: sprintf (buf, "cmp r25,%%1,%%2\n\tbcnd %s,r25,%%l0", opcode); ! 98: return buf; ! 99: } ! 100: ! 101: char * ! 102: output_store (operands, opcode, exchange_opcode) ! 103: rtx *operands; ! 104: char *opcode; ! 105: char *exchange_opcode; ! 106: { ! 107: static char buf[40]; ! 108: rtx op1, op2; ! 109: ! 110: if (GET_CODE (cc_prev_status.value2) == MINUS) ! 111: { ! 112: op1 = XEXP (cc_prev_status.value2, 0); ! 113: op2 = XEXP (cc_prev_status.value2, 1); ! 114: } ! 115: else ! 116: { ! 117: op1 = cc_prev_status.value2; ! 118: op2 = const0_rtx; ! 119: } ! 120: ! 121: if (GET_CODE (op1) == CONST_INT) ! 122: { ! 123: operands[2] = op1; ! 124: operands[1] = op2; ! 125: opcode = exchange_opcode; ! 126: } ! 127: else ! 128: { ! 129: operands[1] = op1; ! 130: operands[2] = op2; ! 131: } ! 132: ! 133: sprintf (buf, "cmp r25,%%1,%%2\n\textu %%0,r25,1<%s>", opcode); ! 134: return buf; ! 135: } ! 136: #endif ! 137: ! 138: /* Nonzero if OP is a valid second operand for an arithmetic insn. */ ! 139: ! 140: int ! 141: arith_operand (op, mode) ! 142: rtx op; ! 143: enum machine_mode mode; ! 144: { ! 145: return (register_operand (op, mode) ! 146: || (GET_CODE (op) == CONST_INT ! 147: && (unsigned) INTVAL (op) < 0x10000)); ! 148: } ! 149: ! 150: int ! 151: arith32_operand (op, mode) ! 152: rtx op; ! 153: enum machine_mode mode; ! 154: { ! 155: return (register_operand (op, mode) || GET_CODE (op) == CONST_INT); ! 156: } ! 157: ! 158: int ! 159: int5_operand (op, mode) ! 160: rtx op; ! 161: enum machine_mode mode; ! 162: { ! 163: return (GET_CODE (op) == CONST_INT && (unsigned) INTVAL (op) < 0x20); ! 164: } ! 165: ! 166: /* Return the best assembler insn template ! 167: for moving operands[1] into operands[0] as a fullword. */ ! 168: ! 169: static char * ! 170: singlemove_string (operands) ! 171: rtx *operands; ! 172: { ! 173: if (GET_CODE (operands[0]) == MEM) ! 174: return "st %r1,%0"; ! 175: if (GET_CODE (operands[1]) == MEM) ! 176: return "ld %0,%1"; ! 177: return "or %0,r0,%1"; ! 178: } ! 179: ! 180: /* Output assembler code to perform a doubleword move insn ! 181: with operands OPERANDS. */ ! 182: ! 183: char * ! 184: output_move_double (operands) ! 185: rtx *operands; ! 186: { ! 187: enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype0, optype1; ! 188: rtx latehalf[2]; ! 189: rtx addreg0 = 0, addreg1 = 0; ! 190: ! 191: /* First classify both operands. */ ! 192: ! 193: if (REG_P (operands[0])) ! 194: optype0 = REGOP; ! 195: else if (offsetable_memref_p (operands[0])) ! 196: optype0 = OFFSOP; ! 197: else if (GET_CODE (operands[0]) == MEM) ! 198: optype0 = MEMOP; ! 199: else ! 200: optype0 = RNDOP; ! 201: ! 202: if (REG_P (operands[1])) ! 203: optype1 = REGOP; ! 204: else if (CONSTANT_P (operands[1]) ! 205: || GET_CODE (operands[1]) == CONST_DOUBLE) ! 206: optype1 = CNSTOP; ! 207: else if (offsetable_memref_p (operands[1])) ! 208: optype1 = OFFSOP; ! 209: else if (GET_CODE (operands[1]) == MEM) ! 210: optype0 = MEMOP; ! 211: else ! 212: optype1 = RNDOP; ! 213: ! 214: /* Check for the cases that the operand constraints are not ! 215: supposed to allow to happen. Abort if we get one, ! 216: because generating code for these cases is painful. */ ! 217: ! 218: if (optype0 == RNDOP || optype1 == RNDOP) ! 219: abort (); ! 220: ! 221: /* If an operand is an unoffsettable memory ref, find a register ! 222: we can increment temporarily to make it refer to the second word. */ ! 223: ! 224: if (optype0 == MEMOP) ! 225: addreg0 = find_addr_reg (operands[0]); ! 226: ! 227: if (optype1 == MEMOP) ! 228: addreg1 = find_addr_reg (operands[1]); ! 229: ! 230: /* Ok, we can do one word at a time. ! 231: Normally we do the low-numbered word first, ! 232: but if either operand is autodecrementing then we ! 233: do the high-numbered word first. ! 234: ! 235: In either case, set up in LATEHALF the operands to use ! 236: for the high-numbered word and in some cases alter the ! 237: operands in OPERANDS to be suitable for the low-numbered word. */ ! 238: ! 239: if (optype0 == REGOP) ! 240: latehalf[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1); ! 241: else if (optype0 == OFFSOP) ! 242: latehalf[0] = adj_offsetable_operand (operands[0], 4); ! 243: else ! 244: latehalf[0] = operands[0]; ! 245: ! 246: if (optype1 == REGOP) ! 247: latehalf[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1); ! 248: else if (optype1 == OFFSOP) ! 249: latehalf[1] = adj_offsetable_operand (operands[1], 4); ! 250: else if (optype1 == CNSTOP) ! 251: { ! 252: if (CONSTANT_P (operands[1])) ! 253: latehalf[1] = const0_rtx; ! 254: else if (GET_CODE (operands[1]) == CONST_DOUBLE) ! 255: { ! 256: latehalf[1] = gen_rtx (CONST_INT, VOIDmode, XINT (operands[1], 1)); ! 257: operands[1] = gen_rtx (CONST_INT, VOIDmode, XINT (operands[1], 0)); ! 258: } ! 259: } ! 260: else ! 261: latehalf[1] = operands[1]; ! 262: ! 263: /* If the first move would clobber the source of the second one, ! 264: do them in the other order. This happens only for registers; ! 265: such overlap can't happen in memory unless the user explicitly ! 266: sets it up, and that is an undefined circumstance. */ ! 267: ! 268: if (optype0 == REGOP && optype1 == REGOP ! 269: && REGNO (operands[0]) == REGNO (latehalf[1])) ! 270: { ! 271: /* Make any unoffsetable addresses point at high-numbered word. */ ! 272: if (addreg0) ! 273: output_asm_insn ("addu %0,%0,4", &addreg0); ! 274: if (addreg1) ! 275: output_asm_insn ("addu %0,%0,4", &addreg1); ! 276: ! 277: /* Do that word. */ ! 278: output_asm_insn (singlemove_string (latehalf), latehalf); ! 279: ! 280: /* Undo the adds we just did. */ ! 281: if (addreg0) ! 282: output_asm_insn ("subu %0,%0,4", &addreg0); ! 283: if (addreg1) ! 284: output_asm_insn ("subu %0,%0,4", &addreg0); ! 285: ! 286: /* Do low-numbered word. */ ! 287: return singlemove_string (operands); ! 288: } ! 289: ! 290: /* Normal case: do the two words, low-numbered first. */ ! 291: ! 292: output_asm_insn (singlemove_string (operands), operands); ! 293: ! 294: /* Make any unoffsetable addresses point at high-numbered word. */ ! 295: if (addreg0) ! 296: output_asm_insn ("addu %0,%0,4", &addreg0); ! 297: if (addreg1) ! 298: output_asm_insn ("addu %0,%0,4", &addreg1); ! 299: ! 300: /* Do that word. */ ! 301: output_asm_insn (singlemove_string (latehalf), latehalf); ! 302: ! 303: /* Undo the adds we just did. */ ! 304: if (addreg0) ! 305: output_asm_insn ("subu %0,%0,4", &addreg0); ! 306: if (addreg1) ! 307: output_asm_insn ("subu %0,%0,4", &addreg1); ! 308: ! 309: return ""; ! 310: } ! 311: ! 312: /* Return a REG that occurs in ADDR with coefficient 1. ! 313: ADDR can be effectively incremented by incrementing REG. */ ! 314: ! 315: static rtx ! 316: find_addr_reg (addr) ! 317: rtx addr; ! 318: { ! 319: while (GET_CODE (addr) == PLUS) ! 320: { ! 321: if (GET_CODE (XEXP (addr, 0)) == REG) ! 322: addr = XEXP (addr, 0); ! 323: if (GET_CODE (XEXP (addr, 1)) == REG) ! 324: addr = XEXP (addr, 1); ! 325: if (CONSTANT_P (XEXP (addr, 0))) ! 326: addr = XEXP (addr, 1); ! 327: if (CONSTANT_P (XEXP (addr, 1))) ! 328: addr = XEXP (addr, 0); ! 329: } ! 330: if (GET_CODE (addr) == REG) ! 331: return addr; ! 332: return 0; ! 333: } ! 334: ! 335: /* Output an ascii string. */ ! 336: output_ascii (file, p, size) ! 337: FILE *file; ! 338: char *p; ! 339: int size; ! 340: { ! 341: int i; ! 342: ! 343: fprintf (file, "\tstring \""); ! 344: ! 345: for (i = 0; i < size; i++) ! 346: { ! 347: register int c = p[i]; ! 348: if (c == '\"' || c == '\\') ! 349: putc ('\\', file); ! 350: if (c >= ' ' && c < 0177) ! 351: putc (c, file); ! 352: else ! 353: { ! 354: fprintf (file, "\\%03o", c); ! 355: /* After an octal-escape, if a digit follows, ! 356: terminate one string constant and start another. ! 357: The Vax assembler fails to stop reading the escape ! 358: after three digits, so this is the only way we ! 359: can get it to parse the data properly. */ ! 360: if (i < size - 1 && p[i + 1] >= '0' && p[i + 1] <= '9') ! 361: fprintf (file, "\"\n\tstring \""); ! 362: } ! 363: } ! 364: fprintf (file, "\"\n"); ! 365: } ! 366: ! 367: void ! 368: output_load_address (operands) ! 369: rtx *operands; ! 370: { ! 371: rtx base, offset; ! 372: ! 373: if (CONSTANT_P (operands[3])) ! 374: { ! 375: output_asm_insn ("lda %0,%3", operands); ! 376: return; ! 377: } ! 378: ! 379: if (REG_P (operands[3])) ! 380: { ! 381: if (REGNO (operands[0]) != REGNO (operands[3])) ! 382: output_asm_insn ("or %0,r0,%3", operands); ! 383: return; ! 384: } ! 385: ! 386: base = XEXP (operands[3], 0); ! 387: offset = XEXP (operands[3], 1); ! 388: ! 389: if (GET_CODE (base) == CONST_INT) ! 390: { ! 391: rtx tmp = base; ! 392: base = offset; ! 393: offset = tmp; ! 394: } ! 395: ! 396: if (GET_CODE (offset) != CONST_INT) ! 397: abort (); ! 398: ! 399: operands[6] = base; ! 400: operands[7] = offset; ! 401: ! 402: if (REG_P (base)) ! 403: if (FITS_16_BITS (offset)) ! 404: output_asm_insn ("addu %0,%6,%7", operands); ! 405: else if (INT_FITS_16_BITS (- INTVAL (offset))) ! 406: output_asm_insn ("subu %0,%6,%7", operands); ! 407: else ! 408: output_asm_insn ("or.h %0,r0,hi16(%7)\n\tor %0,%0,lo16(%7)\n\tadd %0,%6,%0", operands); ! 409: else ! 410: { ! 411: if (GET_CODE (base) == MULT) ! 412: if (GET_MODE (base) == QImode) ! 413: output_asm_insn ("lda.b %0,%6"); ! 414: else if (GET_MODE (base) == HImode) ! 415: output_asm_insn ("lda.h %0,%6"); ! 416: else if (GET_MODE (base) == SImode) ! 417: output_asm_insn ("lda %0,%6"); ! 418: else ! 419: output_asm_insn ("lda.d %0,%6"); ! 420: else ! 421: output_asm_insn ("lda %0,%6"); ! 422: ! 423: if (FITS_16_BITS (offset)) ! 424: output_asm_insn ("addu %0,%7,%0", operands); ! 425: else if (INT_FITS_16_BITS (- INTVAL (offset))) ! 426: output_asm_insn ("subu %0,%7,%0", operands); ! 427: else ! 428: output_asm_insn ("or.h r25,r0,hi16(%7)\n\tor r25,r0,lo16(%7)\n\taddu %0,%0r25", operands); ! 429: } ! 430: } ! 431: ! 432: char * ! 433: output_block_move (operands) ! 434: rtx *operands; ! 435: { ! 436: static int movstrsi_label = 0; ! 437: int align = 4; ! 438: ! 439: rtx xoperands[9]; ! 440: int available[3]; ! 441: int i, j; ! 442: ! 443: /* Since we clobber untold things, nix the condition codes. */ ! 444: CC_STATUS_INIT; ! 445: ! 446: /* Get past the MEMs. */ ! 447: operands[0] = XEXP (operands[0], 0); ! 448: operands[1] = XEXP (operands[1], 0); ! 449: ! 450: xoperands[0] = 0; ! 451: xoperands[1] = 0; ! 452: xoperands[2] = 0; ! 453: ! 454: available[0] = 1; ! 455: available[1] = 1; ! 456: available[2] = 1; ! 457: #if 1 ! 458: /* Prepare to juggle registers if necessary. */ ! 459: if (REG_P (operands[0]) && (unsigned) (REGNO (operands[0]) - 10) < 3) ! 460: { ! 461: xoperands[0] = operands[0]; ! 462: available[REGNO (operands[0]) - 10] = 0; ! 463: } ! 464: if (REG_P (operands[1]) && (unsigned) (REGNO (operands[1]) - 10) < 3) ! 465: { ! 466: xoperands[1] = operands[1]; ! 467: available[REGNO (operands[1]) - 10] = 0; ! 468: } ! 469: if (REG_P (operands[2]) && (unsigned) (REGNO (operands[2]) - 10) < 3) ! 470: { ! 471: xoperands[2] = operands[2]; ! 472: available[REGNO (operands[2]) - 10] = 0; ! 473: } ! 474: for (i = 0; i < 3; i++) ! 475: { ! 476: if (xoperands[i]) ! 477: continue; ! 478: if (available[0]) ! 479: { ! 480: xoperands[i] = gen_rtx (REG, SImode, 10); ! 481: available[0] = 0; ! 482: continue; ! 483: } ! 484: if (available[1]) ! 485: { ! 486: xoperands[i] = gen_rtx (REG, SImode, 11); ! 487: available[1] = 0; ! 488: continue; ! 489: } ! 490: xoperands[i] = gen_rtx (REG, SImode, 12); ! 491: available[2] = 0; ! 492: } ! 493: #endif ! 494: ! 495: /* First, figure out best alignment we may assume. */ ! 496: if (REG_P (operands[2])) ! 497: { ! 498: xoperands[5] = operands[2]; ! 499: output_asm_insn ("sub %5,%2,1", xoperands); ! 500: align = 1; ! 501: } ! 502: else ! 503: { ! 504: int i = INTVAL (operands[2]); ! 505: ! 506: if (i & 1) ! 507: align = 1; ! 508: else if (i & 3) ! 509: { ! 510: align = 2; ! 511: i >>= 1; ! 512: } ! 513: else ! 514: i >>= 2; ! 515: ! 516: /* predecrement count. */ ! 517: i -= 1; ! 518: if (i < 0) abort (); ! 519: ! 520: xoperands[5] = gen_rtx (CONST_INT, VOIDmode, i); ! 521: ! 522: if (INT_FITS_16_BITS (i)) ! 523: output_asm_insn ("addu %2,r0,%5", xoperands); ! 524: else if (INT_FITS_16_BITS (-i)) ! 525: { ! 526: xoperands[5] = gen_rtx (CONST_INT, VOIDmode, -i); ! 527: output_asm_insn ("subu %2,r0,%5", xoperands); ! 528: } ! 529: else ! 530: output_asm_insn ("or.u %2,r0,hi16(%5)\n\tor %2,%2,lo16(%5)", xoperands); ! 531: } ! 532: /* Now, set up for pipelined operation: dest must contain ! 533: a pre-incremented address, because its index is pre-decremented. */ ! 534: ! 535: xoperands[3] = plus_constant (operands[0], align); ! 536: output_load_address (xoperands); ! 537: ! 538: xoperands[4] = operands[1]; ! 539: output_load_address (xoperands+1); ! 540: ! 541: xoperands[3] = gen_rtx (CONST_INT, VOIDmode, movstrsi_label++); ! 542: ! 543: if (align == 4) ! 544: output_asm_insn ("\n@Lm%3:\n\tld r25,%1[%2]\n\tsubu %2,%2,1\n\tbcnd.n ge0,%2,@Lm%3\n\tst r25,%0[%2]", xoperands); ! 545: else if (align == 2) ! 546: output_asm_insn ("\n@Lm%3:\n\tld.h r25,%1[%2]\n\tsubu %2,%2,1\n\tbcnd.n ge0,%2,@Lm%3\n\tst.h r25,%0[%2]", xoperands); ! 547: else ! 548: output_asm_insn ("\n@Lm%3:\n\tld.b r25,%1[%2]\n\tsubu %2,%2,1\n\tbcnd.n ge0,%2,@Lm%3\n\tst.b r25,%0[%2]", xoperands); ! 549: return ""; ! 550: } ! 551: ! 552: char * ! 553: output_store_const_int (mode, operands) ! 554: enum machine_mode mode; ! 555: rtx *operands; ! 556: { ! 557: int i = INTVAL (operands[1]); ! 558: if (INT_FITS_16_BITS (i)) ! 559: return "addu %0,r0,%1"; ! 560: if (INT_FITS_16_BITS (-i)) ! 561: { ! 562: operands[1] = gen_rtx (CONST_INT, VOIDmode, -i); ! 563: return "subu %0,r0,%1"; ! 564: } ! 565: if ((i & 0xffff) == 0) ! 566: return "or.u %0,r0,hi16(%1)"; ! 567: /* Could check to see if number is a contiguous field ! 568: of 1's. Then we could use the SET instruction. */ ! 569: if (mode == HImode) ! 570: { ! 571: warning ("truncating constant `%d' to fit in half-word", INTVAL (operands[1])); ! 572: return "or %0,r0,lo16(%1)"; ! 573: } ! 574: if (mode == QImode) ! 575: { ! 576: warning ("truncating constant `%d' to fit in byte"); ! 577: operands[1] = gen_rtx (CONST_INT, VOIDmode, i & 0xff); ! 578: return "or %0,r0,%1"; ! 579: } ! 580: ! 581: return "or.u %0,r0,hi16(%1)\n\tor %0,%0,lo16(%1)"; ! 582: } ! 583: ! 584: /* This routine assumes that floating point numbers are represented ! 585: in a manner which is consistent between host and target machines. */ ! 586: char * ! 587: output_store_const_float (mode, operands) ! 588: enum machine_mode mode; ! 589: rtx *operands; ! 590: { ! 591: int i = INTVAL (operands[1]); ! 592: if (INT_FITS_16_BITS (i)) ! 593: return "addu %0,r0,%1"; ! 594: if (INT_FITS_16_BITS (-i)) ! 595: { ! 596: operands[1] = gen_rtx (CONST_INT, VOIDmode, -i); ! 597: return "subu %0,r0,%1"; ! 598: } ! 599: if ((i & 0xffff) == 0) ! 600: return "or.u %0,r0,hi16(%1)"; ! 601: /* Could check to see if number is a contiguous field ! 602: of 1's. Then we could use the SET instruction. */ ! 603: return "or.u %0,r0,hi16(%1)\n\tor %0,%0,lo16(%1)"; ! 604: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.