|
|
1.1 ! root 1: /* Subroutines for insn-output.c for Tahoe. ! 2: Copyright (C) 1989 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 1, 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: ! 21: /* ! 22: * File: output-tahoe.c ! 23: * ! 24: * This port made at the University of Buffalo by Devon Bowen, ! 25: * Dale Wiles and Kevin Zachmann. ! 26: * ! 27: * Mail bugs reports or fixes to: [email protected] ! 28: */ ! 29: ! 30: ! 31: /* most of the print_operand_address function was taken from the vax */ ! 32: /* since the modes are basically the same. I had to add a special case, */ ! 33: /* though, for symbol references with offsets. */ ! 34: ! 35: #include <stdio.h> ! 36: ! 37: print_operand_address (file, addr) ! 38: FILE *file; ! 39: register rtx addr; ! 40: { ! 41: register rtx reg1, reg2, breg, ireg; ! 42: rtx offset; ! 43: static char *reg_name[] = REGISTER_NAMES; ! 44: ! 45: retry: ! 46: switch (GET_CODE (addr)) ! 47: { ! 48: case MEM: ! 49: fprintf (file, "*"); ! 50: addr = XEXP (addr, 0); ! 51: goto retry; ! 52: ! 53: case REG: ! 54: fprintf (file, "(%s)", reg_name [REGNO (addr)]); ! 55: break; ! 56: ! 57: case PRE_DEC: ! 58: fprintf (file, "-(%s)", reg_name [REGNO (XEXP (addr, 0))]); ! 59: break; ! 60: ! 61: case POST_INC: ! 62: fprintf (file, "(%s)+", reg_name [REGNO (XEXP (addr, 0))]); ! 63: break; ! 64: ! 65: case PLUS: ! 66: reg1 = 0; reg2 = 0; ! 67: ireg = 0; breg = 0; ! 68: offset = 0; ! 69: ! 70: if (CONSTANT_ADDRESS_P (XEXP (addr, 0)) ! 71: && GET_CODE (XEXP (addr, 1)) == CONST_INT) ! 72: output_addr_const (file, addr); ! 73: ! 74: if (CONSTANT_ADDRESS_P (XEXP (addr, 1)) ! 75: && GET_CODE (XEXP (addr, 0)) == CONST_INT) ! 76: output_addr_const (file, addr); ! 77: ! 78: if (CONSTANT_ADDRESS_P (XEXP (addr, 0)) ! 79: || GET_CODE (XEXP (addr, 0)) == MEM) ! 80: { ! 81: offset = XEXP (addr, 0); ! 82: addr = XEXP (addr, 1); ! 83: } ! 84: else if (CONSTANT_ADDRESS_P (XEXP (addr, 1)) ! 85: || GET_CODE (XEXP (addr, 1)) == MEM) ! 86: { ! 87: offset = XEXP (addr, 1); ! 88: addr = XEXP (addr, 0); ! 89: } ! 90: if (GET_CODE (addr) != PLUS) ! 91: ; ! 92: else if (GET_CODE (XEXP (addr, 0)) == MULT) ! 93: { ! 94: reg1 = XEXP (addr, 0); ! 95: addr = XEXP (addr, 1); ! 96: } ! 97: else if (GET_CODE (XEXP (addr, 1)) == MULT) ! 98: { ! 99: reg1 = XEXP (addr, 1); ! 100: addr = XEXP (addr, 0); ! 101: } ! 102: else if (GET_CODE (XEXP (addr, 0)) == REG) ! 103: { ! 104: reg1 = XEXP (addr, 0); ! 105: addr = XEXP (addr, 1); ! 106: } ! 107: else if (GET_CODE (XEXP (addr, 1)) == REG) ! 108: { ! 109: reg1 = XEXP (addr, 1); ! 110: addr = XEXP (addr, 0); ! 111: } ! 112: if (GET_CODE (addr) == REG || GET_CODE (addr) == MULT) ! 113: { ! 114: if (reg1 == 0) ! 115: reg1 = addr; ! 116: else ! 117: reg2 = addr; ! 118: addr = 0; ! 119: } ! 120: if (offset != 0) ! 121: { ! 122: if (addr != 0) abort (); ! 123: addr = offset; ! 124: } ! 125: if (reg1 != 0 && GET_CODE (reg1) == MULT) ! 126: { ! 127: breg = reg2; ! 128: ireg = reg1; ! 129: } ! 130: else if (reg2 != 0 && GET_CODE (reg2) == MULT) ! 131: { ! 132: breg = reg1; ! 133: ireg = reg2; ! 134: } ! 135: else if (reg2 != 0 || GET_CODE (addr) == MEM) ! 136: { ! 137: breg = reg2; ! 138: ireg = reg1; ! 139: } ! 140: else ! 141: { ! 142: breg = reg1; ! 143: ireg = reg2; ! 144: } ! 145: if (addr != 0) ! 146: output_address (offset); ! 147: if (breg != 0) ! 148: { ! 149: if (GET_CODE (breg) != REG) ! 150: abort (); ! 151: fprintf (file, "(%s)", reg_name[REGNO (breg)]); ! 152: } ! 153: if (ireg != 0) ! 154: { ! 155: if (GET_CODE (ireg) == MULT) ! 156: ireg = XEXP (ireg, 0); ! 157: if (GET_CODE (ireg) != REG) ! 158: abort (); ! 159: fprintf (file, "[%s]", reg_name[REGNO (ireg)]); ! 160: } ! 161: break; ! 162: ! 163: default: ! 164: output_addr_const (file, addr); ! 165: } ! 166: } ! 167: ! 168: ! 169: /* Do a quick check and find out what the best way to do the */ ! 170: /* mini-move is. Could be a push or a move..... */ ! 171: ! 172: static char * ! 173: singlemove_string (operands) ! 174: rtx *operands; ! 175: { ! 176: if (GET_CODE (operands[0]) == MEM ! 177: && GET_CODE (XEXP (operands[0],0)) == PRE_DEC) ! 178: return "pushl %1"; ! 179: return "movl %1,%0"; ! 180: } ! 181: ! 182: ! 183: /* given the rtx for an address, return true if the given */ ! 184: /* register number is used in the address somewhere. */ ! 185: ! 186: int ! 187: regisused (addr,regnum) ! 188: rtx addr; ! 189: int regnum; ! 190: { ! 191: if (GET_CODE (addr) == REG) ! 192: { ! 193: if (REGNO (addr) == regnum) ! 194: return (1); ! 195: else ! 196: return (0); ! 197: } ! 198: ! 199: if (GET_CODE (addr) == MEM) ! 200: return regisused (XEXP (addr,0),regnum); ! 201: ! 202: if (GET_CODE (addr) == MULT || GET_CODE (addr) == PLUS) ! 203: return (regisused (XEXP (addr,0),regnum) ! 204: || regisused (XEXP (addr,1),regnum)); ! 205: ! 206: return 0; ! 207: } ! 208: ! 209: ! 210: /* Given some rtx, traverse it and return the register used in a */ ! 211: /* index. If no index is found, return 0. */ ! 212: ! 213: rtx ! 214: index_reg (addr) ! 215: rtx addr; ! 216: { ! 217: rtx temp; ! 218: ! 219: if (GET_CODE (addr) == MEM) ! 220: return index_reg (XEXP (addr,0)); ! 221: ! 222: if (GET_CODE (addr) == MULT) ! 223: { ! 224: if (GET_CODE (XEXP (addr,0)) == REG) ! 225: return XEXP (addr,0); ! 226: else ! 227: return XEXP (addr,1); ! 228: } ! 229: ! 230: if (GET_CODE (addr) == PLUS) ! 231: { ! 232: if (temp = index_reg (XEXP (addr,0))) ! 233: return temp; ! 234: else ! 235: return index_reg (XEXP (addr,1)); ! 236: } ! 237: ! 238: return 0; ! 239: } ! 240: ! 241: ! 242: /* simulate the move double by generating two movl's. You have */ ! 243: /* to be careful about mixing modes here. A future improvement */ ! 244: /* would be to allow immediate doubles. */ ! 245: ! 246: char * ! 247: output_move_double (operands) ! 248: rtx *operands; ! 249: { ! 250: enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, INDOP, CNSTOP, RNDOP } optype0, optype1; ! 251: rtx latehalf[2]; ! 252: rtx shftreg0 = 0, shftreg1 = 0; ! 253: rtx temp0 = 0, temp1 = 0; ! 254: rtx addreg0 = 0, addreg1 = 0; ! 255: int dohighfirst = 0; ! 256: ! 257: /* First classify both operands. */ ! 258: ! 259: if (REG_P (operands[0])) ! 260: optype0 = REGOP; ! 261: else if ((GET_CODE (operands[0])==MEM) && (shftreg0=index_reg (operands[0]))) ! 262: optype0 = INDOP; ! 263: else if (offsettable_memref_p (operands[0])) ! 264: optype0 = OFFSOP; ! 265: else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC) ! 266: { ! 267: optype0 = PUSHOP; ! 268: dohighfirst++; ! 269: } ! 270: else if (GET_CODE (operands[0]) == MEM) ! 271: optype0 = MEMOP; ! 272: else ! 273: optype0 = RNDOP; ! 274: ! 275: if (REG_P (operands[1])) ! 276: optype1 = REGOP; ! 277: else if ((GET_CODE (operands[1])==MEM) && (shftreg1=index_reg (operands[1]))) ! 278: optype1 = INDOP; ! 279: else if (offsettable_memref_p (operands[1])) ! 280: optype1 = OFFSOP; ! 281: else if (GET_CODE (XEXP (operands[1], 0)) == POST_INC) ! 282: optype1 = POPOP; ! 283: else if (GET_CODE (operands[1]) == MEM) ! 284: optype1 = MEMOP; ! 285: else if (GET_CODE (operands[1]) == CONST_DOUBLE || CONSTANT_P (operands[1])) ! 286: optype1 = CNSTOP; ! 287: else ! 288: optype1 = RNDOP; ! 289: ! 290: /* set up for the high byte move for operand zero */ ! 291: ! 292: switch (optype0) ! 293: { ! 294: ! 295: /* if it's a register, just use the next highest in the */ ! 296: /* high address move. */ ! 297: ! 298: case REGOP: ! 299: latehalf[0] = gen_rtx (REG,SImode,REGNO (operands[0])+1); ! 300: break; ! 301: ! 302: /* for an offsettable address, use the gcc function to */ ! 303: /* modify the operand to get an offset of 4 higher for */ ! 304: /* the second move. */ ! 305: ! 306: case OFFSOP: ! 307: latehalf[0] = adj_offsettable_operand (operands[0], 4); ! 308: break; ! 309: ! 310: /* if the operand is MEMOP type, it must be a pointer */ ! 311: /* to a pointer. So just remember to increase the mem */ ! 312: /* location and use the same operand. */ ! 313: ! 314: case MEMOP: ! 315: latehalf[0] = operands[0]; ! 316: addreg0 = XEXP (operands[0],0); ! 317: break; ! 318: ! 319: /* if we're dealing with a push instruction, just leave */ ! 320: /* the operand alone since it auto-increments. */ ! 321: ! 322: case PUSHOP: ! 323: latehalf[0] = operands[0]; ! 324: break; ! 325: ! 326: /* YUCK! Indexed addressing!! If the address is considered */ ! 327: /* offsettable, go use the offset in the high part. Otherwise */ ! 328: /* find what exactly is being added to the mutiplication. If */ ! 329: /* it's a mem reference, increment that with the high part */ ! 330: /* being unchanged to cause the shift. If it's a reg, do the */ ! 331: /* same. If you can't identify it, abort. Remember that the */ ! 332: /* shift register was already set during identification. */ ! 333: ! 334: case INDOP: ! 335: if (offsettable_memref_p (operands[0])) ! 336: { ! 337: latehalf[0] = adj_offsettable_operand (operands[0],4); ! 338: break; ! 339: } ! 340: ! 341: latehalf[0] = operands[0]; ! 342: ! 343: temp0 = XEXP (XEXP (operands[0],0),0); ! 344: if (GET_CODE (temp0) == MULT) ! 345: { ! 346: temp1 = temp0; ! 347: temp0 = XEXP (XEXP (operands[0],0),1); ! 348: } ! 349: else ! 350: { ! 351: temp1 = XEXP (XEXP (operands[0],0),1); ! 352: if (GET_CODE (temp1) != MULT) ! 353: abort (); ! 354: } ! 355: ! 356: if (GET_CODE (temp0) == MEM) ! 357: addreg0 = temp0; ! 358: else if (GET_CODE (temp0) == REG) ! 359: addreg0 = temp0; ! 360: else ! 361: abort (); ! 362: ! 363: break; ! 364: ! 365: /* if we don't know the operand type, print a friendly */ ! 366: /* little error message... 8-) */ ! 367: ! 368: case RNDOP: ! 369: default: ! 370: abort (); ! 371: } ! 372: ! 373: /* do the same setup for operand one */ ! 374: ! 375: switch (optype1) ! 376: { ! 377: ! 378: case REGOP: ! 379: latehalf[1] = gen_rtx (REG,SImode,REGNO (operands[1])+1); ! 380: break; ! 381: ! 382: case OFFSOP: ! 383: latehalf[1] = adj_offsettable_operand (operands[1], 4); ! 384: break; ! 385: ! 386: case MEMOP: ! 387: latehalf[1] = operands[1]; ! 388: addreg1 = XEXP (operands[1],0); ! 389: break; ! 390: ! 391: case POPOP: ! 392: latehalf[1] = operands[1]; ! 393: break; ! 394: ! 395: case INDOP: ! 396: if (offsettable_memref_p (operands[1])) ! 397: { ! 398: latehalf[1] = adj_offsettable_operand (operands[1],4); ! 399: break; ! 400: } ! 401: ! 402: latehalf[1] = operands[1]; ! 403: ! 404: temp0 = XEXP (XEXP (operands[1],0),0); ! 405: if (GET_CODE (temp0) == MULT) ! 406: { ! 407: temp1 = temp0; ! 408: temp0 = XEXP (XEXP (operands[1],0),1); ! 409: } ! 410: else ! 411: { ! 412: temp1 = XEXP (XEXP (operands[1],0),1); ! 413: if (GET_CODE (temp1) != MULT) ! 414: abort (); ! 415: } ! 416: ! 417: if (GET_CODE (temp0) == MEM) ! 418: addreg1 = temp0; ! 419: else if (GET_CODE (temp0) == REG) ! 420: addreg1 = temp0; ! 421: else ! 422: abort (); ! 423: ! 424: break; ! 425: ! 426: case CNSTOP: ! 427: /* Since this machine is big-endian, ! 428: the late half must be the low-order word for an integer, ! 429: or the latter word for a float. */ ! 430: if (GET_CODE (operands[1]) == CONST_DOUBLE) ! 431: { ! 432: if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_FLOAT) ! 433: { ! 434: latehalf[1] = gen_rtx (CONST_INT, VOIDmode, ! 435: CONST_DOUBLE_HIGH (operands[1])); ! 436: operands[1] = gen_rtx (CONST_INT, VOIDmode, ! 437: CONST_DOUBLE_LOW (operands[1])); ! 438: } ! 439: else ! 440: { ! 441: latehalf[1] = gen_rtx (CONST_INT, VOIDmode, ! 442: CONST_DOUBLE_LOW (operands[1])); ! 443: operands[1] = gen_rtx (CONST_INT, VOIDmode, ! 444: CONST_DOUBLE_HIGH (operands[1])); ! 445: } ! 446: } ! 447: else ! 448: { ! 449: latehalf[1] = operands[1]; ! 450: operands[1] = const0_rtx; ! 451: } ! 452: break; ! 453: ! 454: case RNDOP: ! 455: default: ! 456: abort (); ! 457: } ! 458: ! 459: ! 460: /* double the register used for shifting in both of the operands */ ! 461: /* but make sure the same register isn't doubled twice! */ ! 462: ! 463: if (shftreg0 && shftreg1 && rtx_equal_p (shftreg0, shftreg1)) ! 464: output_asm_insn ("addl2 %0,%0", &shftreg0); ! 465: else ! 466: { ! 467: if (shftreg0) ! 468: output_asm_insn ("addl2 %0,%0", &shftreg0); ! 469: if (shftreg1) ! 470: output_asm_insn ("addl2 %0,%0", &shftreg1); ! 471: } ! 472: ! 473: /* if the destination is a register and that register is needed in */ ! 474: /* the source addressing mode, swap the order of the moves since we */ ! 475: /* don't want this destroyed til last. If both regs are used, not */ ! 476: /* much we can do, so abort. If these becomes a problem, maybe we */ ! 477: /* can do it on the stack? */ ! 478: ! 479: if (GET_CODE (operands[0])==REG && regisused (operands[1],REGNO (operands[0]))) ! 480: if (regisused (latehalf[1],REGNO (latehalf[0]))) ! 481: 8; ! 482: else ! 483: dohighfirst++; ! 484: ! 485: /* if we're pushing, do the high address part first. */ ! 486: ! 487: if (dohighfirst) ! 488: { ! 489: ! 490: if (addreg0 && addreg1 && (rtx_equal_p (addreg0,addreg1))) ! 491: output_asm_insn ("addl2 $4,%0", &addreg0); ! 492: else ! 493: { ! 494: if (addreg0) ! 495: output_asm_insn ("addl2 $4,%0", &addreg0); ! 496: if (addreg1) ! 497: output_asm_insn ("addl2 $4,%0", &addreg1); ! 498: } ! 499: ! 500: output_asm_insn (singlemove_string (latehalf), latehalf); ! 501: ! 502: if (addreg0 && addreg1 && (rtx_equal_p (addreg0,addreg1))) ! 503: output_asm_insn ("subl2 $4,%0", &addreg0); ! 504: else ! 505: { ! 506: if (addreg0) ! 507: output_asm_insn ("subl2 $4,%0", &addreg0); ! 508: if (addreg1) ! 509: output_asm_insn ("subl2 $4,%0", &addreg1); ! 510: } ! 511: ! 512: return singlemove_string (operands); ! 513: } ! 514: ! 515: output_asm_insn (singlemove_string (operands), operands); ! 516: ! 517: if (addreg0 && addreg1 && (rtx_equal_p (addreg0,addreg1))) ! 518: output_asm_insn ("addl2 $4,%0", &addreg0); ! 519: else ! 520: { ! 521: if (addreg0) ! 522: output_asm_insn ("addl2 $4,%0", &addreg0); ! 523: if (addreg1) ! 524: output_asm_insn ("addl2 $4,%0", &addreg1); ! 525: } ! 526: ! 527: output_asm_insn (singlemove_string (latehalf), latehalf); ! 528: ! 529: if (addreg0 && addreg1 && (rtx_equal_p (addreg0,addreg1))) ! 530: output_asm_insn ("subl2 $4,%0", &addreg0); ! 531: else ! 532: { ! 533: if (addreg0) ! 534: output_asm_insn ("subl2 $4,%0", &addreg0); ! 535: if (addreg1) ! 536: output_asm_insn ("subl2 $4,%0", &addreg1); ! 537: } ! 538: ! 539: if (shftreg0 && shftreg1 && (rtx_equal_p (shftreg0,shftreg1))) ! 540: output_asm_insn ("shar $1,%0,%0", &shftreg0); ! 541: else ! 542: { ! 543: if (shftreg0) ! 544: output_asm_insn ("shar $1,%0,%0", &shftreg0); ! 545: if (shftreg1) ! 546: output_asm_insn ("shar $1,%0,%0", &shftreg1); ! 547: } ! 548: ! 549: return ""; ! 550: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.