|
|
1.1 ! root 1: /* Subroutines for insn-output.c for Pyramid 90 Series. ! 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: /* Some output-actions in pyr.md need these. */ ! 21: #include <stdio.h> ! 22: extern FILE *asm_out_file; ! 23: #include "tree.h" ! 24: ! 25: /* ! 26: * Do FUNCTION_ARG. ! 27: * This cannot be defined as a macro on pyramids, because Pyramid Technology's ! 28: * C compiler dies on (several equivalent definitions of) this macro. ! 29: * The only way around this cc bug was to make this a function. ! 30: * While it would be possible to use a macro version for gcc, it seems ! 31: * more reliable to have a single version of the code. ! 32: */ ! 33: void * ! 34: pyr_function_arg(cum, mode, type, named) ! 35: CUMULATIVE_ARGS cum; ! 36: enum machine_mode mode; ! 37: tree type; ! 38: { ! 39: return (void *)(FUNCTION_ARG_HELPER (cum, mode,type,named)); ! 40: } ! 41: ! 42: /* Do the hard part of PARAM_SAFE_FOR_REG_P. ! 43: * This cannot be defined as a macro on pyramids, because Pyramid Technology's ! 44: * C compiler dies on (several equivalent definitions of) this macro. ! 45: * The only way around this cc bug was to make this a function. ! 46: */ ! 47: int ! 48: inner_param_safe_helper (type) ! 49: tree type; ! 50: { ! 51: return (INNER_PARAM_SAFE_HELPER(type)); ! 52: } ! 53: ! 54: ! 55: /* Return 1 if OP is a non-indexed operand of mode MODE. ! 56: This is either a register reference, a memory reference, ! 57: or a constant. In the case of a memory reference, the address ! 58: is checked to make sure it isn't indexed. ! 59: ! 60: Register and memory references must have mode MODE in order to be valid, ! 61: but some constants have no machine mode and are valid for any mode. ! 62: ! 63: If MODE is VOIDmode, OP is checked for validity for whatever mode ! 64: it has. ! 65: ! 66: The main use of this function is as a predicate in match_operand ! 67: expressions in the machine description. ! 68: ! 69: It is useful to compare this with general_operand(). They should ! 70: be identical except for one line. ! 71: ! 72: This function seems necessary because of the non-orthogonality of ! 73: Pyramid insns. ! 74: For any 2-operand insn, and any combination of operand modes, ! 75: if indexing is valid for the isn's second operand, it is invalid ! 76: for the first operand to be indexed. */ ! 77: ! 78: extern int volatile_ok; ! 79: ! 80: int ! 81: nonindexed_operand(op, mode) ! 82: register rtx op; ! 83: enum machine_mode mode; ! 84: { ! 85: register enum rtx_code code = GET_CODE (op); ! 86: int mode_altering_drug = 0; ! 87: ! 88: if (mode == VOIDmode) ! 89: mode = GET_MODE (op); ! 90: ! 91: if (CONSTANT_P (op)) ! 92: return ((GET_MODE (op) == VOIDmode || GET_MODE (op) == mode) ! 93: && LEGITIMATE_CONSTANT_P (op)); ! 94: ! 95: /* Except for certain constants with VOIDmode, already checked for, ! 96: OP's mode must match MODE if MODE specifies a mode. */ ! 97: ! 98: if (GET_MODE (op) != mode) ! 99: return 0; ! 100: ! 101: while (code == SUBREG) ! 102: { ! 103: op = SUBREG_REG (op); ! 104: code = GET_CODE (op); ! 105: #if 0 ! 106: /* No longer needed, since (SUBREG (MEM...)) ! 107: will load the MEM into a reload reg in the MEM's own mode. */ ! 108: mode_altering_drug = 1; ! 109: #endif ! 110: } ! 111: if (code == REG) ! 112: return 1; ! 113: if (code == CONST_DOUBLE) ! 114: return LEGITIMATE_CONSTANT_P (op); ! 115: if (code == MEM) ! 116: { ! 117: register rtx y = XEXP (op, 0); ! 118: if (! volatile_ok && MEM_VOLATILE_P (op)) ! 119: return 0; ! 120: GO_IF_NONINDEXED_ADDRESS (y, win); ! 121: } ! 122: return 0; ! 123: ! 124: win: ! 125: if (mode_altering_drug) ! 126: return ! mode_dependent_address_p (XEXP (op, 0)); ! 127: return 1; ! 128: } ! 129: ! 130: int ! 131: has_direct_base (op) ! 132: rtx op; ! 133: { ! 134: if ((GET_CODE (op) == PLUS ! 135: && (CONSTANT_ADDRESS_P (XEXP (op, 1)) ! 136: || CONSTANT_ADDRESS_P (XEXP (op, 0)))) ! 137: || CONSTANT_ADDRESS_P (op)) ! 138: return 1; ! 139: ! 140: return 0; ! 141: } ! 142: ! 143: int ! 144: has_index (op) ! 145: rtx op; ! 146: { ! 147: if (GET_CODE (op) == PLUS ! 148: && (GET_CODE (XEXP (op, 0)) == MULT ! 149: || (GET_CODE (XEXP (op, 1)) == MULT))) ! 150: return 1; ! 151: else ! 152: return 0; ! 153: } ! 154: ! 155: int swap_operands; ! 156: ! 157: /* weird_memory_memory -- return 1 if OP1 and OP2 can be compared (or ! 158: exchanged with xchw) with one instruction. If the operands need to ! 159: be swapped, set the global variable SWAP_OPERANDS. This function ! 160: silently assumes that both OP0 and OP1 are valid memory references. ! 161: */ ! 162: ! 163: int ! 164: weird_memory_memory (op0, op1) ! 165: rtx op0, op1; ! 166: { ! 167: int ret; ! 168: int c; ! 169: enum rtx_code code0, code1; ! 170: ! 171: op0 = XEXP (op0, 0); ! 172: op1 = XEXP (op1, 0); ! 173: code0 = GET_CODE (op0); ! 174: code1 = GET_CODE (op1); ! 175: ! 176: swap_operands = 0; ! 177: ! 178: if (code1 == REG) ! 179: { ! 180: return 1; ! 181: } ! 182: if (code0 == REG) ! 183: { ! 184: swap_operands = 1; ! 185: return 1; ! 186: } ! 187: if (has_direct_base (op0) && has_direct_base (op1)) ! 188: { ! 189: if (has_index (op1)) ! 190: { ! 191: if (has_index (op0)) ! 192: return 0; ! 193: swap_operands = 1; ! 194: } ! 195: ! 196: return 1; ! 197: } ! 198: return 0; ! 199: } ! 200: ! 201: int ! 202: signed_comparison (x, mode) ! 203: rtx x; ! 204: enum machine_mode mode; ! 205: { ! 206: enum rtx_code code = GET_CODE (x); ! 207: ! 208: return (code == NE || code == EQ || code == GE || code == GT || code == LE ! 209: || code == LT); ! 210: } ! 211: ! 212: char * ! 213: output_branch (code) ! 214: enum rtx_code code; ! 215: { ! 216: switch (code) ! 217: { ! 218: case NE: return "bne %l4"; ! 219: case EQ: return "beq %l4"; ! 220: case GE: return "bge %l4"; ! 221: case GT: return "bgt %l4"; ! 222: case LE: return "ble %l4"; ! 223: case LT: return "blt %l4"; ! 224: } ! 225: } ! 226: ! 227: char * ! 228: output_inv_branch (code) ! 229: enum rtx_code code; ! 230: { ! 231: switch (code) ! 232: { ! 233: case NE: return "beq %l4"; ! 234: case EQ: return "bne %l4"; ! 235: case GE: return "ble %l4"; ! 236: case GT: return "blt %l4"; ! 237: case LE: return "bge %l4"; ! 238: case LT: return "bgt %l4"; ! 239: } ! 240: } ! 241: ! 242: extern rtx force_reg (); ! 243: rtx test_op0, test_op1; ! 244: ! 245: rtx ! 246: ensure_extended (op, extop) ! 247: rtx op; ! 248: enum rtx_code extop; ! 249: { ! 250: if (GET_MODE (op) == HImode || GET_MODE (op) == QImode) ! 251: op = gen_rtx (extop, SImode, op); ! 252: op = force_reg (SImode, op); ! 253: return op; ! 254: } ! 255: ! 256: /* Sign-extend or zero-extend constant X from FROM_MODE to TO_MODE. */ ! 257: ! 258: rtx ! 259: extend_const (x, extop, from_mode, to_mode) ! 260: rtx x; ! 261: enum rtx_code extop; ! 262: enum machine_mode from_mode, to_mode; ! 263: { ! 264: int val = INTVAL (x); ! 265: int negative = val & (1 << (GET_MODE_BITSIZE (from_mode) - 1)); ! 266: if (from_mode == to_mode) ! 267: return x; ! 268: if (GET_MODE_BITSIZE (from_mode) == HOST_BITS_PER_INT) ! 269: abort (); ! 270: if (negative && extop == SIGN_EXTEND) ! 271: val = val | ((-1) << (GET_MODE_BITSIZE (from_mode))); ! 272: else ! 273: val = val & ~((-1) << (GET_MODE_BITSIZE (from_mode))); ! 274: if (GET_MODE_BITSIZE (to_mode) == HOST_BITS_PER_INT) ! 275: return gen_rtx (CONST_INT, VOIDmode, val); ! 276: return gen_rtx (CONST_INT, VOIDmode, ! 277: val & ~((-1) << (GET_MODE_BITSIZE (to_mode)))); ! 278: } ! 279: ! 280: /* Emit rtl for a branch, as well as any delayed (integer) compare insns. ! 281: The compare insn to perform is determined by the global variables ! 282: test_op0 and test_op1. */ ! 283: ! 284: void ! 285: extend_and_branch (extop) ! 286: enum rtx_code extop; ! 287: { ! 288: rtx op0, op1; ! 289: enum rtx_code code0, code1; ! 290: ! 291: op0 = test_op0, op1 = test_op1; ! 292: if (op0 == 0) ! 293: return; ! 294: ! 295: code0 = GET_CODE (op0); ! 296: if (op1 != 0) ! 297: code1 = GET_CODE (op1); ! 298: test_op0 = test_op1 = 0; ! 299: ! 300: if (op1 == 0) ! 301: { ! 302: op0 = ensure_extended (op0, extop); ! 303: emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx, op0)); ! 304: } ! 305: else ! 306: { ! 307: if (CONSTANT_P (op0) && CONSTANT_P (op1)) ! 308: { ! 309: op0 = force_reg (SImode, op0); ! 310: op1 = force_reg (SImode, op1); ! 311: } ! 312: else if (extop == ZERO_EXTEND && GET_MODE (op0) == HImode) ! 313: { ! 314: /* Pyramids have no unsigned "cmphi" instructions. We need to ! 315: zero extend unsigned halfwords into temporary registers. */ ! 316: op0 = ensure_extended (op0, extop); ! 317: op1 = ensure_extended (op1, extop); ! 318: } ! 319: else if (CONSTANT_P (op0)) ! 320: { ! 321: op0 = extend_const (op0, extop, GET_MODE (op1), SImode); ! 322: op1 = ensure_extended (op1, extop); ! 323: } ! 324: else if (CONSTANT_P (op1)) ! 325: { ! 326: op1 = extend_const (op1, extop, GET_MODE (op0), SImode); ! 327: op0 = ensure_extended (op0, extop); ! 328: } ! 329: else if (code0 == REG && code1 == REG) ! 330: { ! 331: /* I could do this case without extension, by using the virtual ! 332: register address (but that would lose for global regs). */ ! 333: op0 = ensure_extended (op0, extop); ! 334: op1 = ensure_extended (op1, extop); ! 335: } ! 336: else if (code0 == MEM && code1 == MEM) ! 337: { ! 338: /* Load into a reg if the address combination can't be handled ! 339: directly. */ ! 340: if (! weird_memory_memory (op0, op1)) ! 341: op0 = force_reg (GET_MODE (op0), op0); ! 342: } ! 343: ! 344: emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx, ! 345: gen_rtx (COMPARE, VOIDmode, op0, op1))); ! 346: } ! 347: } ! 348: ! 349: /* Return non-zero if the two single-word operations with operands[0] ! 350: and operands[1] for the first single-word operation, and operands[2] ! 351: and operands[3] for the second single-word operation, is possible to ! 352: combine to a double word operation. ! 353: ! 354: The criterion is whether the operands are in consecutive memory cells, ! 355: registers, etc. */ ! 356: ! 357: int ! 358: movdi_possible (operands) ! 359: rtx operands[]; ! 360: { ! 361: int cnst_diff0, cnst_diff1; ! 362: ! 363: cnst_diff0 = consecutive_operands (operands[0], operands[2]); ! 364: if (cnst_diff0 == 0) ! 365: return 0; ! 366: ! 367: cnst_diff1 = consecutive_operands (operands[1], operands[3]); ! 368: if (cnst_diff0 & cnst_diff1) ! 369: { ! 370: if (cnst_diff0 & 1) ! 371: swap_operands = 0; ! 372: else ! 373: swap_operands = 1; ! 374: return 1; ! 375: } ! 376: return 0; ! 377: } ! 378: ! 379: /* Return +1 of OP0 is a consecutive operand to OP1, -1 if OP1 is a ! 380: consecutive operand to OP0. ! 381: ! 382: This function is used to determine if addresses are consecutive, ! 383: and therefore possible to combine to fewer instructions. */ ! 384: ! 385: int ! 386: consecutive_operands (op0, op1) ! 387: rtx op0, op1; ! 388: { ! 389: enum rtx_code code0, code1; ! 390: int cnst_diff; ! 391: ! 392: code0 = GET_CODE (op0); ! 393: code1 = GET_CODE (op1); ! 394: ! 395: if (CONSTANT_P (op0) && CONSTANT_P (op1)) ! 396: { ! 397: if (op0 == const0_rtx) ! 398: if (op1 == const0_rtx) ! 399: return 3; ! 400: else ! 401: return 2; ! 402: if (op1 == const0_rtx) ! 403: return 1; ! 404: } ! 405: ! 406: if (code0 != code1) ! 407: return 0; ! 408: ! 409: if (code0 == REG) ! 410: { ! 411: cnst_diff = REGNO (op0) - REGNO (op1); ! 412: if (cnst_diff == 1) ! 413: return 1; ! 414: else if (cnst_diff == -1) ! 415: return 2; ! 416: } ! 417: else if (code0 == MEM) ! 418: { ! 419: cnst_diff = radr_diff (XEXP (op0, 0), XEXP (op1, 0)); ! 420: if (cnst_diff) ! 421: if (cnst_diff == 4) ! 422: return 1; ! 423: else if (cnst_diff == -4) ! 424: return 2; ! 425: } ! 426: return 0; ! 427: } ! 428: ! 429: /* Return the constant difference of the rtx expressions OP0 and OP1, ! 430: or 0 if the y don't have a constant difference. ! 431: ! 432: This function is used to determine if addresses are consecutive, ! 433: and therefore possible to combine to fewer instructions. */ ! 434: ! 435: int ! 436: radr_diff (op0, op1) ! 437: rtx op0, op1; ! 438: { ! 439: enum rtx_code code0, code1; ! 440: int cnst_diff; ! 441: ! 442: code0 = GET_CODE (op0); ! 443: code1 = GET_CODE (op1); ! 444: ! 445: if (code0 != code1) ! 446: { ! 447: if (code0 == PLUS) ! 448: { ! 449: if (GET_CODE (XEXP (op0, 1)) == CONST_INT ! 450: && rtx_equal_p (op1, XEXP (op0, 0))) ! 451: return INTVAL (XEXP (op0, 1)); ! 452: } ! 453: else if (code1 == PLUS) ! 454: { ! 455: if (GET_CODE (XEXP (op1, 1)) == CONST_INT ! 456: && rtx_equal_p (op0, XEXP (op1, 0))) ! 457: return -INTVAL (XEXP (op1, 1)); ! 458: } ! 459: return 0; ! 460: } ! 461: ! 462: if (code0 == CONST_INT) ! 463: return INTVAL (op0) - INTVAL (op1); ! 464: ! 465: if (code0 == PLUS) ! 466: { ! 467: cnst_diff = radr_diff (XEXP (op0, 0), XEXP (op1, 0)); ! 468: if (cnst_diff) ! 469: return (rtx_equal_p (XEXP (op0, 1), XEXP (op1, 1))) ! 470: ? cnst_diff : 0; ! 471: cnst_diff = radr_diff (XEXP (op0, 1), XEXP (op1, 1)); ! 472: if (cnst_diff) ! 473: return (rtx_equal_p (XEXP (op0, 0), XEXP (op1, 0))) ! 474: ? cnst_diff : 0; ! 475: } ! 476: ! 477: return 0; ! 478: } ! 479: ! 480: int ! 481: already_sign_extended (insn, from_mode, op) ! 482: rtx insn; ! 483: enum machine_mode from_mode; ! 484: rtx op; ! 485: { ! 486: rtx xinsn; ! 487: ! 488: return 0; ! 489: ! 490: #if 0 ! 491: for (;;) ! 492: { ! 493: insn = PREV_INSN (insn); ! 494: if (insn == 0) ! 495: return 0; ! 496: if (GET_CODE (insn) == NOTE) ! 497: continue; ! 498: if (GET_CODE (insn) != INSN) ! 499: return 0; ! 500: xinsn = PATTERN (insn); ! 501: ! 502: if (GET_CODE (xinsn) != SET) ! 503: return 0; ! 504: ! 505: /* Is it another register that is set in this insn? */ ! 506: if (GET_CODE (SET_DEST (xinsn)) != REG ! 507: || REGNO (SET_DEST (xinsn)) != REGNO (op)) ! 508: continue; ! 509: ! 510: if (GET_CODE (SET_SRC (xinsn)) == SIGN_EXTEND ! 511: || (GET_CODE (SET_SRC (xinsn)) == MEM ! 512: && GET_MODE (SET_SRC (xinsn)) == from_mode)) ! 513: return 1; ! 514: ! 515: /* Is the register modified by another operation? */ ! 516: if (REGNO (SET_DEST (xinsn)) == REGNO (op)) ! 517: return 0; ! 518: } ! 519: #endif ! 520: } ! 521: ! 522: char * ! 523: output_move_double (operands) ! 524: rtx *operands; ! 525: { ! 526: CC_STATUS_INIT; ! 527: if (GET_CODE (operands[1]) == CONST_DOUBLE) ! 528: { ! 529: if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT) ! 530: { ! 531: /* In an integer, the low-order word is in CONST_DOUBLE_LOW. */ ! 532: rtx const_op = operands[1]; ! 533: if (CONST_DOUBLE_HIGH (const_op) == 0) ! 534: { ! 535: operands[1] = gen_rtx (CONST_INT, VOIDmode, ! 536: CONST_DOUBLE_LOW (const_op)); ! 537: return "movl %1,%0"; ! 538: } ! 539: operands[1] = gen_rtx (CONST_INT, VOIDmode, ! 540: CONST_DOUBLE_HIGH (const_op)); ! 541: output_asm_insn ("movw %1,%0", operands); ! 542: operands[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1); ! 543: operands[1] = gen_rtx (CONST_INT, VOIDmode, ! 544: CONST_DOUBLE_LOW (const_op)); ! 545: return "movw %1,%0"; ! 546: } ! 547: else ! 548: { ! 549: /* In a real, the low-address word is in CONST_DOUBLE_LOW. */ ! 550: rtx const_op = operands[1]; ! 551: if (CONST_DOUBLE_LOW (const_op) == 0) ! 552: { ! 553: operands[1] = gen_rtx (CONST_INT, VOIDmode, ! 554: CONST_DOUBLE_HIGH (const_op)); ! 555: return "movl %1,%0"; ! 556: } ! 557: operands[1] = gen_rtx (CONST_INT, VOIDmode, ! 558: CONST_DOUBLE_LOW (const_op)); ! 559: output_asm_insn ("movw %1,%0", operands); ! 560: operands[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1); ! 561: operands[1] = gen_rtx (CONST_INT, VOIDmode, ! 562: CONST_DOUBLE_HIGH (const_op)); ! 563: return "movw %1,%0"; ! 564: } ! 565: } ! 566: ! 567: return "movl %1,%0"; ! 568: } ! 569: ! 570: /* Return non-zero if the code of this rtx pattern is a relop. */ ! 571: int ! 572: relop (op, mode) ! 573: rtx op; ! 574: enum machine_mode mode; ! 575: { ! 576: switch (GET_CODE (op)) ! 577: { ! 578: case EQ: ! 579: case NE: ! 580: case LT: ! 581: case LE: ! 582: case GE: ! 583: case GT: ! 584: case LTU: ! 585: case LEU: ! 586: case GEU: ! 587: case GTU: ! 588: return 1; ! 589: } ! 590: return 0; ! 591: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.