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