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