|
|
1.1 root 1: /* Output routines for GCC for Hitachi Super-H 1.1.1.3 ! root 2: Copyright (C) 1993, 1994 Free Software Foundation, Inc. 1.1 root 3: 1.1.1.3 ! root 4: This file is part of GNU CC. 1.1 root 5: 1.1.1.3 ! root 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. */ 1.1 root 19: 20: 21: /* Contributed by Steve Chamberlain ([email protected]) */ 22: 23: #include <stdio.h> 24: #include "assert.h" 25: #include "config.h" 26: #include "rtl.h" 27: #include "regs.h" 28: #include "hard-reg-set.h" 29: #include "real.h" 30: #include "insn-config.h" 31: #include "conditions.h" 32: #include "insn-flags.h" 33: #include "tree.h" 34: #include "output.h" 1.1.1.3 ! root 35: 1.1 root 36: #include "insn-attr.h" 37: #include "flags.h" 38: #include "obstack.h" 39: #include "expr.h" 40: 1.1.1.3 ! root 41: static rtx add_constant (); 1.1 root 42: 1.1.1.3 ! root 43: int pragma_interrupt; ! 44: int pragma_trapa; 1.1 root 45: 46: int current_function_anonymous_args; 47: extern int current_function_pretend_args_size; 1.1.1.2 root 48: extern char *version_string; 49: extern int flag_traditional; 50: 1.1.1.3 ! root 51: static rtx shiftsyms[32]; ! 52: struct rtx_def *table_lab; 1.1.1.2 root 53: enum attr_cpu sh_cpu; /* target cpu */ 1.1 root 54: 55: /* Global variables for machine-dependent things. */ 56: 57: /* Saved operands from the last compare to use when we generate an scc 1.1.1.3 ! root 58: or bcc insn. */ 1.1 root 59: 60: rtx sh_compare_op0; 61: rtx sh_compare_op1; 62: 63: /* Provides the class number of the smallest class containing 64: reg number */ 65: 66: int regno_reg_class[FIRST_PSEUDO_REGISTER] = 67: { 68: R0_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, 69: GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, 70: GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, 71: GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, 1.1.1.3 ! root 72: GENERAL_REGS, PR_REGS, T_REGS, NO_REGS, ! 73: MAC_REGS, MAC_REGS, 1.1 root 74: }; 75: 76: /* Provide reg_class from a letter such as appears in the machine 77: description. */ 78: 79: enum reg_class reg_class_from_letter[] = 80: { 81: /* a */ NO_REGS, /* b */ NO_REGS, /* c */ NO_REGS, /* d */ NO_REGS, 82: /* e */ NO_REGS, /* f */ NO_REGS, /* g */ NO_REGS, /* h */ NO_REGS, 83: /* i */ NO_REGS, /* j */ NO_REGS, /* k */ NO_REGS, /* l */ PR_REGS, 84: /* m */ NO_REGS, /* n */ NO_REGS, /* o */ NO_REGS, /* p */ NO_REGS, 85: /* q */ NO_REGS, /* r */ NO_REGS, /* s */ NO_REGS, /* t */ T_REGS, 86: /* u */ NO_REGS, /* v */ NO_REGS, /* w */ NO_REGS, /* x */ MAC_REGS, 87: /* y */ NO_REGS, /* z */ R0_REGS 88: }; 89: 1.1.1.3 ! root 90: /* Value is 1 if register/mode pair is acceptable on SH. Even ! 91: registers can hold DIs and DF values. The rest can only hold ! 92: SI's efficiently */ ! 93: ! 94: ! 95: #define REG_ODD \ ! 96: ( (1 << (int) QImode) | (1 << (int) HImode) | (1 << (int) SImode) \ ! 97: | (1 << (int) QFmode) | (1 << (int) HFmode) | (1 << (int) SFmode) \ ! 98: | (1 << (int) CQImode) | (1 << (int) CHImode)| (1<< (int)DFmode) | (1<<(int)DImode)) 1.1 root 99: 1.1.1.3 ! root 100: #define REG_EVEN \ ! 101: (REG_ODD | (1 << (int) CSImode) | (1 << (int) SCmode)) 1.1.1.2 root 102: 1.1.1.3 ! root 103: #define SI_ONLY (1<<(int)SImode) ! 104: ! 105: int hard_regno_mode_ok[] = ! 106: { ! 107: REG_EVEN, REG_ODD, REG_EVEN, REG_ODD, ! 108: REG_EVEN, REG_ODD, REG_EVEN, REG_ODD, ! 109: REG_EVEN, REG_ODD, REG_EVEN, REG_ODD, ! 110: REG_EVEN, REG_ODD, REG_EVEN, REG_ODD, ! 111: REG, 0, SI_ONLY, SI_ONLY, ! 112: SI_ONLY, SI_ONLY ! 113: }; 1.1.1.2 root 114: 1.1 root 115: /* Local label counter, used for constants in the pool and inside 116: pattern branches. */ 117: static int lf = 100; 118: 1.1.1.3 ! root 119: ! 120: /* Number of bytes pushed for anonymous args, used to pass information ! 121: between expand_prologue and expand_epilogue. */ ! 122: static int extra_push; 1.1 root 123: 124: 1.1.1.3 ! root 125: 1.1.1.2 root 126: void 127: push (rn) 1.1.1.3 ! root 128: int rn; 1.1 root 129: { 1.1.1.3 ! root 130: rtx x ; ! 131: x= emit_insn (gen_push (gen_rtx (REG, SImode, rn))); ! 132: REG_NOTES (x) = gen_rtx (EXPR_LIST, REG_INC, ! 133: gen_rtx(REG, SImode, STACK_POINTER_REGNUM), 0); 1.1 root 134: } 135: 1.1.1.2 root 136: void 137: pop (rn) 1.1.1.3 ! root 138: int rn; 1.1 root 139: { 1.1.1.3 ! root 140: rtx x; ! 141: x = emit_insn (gen_pop (gen_rtx (REG, SImode, rn))); ! 142: REG_NOTES (x) = gen_rtx (EXPR_LIST, REG_INC, ! 143: gen_rtx(REG, SImode, STACK_POINTER_REGNUM), 0); 1.1 root 144: } 145: 146: 1.1.1.2 root 147: /* Adjust the stack and return the number of bytes taken to do it */ 1.1.1.3 ! root 148: static rtx lastreg; ! 149: int lastval; 1.1.1.2 root 150: static void 1.1.1.3 ! root 151: output_stack_adjust (size) 1.1.1.2 root 152: int size; 1.1 root 153: { 1.1.1.2 root 154: if (size) 1.1 root 155: { 1.1.1.2 root 156: rtx val = GEN_INT (size); 157: rtx insn; 1.1 root 158: 1.1.1.3 ! root 159: if (!CONST_OK_FOR_I (size)) 1.1.1.2 root 160: { 1.1.1.3 ! root 161: lastreg = gen_rtx (REG, SImode, 3); ! 162: lastval = size; ! 163: emit_insn (gen_movsi (lastreg, val)); ! 164: val = lastreg; 1.1 root 165: 1.1.1.3 ! root 166: } 1.1 root 167: 1.1.1.3 ! root 168: insn = gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, val); 1.1.1.2 root 169: emit_insn (insn); 170: } 1.1 root 171: } 172: 173: 1.1.1.2 root 174: /* Generate code to push the regs specified in the mask, and return 175: the number of bytes the insns take. */ 1.1 root 176: 177: static void 1.1.1.2 root 178: push_regs (mask) 179: int mask; 1.1 root 180: { 181: int i; 182: 1.1.1.2 root 183: for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) 1.1 root 184: { 1.1.1.2 root 185: if (mask & (1 << i)) 1.1 root 186: { 1.1.1.2 root 187: push (i); 1.1 root 188: } 189: } 190: } 191: 192: 1.1.1.3 ! root 193: /* Print an instruction which would have gone into a delay slot after ! 194: an instructiuon, but couldn't because the instruction expanded into a ! 195: sequence where putting the slot insn at the end wouldn't work. */ 1.1 root 196: 1.1.1.3 ! root 197: static void 1.1.1.2 root 198: print_slot (insn) 199: rtx insn; 200: { 201: final_scan_insn (XVECEXP (insn, 0, 1), asm_out_file, optimize, 0, 1); 1.1 root 202: 1.1.1.2 root 203: INSN_DELETED_P (XVECEXP (insn, 0, 1)) = 1; 1.1 root 204: } 205: 206: 207: /* Work out the registers which need to be saved, both as a mask and a 1.1.1.3 ! root 208: count. 1.1 root 209: 1.1.1.3 ! root 210: If doing a pragma interrupt function, then push all regs used by the function, ! 211: and if we call another function (we can tell by looking at PR), make sure that all the ! 212: regs it clobbers are safe too. ! 213: */ ! 214: static int ! 215: calc_live_regs (count_ptr) ! 216: int *count_ptr; 1.1 root 217: { 218: int reg; 219: int live_regs_mask = 0; 1.1.1.3 ! root 220: int count = 0; 1.1 root 221: for (reg = 0; reg < FIRST_PSEUDO_REGISTER; reg++) 222: { 1.1.1.3 ! root 223: if (reg == ARG_POINTER_REGNUM) ! 224: continue; ! 225: if (reg == T_REG) ! 226: continue; ! 227: if (reg == GBR_REG) ! 228: continue; ! 229: ! 230: if (pragma_interrupt && !pragma_trapa) ! 231: { ! 232: /* Need to save all the regs ever live */ ! 233: if ((regs_ever_live[reg] ! 234: || (call_used_regs[reg] && regs_ever_live[PR_REG])) ! 235: && reg != 15) ! 236: { ! 237: live_regs_mask |= 1 << reg; ! 238: count++; ! 239: } ! 240: } ! 241: else if (TARGET_SMALLCALL) 1.1 root 242: { 1.1.1.3 ! root 243: /* Don't need to push anthing, but count the regs which have ! 244: been pushed by the wrapper */ ! 245: if (call_used_regs[reg]) ! 246: count++; ! 247: } ! 248: else ! 249: { ! 250: /* Only push those regs which are used and need to be saved */ ! 251: if (regs_ever_live[reg] && !call_used_regs[reg]) ! 252: { ! 253: count++; ! 254: live_regs_mask |= (1 << reg); ! 255: } 1.1 root 256: } 257: } 1.1.1.3 ! root 258: ! 259: ! 260: *count_ptr = count; 1.1 root 261: return live_regs_mask; 262: } 1.1.1.2 root 263: 1.1 root 264: 1.1.1.2 root 265: static int 266: need_slot (insn) 267: rtx insn; 1.1 root 268: { 1.1.1.2 root 269: return (insn && !INSN_ANNULLED_BRANCH_P (XVECEXP (insn, 0, 0))); 1.1 root 270: } 1.1.1.2 root 271: 1.1 root 272: /* Print the operand address in x to the stream */ 273: 274: void 275: print_operand_address (stream, x) 276: FILE *stream; 277: rtx x; 278: { 279: switch (GET_CODE (x)) 280: { 281: case REG: 282: fprintf (stream, "@%s", reg_names[REGNO (x)]); 283: break; 284: case PLUS: 285: { 286: rtx base = XEXP (x, 0); 287: rtx index = XEXP (x, 1); 288: 289: if (GET_CODE (base) != REG) 290: { 291: /* Ensure that BASE is a register (one of them must be). */ 292: rtx temp = base; 293: base = index; 294: index = temp; 295: } 296: 297: switch (GET_CODE (index)) 298: { 299: case CONST_INT: 300: fprintf (stream, "@(%d,%s)", 301: INTVAL (index), 302: reg_names[REGNO (base)]); 303: break; 304: 305: case REG: 1.1.1.2 root 306: fprintf (stream, "@(r0,%s)", 307: reg_names[MAX (REGNO (base), REGNO (index))]); 308: 1.1 root 309: break; 310: 311: default: 1.1.1.2 root 312: debug_rtx (x); 1.1 root 313: abort (); 314: } 315: } 316: 317: break; 318: case PRE_DEC: 319: fprintf (stream, "@-%s", reg_names[REGNO (XEXP (x, 0))]); 320: break; 321: 322: case POST_INC: 323: fprintf (stream, "@%s+", reg_names[REGNO (XEXP (x, 0))]); 324: break; 325: 326: default: 327: output_addr_const (stream, x); 328: break; 329: } 330: } 331: 332: /* Print operand x (an rtx) in assembler syntax to file stream 333: according to modifier code. 334: 1.1.1.2 root 335: '.' print a .s if insn needs delay slot 336: '*' print a local label 337: '^' increment the local label number 338: '!' dump the constant table 339: '#' output a nop if there is nothing to put in the delay slot 1.1.1.3 ! root 340: '@' print rte or rts depending upon pragma interruptness 1.1.1.2 root 341: 'R' print the next register or memory location along, ie the lsw in 342: a double word value 343: 'O' print a constant without the # 344: 'M' print a constant as its negative 1.1.1.3 ! root 345: 'N' print insides of a @++ or @-- o */ 1.1 root 346: 347: void 348: print_operand (stream, x, code) 349: FILE *stream; 350: rtx x; 351: int code; 352: { 353: switch (code) 354: { 1.1.1.2 root 355: case '.': 356: if (need_slot (final_sequence)) 357: fprintf (stream, ".s"); 358: break; 1.1 root 359: case '*': 360: fprintf (stream, "LF%d", lf); 361: break; 362: case '^': 363: lf++; 364: break; 1.1.1.3 ! root 365: case '@': ! 366: if (pragma_interrupt) ! 367: fprintf (stream, "rte"); ! 368: else ! 369: fprintf (stream, "rts"); ! 370: break; 1.1 root 371: case '#': 372: /* Output a nop if there's nothing in the delay slot */ 373: if (dbr_sequence_length () == 0) 374: { 1.1.1.3 ! root 375: fprintf (stream, "\n\tnop"); 1.1 root 376: } 377: break; 1.1.1.2 root 378: case 'O': 1.1.1.3 ! root 379: output_addr_const (stream, x); 1.1 root 380: break; 1.1.1.2 root 381: case 'M': 382: fprintf (asm_out_file, "#%d", -INTVAL (x)); 383: break; 1.1.1.3 ! root 384: case 'N': ! 385: fputs (reg_names[REGNO (XEXP (XEXP (x, 0), 0))], (stream)); ! 386: break; 1.1 root 387: case 'R': 1.1.1.3 ! root 388: /* Next location along in memory or register */ 1.1 root 389: switch (GET_CODE (x)) 390: { 391: case REG: 392: fputs (reg_names[REGNO (x) + 1], (stream)); 393: break; 394: case MEM: 1.1.1.3 ! root 395: print_operand_address (stream, XEXP (adj_offsettable_operand (x, 4), 0)); 1.1 root 396: break; 397: } 398: break; 399: default: 400: switch (GET_CODE (x)) 401: { 402: case REG: 403: fputs (reg_names[REGNO (x)], (stream)); 404: break; 405: case MEM: 406: output_address (XEXP (x, 0)); 407: break; 408: default: 409: fputc ('#', stream); 410: output_addr_const (stream, x); 411: break; 412: } 413: break; 414: } 415: } 416: 417: 1.1.1.3 ! root 418: static int ! 419: sextb (x) ! 420: int x; ! 421: { ! 422: x &= 0xff; ! 423: if (x > 127) ! 424: { ! 425: x = -256 + x; ! 426: } ! 427: return x; ! 428: } ! 429: 1.1.1.2 root 430: 1.1 root 431: 1.1.1.3 ! root 432: /* Take a move with integer constant source in OPERANDS, see if it can be generated by ! 433: devious shifting. If so, generate the instruction sequence and return 1, otherwise ! 434: return 0. ! 435: ! 436: OPERANDS[0] Destination register ! 437: OPERANDS[1] Source constant ! 438: ! 439: 00000000 00000000 00000000 0NNNNNNNN simple load ! 440: 00000000 00000000 00000000 NNNNNNNN0 load and shift by 1 ! 441: 00000000 00000000 0000000N NNNNNNN00 load and shift by 2 ! 442: 00000000 00000000 0NNNNNNN 000000000 load and shift by 8 ! 443: 00000000 0NNNNNNN 00000000 000000000 load and shift by 16 ! 444: N0000000 00000000 00000000 00NNNNNNN load and rotate right ! 445: ! 446: 11111111 11111111 11111111 1NNNNNNNN simple load ! 447: 11111111 11111111 11111111 NNNNNNNN0 load and shift by 1 ! 448: 11111111 11111111 1111111N NNNNNNN00 load and shift by 2 ! 449: 11111111 11111111 1NNNNNNN 000000000 load and shift by 8 ! 450: 11111111 1NNNNNNN 00000000 000000000 load and shift by 16 ! 451: N1111111 11111111 11111111 11NNNNNNN load and rotate right ! 452: ! 453: 00000000 00000000 00000000 1NNNNNNNN load and zero extend byte ! 454: 00000000 00000000 11111111 1NNNNNNNN load and zero extend word ! 455: ! 456: ! 457: */ ! 458: ! 459: static int ! 460: synth_constant (operands, mode) ! 461: rtx operands[]; ! 462: enum machine_mode mode; 1.1 root 463: { 1.1.1.3 ! root 464: rtx dst; ! 465: int i = INTVAL (operands[1]) & 0xffffffff; ! 466: ! 467: if (CONST_OK_FOR_I (i)) ! 468: return 0; 1.1 root 469: 1.1.1.3 ! root 470: if (TARGET_CLEN0 && mode != QImode) ! 471: return 0; 1.1 root 472: 1.1.1.3 ! root 473: if (mode != SImode) ! 474: { ! 475: if (reload_in_progress) ! 476: return 0; ! 477: dst = gen_reg_rtx (SImode); ! 478: } ! 479: else 1.1 root 480: { 1.1.1.3 ! root 481: dst = operands[0]; 1.1 root 482: } 483: 1.1.1.3 ! root 484: ! 485: /* 00000000 00000000 11111111 1NNNNNNNN load and zero extend word */ ! 486: if ((i & 0xffffff80) == 0x0000ff80) 1.1 root 487: { 1.1.1.3 ! root 488: emit_move_insn (dst, GEN_INT (sextb (i))); ! 489: emit_insn (gen_and_ffff (dst, dst)); ! 490: } ! 491: /* 00000000 00000000 00000000 1NNNNNNNN load and zero extend byte */ ! 492: else if ((i & 0xffffff80) == 0x00000080) ! 493: { ! 494: emit_move_insn (dst, GEN_INT (sextb (i))); ! 495: emit_insn (gen_and_ff (dst, dst)); ! 496: } ! 497: /* 00000000 00000000 00000000 NNNNNNNN0 load and shift by 1 ! 498: 11111111 11111111 11111111 NNNNNNNN0 load and shift by 1 */ ! 499: else if ((i & 0xffffff01) == 0 ! 500: || (i & 0xffffff01) == 0xffffff00) ! 501: { ! 502: emit_move_insn (dst, GEN_INT (sextb (i >> 1))); ! 503: emit_insn (gen_ashlsi3_n (dst, dst, GEN_INT (1))); ! 504: } ! 505: /* 00000000 00000000 0000000N NNNNNNN00 load and shift by 2 ! 506: 11111111 11111111 1111111N NNNNNNN00 load and shift by 2*/ ! 507: else if ((i & 0xfffffe03) == 0 ! 508: || (i & 0xfffffe03) == 0xfffffe00) ! 509: { ! 510: emit_move_insn (dst, GEN_INT (sextb (i >> 2))); ! 511: emit_insn (gen_ashlsi3_n (dst, dst, GEN_INT (2))); 1.1 root 512: } 1.1.1.3 ! root 513: /* 00000000 00000000 0NNNNNNN 000000000 load and shift by 8 ! 514: 11111111 11111111 1NNNNNNN 000000000 load and shift by 8 */ 1.1 root 515: 1.1.1.3 ! root 516: else if ((i & 0xffff80ff) == 0 ! 517: || (i & 0xffff80ff) == 0xffff8000) ! 518: { ! 519: emit_move_insn (dst, GEN_INT (sextb (i >> 8))); ! 520: emit_insn (gen_ashlsi3_n (dst, dst, GEN_INT (8))); ! 521: } ! 522: /* 00000000 0NNNNNNN 00000000 000000000 load and shift by 16 ! 523: 11111111 1NNNNNNN 00000000 000000000 load and shift by 16 */ ! 524: else if ((i & 0xff80ffff) == 0x00000000 ! 525: || (i & 0xff80ffff) == 0xff800000) ! 526: { ! 527: emit_move_insn (dst, GEN_INT (sextb (i >> 16))); ! 528: emit_insn (gen_ashlsi3_n (dst, dst, GEN_INT (16))); ! 529: } ! 530: /* 00000000 00000000 0NNNNNNN 0NNNNNNNN load shift 8 and add */ ! 531: else if ((i & 0xffff8080) == 0 && TARGET_CLEN3) 1.1 root 532: { 1.1.1.3 ! root 533: emit_move_insn (dst, GEN_INT (sextb (i >> 8))); ! 534: emit_insn (gen_ashlsi3_n (dst, dst, GEN_INT (8))); ! 535: emit_insn (gen_addsi3 (dst, dst, GEN_INT (i & 0x7f))); 1.1 root 536: } 1.1.1.3 ! root 537: else ! 538: return 0; ! 539: ! 540: if (mode == DImode) ! 541: { ! 542: /* Moving from SI to DI, we've got to zero out the high part */ ! 543: ! 544: emit_insn (gen_rtx (SET, VOIDmode, ! 545: gen_rtx (SUBREG, SImode, operands[0], 0), ! 546: dst)); ! 547: emit_insn (gen_rtx (SET, VOIDmode, ! 548: gen_rtx (SUBREG, SImode, operands[0], 1), ! 549: const0_rtx)); ! 550: ! 551: } ! 552: else if (mode != SImode) ! 553: { ! 554: emit_insn (gen_rtx (SET, VOIDmode, operands[0], ! 555: gen_rtx (SUBREG, mode, dst, 0))); ! 556: ! 557: } ! 558: return 1; 1.1 root 559: } 560: 561: 1.1.1.3 ! root 562: /* Emit code to perform a block move. Choose the best method. ! 563: ! 564: OPERANDS[0] is the destination. ! 565: OPERANDS[1] is the source. ! 566: OPERANDS[2] is the size. ! 567: OPERANDS[3] is the alignment safe to use. */ ! 568: ! 569: ! 570: int ! 571: expand_block_move (operands) ! 572: rtx *operands; ! 573: { ! 574: int align = INTVAL (operands[3]); ! 575: int constp = (GET_CODE (operands[2]) == CONST_INT); ! 576: int bytes = (constp ? INTVAL (operands[2]) : 0); ! 577: enum machine_mode mode; ! 578: ! 579: /* IF odd then fail */ ! 580: if (!constp || bytes <= 0) ! 581: return 0; ! 582: ! 583: /* Don't expand if we'd make the code bigger and we don't want big code */ ! 584: ! 585: if (bytes > 8 && TARGET_SMALLCODE) ! 586: return 0; ! 587: ! 588: switch (align) ! 589: { ! 590: case 1: ! 591: mode = QImode; ! 592: break; ! 593: case 2: ! 594: mode = HImode; ! 595: break; ! 596: default: ! 597: mode = SImode; ! 598: align = 4; ! 599: } ! 600: ! 601: if (mode == SImode && constp && bytes < 64 && (bytes % 4 == 0)) ! 602: { ! 603: char entry[30]; ! 604: tree entry_name; ! 605: rtx func_addr_rtx; ! 606: rtx r4 = gen_rtx (REG, SImode, 4); ! 607: rtx r5 = gen_rtx (REG, SImode, 5); ! 608: sprintf (entry, "__movstr%s%d", GET_MODE_NAME (mode), bytes); ! 609: entry_name = get_identifier (entry); ! 610: ! 611: func_addr_rtx = copy_to_mode_reg (Pmode, ! 612: gen_rtx (SYMBOL_REF, Pmode, IDENTIFIER_POINTER (entry_name))); ! 613: emit_insn (gen_move_insn (r4, XEXP (operands[0], 0))); ! 614: emit_insn (gen_move_insn (r5, XEXP (operands[1], 0))); ! 615: emit_insn (gen_block_move_real (func_addr_rtx)); ! 616: return 1; ! 617: } ! 618: if (mode == SImode && constp && (bytes % 4 == 0)) ! 619: { ! 620: tree entry_name; ! 621: rtx func_addr_rtx; ! 622: rtx r4 = gen_rtx (REG, SImode, 4); ! 623: rtx r5 = gen_rtx (REG, SImode, 5); ! 624: rtx r6 = gen_rtx (REG, SImode, 6); ! 625: entry_name = get_identifier ("__movstr"); ! 626: ! 627: func_addr_rtx = copy_to_mode_reg (Pmode, ! 628: gen_rtx (SYMBOL_REF, Pmode, ! 629: IDENTIFIER_POINTER (entry_name))); ! 630: emit_insn (gen_move_insn (r4, XEXP (operands[0], 0))); ! 631: emit_insn (gen_move_insn (r5, XEXP (operands[1], 0))); ! 632: ! 633: /* r6 controls the size of the move, 16 is decremented from it ! 634: for each 64 bytes moved, then the -ve bit is used as an index into a ! 635: list of move instructions like this: ! 636: ! 637: { ! 638: do { ! 639: *dst++ = *src++; ! 640: *dst++ = *src++; ! 641: *dst++ = *src++; ! 642: ..etc.. 16 in all ! 643: *dst++ = *src++; ! 644: *dst++ = *src++; ! 645: size -= 16; ! 646: } while (size > 0); ! 647: ! 648: switch (size) ! 649: { ! 650: case -15: ! 651: *dst++ = *src++; ! 652: case -14: ! 653: *dst++ = *src++; ! 654: .. etc.. ; ! 655: case -2: ! 656: *dst++ = *src++; ! 657: case -1: ! 658: *dst++ = *src++; ! 659: case 0: ! 660: ; ! 661: } ! 662: } ! 663: ! 664: eg, a 72 byte move would be set up with size(r6) = 14, for one ! 665: iteration through the big while loop, and a switch of -2 for the last part */ ! 666: ! 667: { ! 668: int final_switch = 16 - ((bytes / 4) % 16); ! 669: int while_loop = ((bytes / 4) / 16 - 1) * 16; ! 670: emit_insn (gen_move_insn (r6, GEN_INT (while_loop + final_switch))); ! 671: emit_insn (gen_block_lump_real (func_addr_rtx)); ! 672: return 1; ! 673: } ! 674: } ! 675: ! 676: return 0; ! 677: } ! 678: 1.1 root 679: /* Prepare operands for a move define_expand; specifically, one of the 1.1.1.2 root 680: operands must be in a register. Take this chance to remove 681: addressing modes which can't be coped with very well. */ 1.1 root 682: 1.1.1.2 root 683: int 1.1 root 684: prepare_move_operands (operands, mode) 685: rtx operands[]; 686: enum machine_mode mode; 687: { 1.1.1.3 ! root 688: if (!(reload_in_progress || reload_completed) ! 689: && ((!register_operand (operands[0], mode) ! 690: && !register_operand (operands[1], mode)) ! 691: || GET_CODE (operands[1]) == PLUS)) 1.1 root 692: { 693: /* copy the source to a register */ 694: operands[1] = copy_to_mode_reg (mode, operands[1]); 695: } 1.1.1.3 ! root 696: if ((mode == SImode || mode == HImode || mode == QImode) ! 697: && GET_CODE (operands[1]) == CONST_INT) ! 698: { ! 699: return synth_constant (operands, mode); ! 700: } ! 701: if (mode == DFmode || mode == DImode) 1.1.1.2 root 702: { 1.1.1.3 ! root 703: rtx src = operands[1]; ! 704: rtx dst = operands[0]; ! 705: rtx insns; 1.1.1.2 root 706: 1.1.1.3 ! root 707: if (src == dst) 1.1.1.2 root 708: { 1.1.1.3 ! root 709: emit_insn (gen_rtx (SET, VOIDmode, dst, src)); ! 710: return 1; ! 711: } 1.1.1.2 root 712: 1.1.1.3 ! root 713: if (GET_CODE (src) == REG && ! 714: REGNO (src) >= FIRST_PSEUDO_REGISTER) ! 715: return 0; ! 716: ! 717: if (GET_CODE (dst) == REG && ! 718: REGNO (dst) >= FIRST_PSEUDO_REGISTER) ! 719: return 0; ! 720: ! 721: if (push_operand (dst, mode)) ! 722: return 0; ! 723: ! 724: if (GET_CODE (src) == CONST_DOUBLE) ! 725: src = force_const_mem (DFmode, src); ! 726: ! 727: if (reload_in_progress) ! 728: { ! 729: if (!(offsettable_memref_p (src) || register_operand (src, mode))) ! 730: return 0; ! 731: if (!(offsettable_memref_p (dst) || register_operand (dst, ! 732: mode))) ! 733: return 0; ! 734: } ! 735: start_sequence (); ! 736: if (GET_CODE (operands[0]) != REG ! 737: || !refers_to_regno_p (REGNO (operands[0]), REGNO (operands[0]) + 1, operands[1], 0)) ! 738: { ! 739: emit_move_insn (operand_subword (dst, 0, 1, mode), ! 740: operand_subword_force (src, 0, mode)); ! 741: emit_move_insn (operand_subword (dst, 1, 1, mode), ! 742: operand_subword_force (src, 1, mode)); ! 743: } ! 744: else ! 745: { ! 746: emit_move_insn (operand_subword (dst, 1, 1, mode), ! 747: operand_subword_force (src, 1, mode)); ! 748: emit_move_insn (operand_subword (dst, 0, 1, mode), ! 749: operand_subword_force (src, 0, mode)); 1.1.1.2 root 750: } 1.1.1.3 ! root 751: ! 752: insns = get_insns (); ! 753: end_sequence (); ! 754: ! 755: emit_no_conflict_block (insns, dst, src, 0, src); ! 756: return 1; 1.1.1.2 root 757: } 1.1.1.3 ! root 758: 1.1.1.2 root 759: return 0; 1.1 root 760: } 761: 762: /* Prepare the operands for an scc instruction; make sure that the 763: compare has been done. */ 764: rtx 765: prepare_scc_operands (code) 1.1.1.3 ! root 766: int code; 1.1 root 767: { 1.1.1.2 root 768: if (GET_CODE (sh_compare_op0) != REG 769: || REGNO (sh_compare_op0) != T_REG) 1.1 root 770: { 1.1.1.3 ! root 771: int newcode = code; 1.1 root 772: /* First need a compare insn */ 1.1.1.3 ! root 773: switch (code) ! 774: { ! 775: case NE: ! 776: newcode = EQ; ! 777: break; ! 778: case LT: ! 779: newcode = GT; ! 780: break; ! 781: case LE: ! 782: newcode = GE; ! 783: break; ! 784: case LTU: ! 785: newcode = GTU; ! 786: break; ! 787: case LEU: ! 788: newcode = GEU; ! 789: break; ! 790: } ! 791: if (newcode != code) ! 792: { ! 793: rtx tmp = sh_compare_op0; ! 794: sh_compare_op0 = sh_compare_op1; ! 795: sh_compare_op1 = tmp; ! 796: code = newcode; ! 797: } ! 798: ! 799: sh_compare_op0 = force_reg (SImode, sh_compare_op0); ! 800: emit_insn (gen_rtx (SET, VOIDmode, 1.1 root 801: gen_rtx (REG, SImode, T_REG), 1.1.1.3 ! root 802: gen_rtx (code, SImode, sh_compare_op0, sh_compare_op1))); 1.1 root 803: } 1.1.1.2 root 804: return gen_rtx (REG, SImode, T_REG); 1.1 root 805: } 806: 1.1.1.2 root 807: 1.1.1.3 ! root 808: /* Functions to output assembly code. */ 1.1 root 809: 1.1.1.2 root 810: /* Return a sequence of instructions to perform DI or DF move. 1.1 root 811: 1.1.1.2 root 812: Since the SH cannot move a DI or DF in one instruction, we have 813: to take care when we see overlapping source and dest registers. 814: 815: */ 1.1.1.3 ! root 816: 1.1 root 817: char * 1.1.1.3 ! root 818: output_movedouble (insn, operands, mode) ! 819: rtx insn; 1.1 root 820: rtx operands[]; 821: enum machine_mode mode; 822: { 1.1.1.2 root 823: rtx dst = operands[0]; 824: rtx src = operands[1]; 825: 1.1.1.3 ! root 826: /* fprintf (asm_out_file, "! move double \n"); ! 827: fprintf (asm_out_file, "! pc %04x\n", insn_addresses[INSN_UID (insn)]);*/ ! 828: if (GET_CODE (dst) == MEM ! 829: && GET_CODE (XEXP (dst, 0)) == POST_INC) ! 830: { ! 831: operands[0] = XEXP (XEXP (dst, 0), 0); ! 832: return "mov.l %R1,@(4,%0)\n\tmov.l %1,@%0\n\tadd #8,%0"; ! 833: } 1.1.1.2 root 834: if (register_operand (dst, mode) 835: && register_operand (src, mode)) 1.1 root 836: { 1.1.1.2 root 837: if (REGNO (src) == MACH_REG) 1.1 root 838: return "sts mach,%0\n\tsts macl,%R0"; 839: 1.1.1.2 root 840: /* 1.1.1.3 ! root 841: when mov.d r1,r2 do r2->r3 then r1->r2 ! 842: when mov.d r1,r0 do r1->r0 then r2->r1 ! 843: */ 1.1.1.2 root 844: 845: if (REGNO (src) + 1 == REGNO (dst)) 1.1.1.3 ! root 846: return "mov %R1,%R0\n\tmov %1,%0 ! cra"; 1.1.1.2 root 847: else 1.1.1.3 ! root 848: return "mov %1,%0\n\tmov %R1,%R0 ! crb"; 1.1.1.2 root 849: } 850: else if (GET_CODE (src) == CONST_INT) 1.1 root 851: { 1.1.1.3 ! root 852: HOST_WIDE_INT val = INTVAL (src); ! 853: int rn = REGNO (operands[0]); ! 854: if (val < 0) ! 855: { ! 856: fprintf (asm_out_file, "\tmov #-1,r%d\n", rn); ! 857: } 1.1 root 858: else 1.1.1.3 ! root 859: { ! 860: fprintf (asm_out_file, "\tmov #0,r%d\n", rn); ! 861: } 1.1 root 862: 1.1.1.3 ! root 863: fprintf (asm_out_file, "\tmov #%d,r%d\n", val, rn + 1); ! 864: return ""; ! 865: } 1.1.1.2 root 866: else if (GET_CODE (src) == MEM) 1.1 root 867: { 1.1.1.2 root 868: int ptrreg1 = -1; 869: int ptrreg2 = -1; 870: int dreg = REGNO (dst); 871: rtx inside = XEXP (src, 0); 1.1 root 872: 873: if (GET_CODE (inside) == REG) 1.1.1.2 root 874: { 875: ptrreg1 = REGNO (inside); 876: } 1.1 root 877: else if (GET_CODE (inside) == PLUS) 878: { 879: rtx lhs = XEXP (inside, 0); 880: rtx rhs = XEXP (inside, 1); 881: if (GET_CODE (lhs) == REG) 1.1.1.2 root 882: ptrreg1 = REGNO (lhs); 883: if (GET_CODE (rhs) == REG) 884: ptrreg2 = REGNO (rhs); 1.1 root 885: } 1.1.1.3 ! root 886: else if (GET_CODE (inside) == LABEL_REF) ! 887: { ! 888: return "mov.l %1,%0\n\tmov.l %1+4,%R0"; ! 889: } ! 890: else if (GET_CODE (inside) == POST_INC) ! 891: { ! 892: return "mov.l %1,%0\n\tmov.l %1,%R0 !mdi\n"; ! 893: } 1.1 root 894: else 895: abort (); 896: 1.1.1.2 root 897: if ((ptrreg1 >= 0 && ptrreg2 >= 0) 898: && (dreg == ptrreg1 899: || dreg == ptrreg2 900: || dreg + 1 == ptrreg1 901: || dreg + 1 == ptrreg2)) 902: { 903: /* This move clobbers both index registers, 904: calculate the sum in one register. */ 905: fprintf (asm_out_file, " add %s,%s ! special fix\n", 906: reg_names[ptrreg2], reg_names[ptrreg1]); 907: 908: if (dreg == ptrreg1) 909: { 910: /* Copy into dreg+1 first. */ 911: fprintf (asm_out_file, " mov.l @(4,%s),%s\n", 912: reg_names[ptrreg1], 913: reg_names[dreg + 1]); 914: 915: fprintf (asm_out_file, " mov.l @(%s),%s\n", 916: reg_names[ptrreg1], 917: reg_names[dreg]); 918: } 919: else 920: { 921: /* Copy into dreg first. */ 922: fprintf (asm_out_file, " mov.l @(%s),%s\n", 923: reg_names[ptrreg1], 924: reg_names[dreg]); 925: 926: fprintf (asm_out_file, " mov.l @(4,%s),%s\n", 927: reg_names[ptrreg1], 928: reg_names[dreg + 1]); 929: 930: } 931: warning ("generated complex amode"); 932: return ""; 933: } 934: 935: /* Work out the safe way to copy */ 936: if (dreg == ptrreg1) 1.1 root 937: { 1.1.1.2 root 938: /* Copy into the second half first */ 939: return "mov.l %R1,%R0\n\tmov.l %1,%0 ! cr"; 1.1 root 940: } 941: } 942: 1.1.1.2 root 943: return "mov.l %1,%0\n\tmov.l %R1,%R0"; 1.1 root 944: } 945: 946: /* Emit assembly to shift reg by k bits */ 947: 948: char * 1.1.1.2 root 949: output_shift (string, reg, k, code) 1.1 root 950: char *string; 951: rtx reg; 952: rtx k; 1.1.1.2 root 953: int code; 954: 1.1 root 955: { 956: int s = INTVAL (k); 1.1.1.3 ! root 957: if (s < 0) ! 958: { ! 959: s = -s; ! 960: switch (code) ! 961: { ! 962: case LSHIFTRT: ! 963: case ASHIFTRT: ! 964: code = ASHIFT; ! 965: break; ! 966: case ASHIFT: ! 967: code = ASHIFTRT; ! 968: break; ! 969: default: ! 970: abort (); ! 971: } ! 972: } 1.1.1.2 root 973: if (code == ASHIFT && s == 31) 974: { 975: /* Shift left by 31 moving into the t bit, clearing and rotating the other way */ 976: 977: fprintf (asm_out_file, "\trotr r%d\n", REGNO (reg)); 978: fprintf (asm_out_file, "\tmov #0,r%d\n", REGNO (reg)); 979: fprintf (asm_out_file, "\trotcr r%d\n", REGNO (reg)); 980: s = 0; 981: } 982: 983: if (code == LSHIFTRT && s == 31) 984: { 985: fprintf (asm_out_file, "\trotl r%d\n", REGNO (reg)); 986: fprintf (asm_out_file, "\tmov #0,r%d\n", REGNO (reg)); 987: fprintf (asm_out_file, "\trotcl r%d\n", REGNO (reg)); 988: s = 0; 989: } 990: 1.1 root 991: while (s) 992: { 993: char *out; 994: int d; 995: 996: if (s >= 16) 997: { 998: d = 16; 999: out = "16"; 1000: } 1001: else if (s >= 8) 1002: { 1003: d = 8; 1004: out = "8"; 1005: } 1006: else if (s >= 2) 1007: { 1008: d = 2; 1009: out = "2"; 1010: } 1011: else 1012: { 1013: d = 1; 1014: out = ""; 1015: } 1016: fprintf (asm_out_file, "\t%s%s\tr%d\n", string, out, REGNO (reg)); 1017: s -= d; 1018: } 1019: return ""; 1020: } 1021: 1.1.1.3 ! root 1022: ! 1023: void ! 1024: function_epilogue (stream, size) ! 1025: FILE *stream; ! 1026: int size; ! 1027: { ! 1028: pragma_interrupt = pragma_trapa = 0; ! 1029: } ! 1030: ! 1031: 1.1 root 1032: /* Return the text of the branch instruction which matches its length 1.1.1.2 root 1033: attribute. 1034: 1035: This gets tricky if we have an insn in the delay slot of a branch 1.1.1.3 ! root 1036: and the branch needs more than 1 insn to complete. */ ! 1037: ! 1038: int pending_const_table; ! 1039: ! 1040: /* We can't tell if we need a register as a scratch for the jump ! 1041: until after branch shortening, and then it's too late to allocate a ! 1042: register the 'proper' way. These instruction sequences are rare ! 1043: anyway, so to avoid always using a reg up from our limited set, we'll ! 1044: grab one when we need one on output. */ ! 1045: ! 1046: char * ! 1047: output_far_jump (insn, op) ! 1048: rtx insn; ! 1049: rtx op; ! 1050: { ! 1051: rtx thislab = gen_label_rtx (); ! 1052: ! 1053: if (dbr_sequence_length ()) ! 1054: { ! 1055: /* Something to go in what would have been the delay ! 1056: slot if this had been a short branch. Make sure the ! 1057: reg we use to generate the branch target address ! 1058: doesn't conflict */ 1.1.1.2 root 1059: 1.1.1.3 ! root 1060: int i; ! 1061: rtx vec[2]; ! 1062: vec[0] = thislab; ! 1063: ! 1064: for (i = 0; i < 8; i++) ! 1065: { ! 1066: vec[1] = gen_rtx (REG, SImode, i); ! 1067: if (!reg_referenced_p (vec[1], ! 1068: PATTERN (XVECEXP (final_sequence, 0, 1)))) ! 1069: break; ! 1070: } ! 1071: ! 1072: ! 1073: print_slot (final_sequence); ! 1074: output_asm_insn ("mov.l %1,@-r15", vec); ! 1075: output_asm_insn ("mov.l %O0,%1", vec); ! 1076: ! 1077: output_asm_insn ("jmp @%1 ! 32 xcond", vec); ! 1078: output_asm_insn ("mov.l @r15+,%1", vec); ! 1079: } ! 1080: else ! 1081: { ! 1082: output_asm_insn ("mov.l r13,@-r15", 0); ! 1083: output_asm_insn ("mov.l %O0,r13", &thislab); ! 1084: output_asm_insn ("jmp @r13 ! 32 zcond", 0); ! 1085: output_asm_insn ("mov.l @r15+,r13", 0); ! 1086: } 1.1.1.2 root 1087: 1.1.1.3 ! root 1088: output_asm_insn (".align 2", 0); ! 1089: ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", CODE_LABEL_NUMBER (thislab)); ! 1090: output_asm_insn (".long %O0", &op); ! 1091: return ""; ! 1092: } 1.1 root 1093: 1094: char * 1095: output_branch (logic, insn) 1096: int logic; 1.1.1.2 root 1097: rtx insn; 1.1 root 1098: { 1099: extern rtx recog_operand[]; 1100: int label = lf++; 1.1.1.3 ! root 1101: ! 1102: /* fprintf (asm_out_file, "! pc %04x\n", insn_addresses[INSN_UID (insn)]);*/ 1.1.1.2 root 1103: 1.1 root 1104: switch (get_attr_length (insn)) 1105: { 1106: case 2: 1107: /* Simple branch in range -200..+200 bytes */ 1.1.1.2 root 1108: return logic ? "bt%. %l0" : "bf%. %l0"; 1.1 root 1109: 1110: case 6: 1111: /* Branch in range -4000..+4000 bytes */ 1.1.1.2 root 1112: { 1113: rtx oldop = recog_operand[0]; 1114: 1115: 1116: if (need_slot (final_sequence)) 1117: { 1118: fprintf (asm_out_file, "\tb%c.s\tLF%d\n", logic ? 'f' : 't', 1119: label); 1120: 1121: print_slot (final_sequence); 1122: } 1123: 1124: else 1125: { 1126: fprintf (asm_out_file, "\tb%c\tLF%d\n", logic ? 'f' : 't', 1127: label); 1128: } 1129: recog_operand[0] = oldop; 1130: 1131: output_asm_insn ("bra %l0 ! 12 bit cond ", recog_operand); 1132: fprintf (asm_out_file, "\tor r0,r0\n"); 1133: fprintf (asm_out_file, "LF%d:\n", label); 1134: } 1.1 root 1135: return ""; 1.1.1.2 root 1136: 1.1.1.3 ! root 1137: case 16: 1.1.1.2 root 1138: /* Branches a long way away */ 1139: { 1140: rtx oldop = recog_operand[0]; 1141: 1142: if (need_slot (final_sequence)) 1143: { 1144: fprintf (asm_out_file, "\tb%c.s\tLF%d\n", logic ? 'f' : 't', label); 1145: print_slot (final_sequence); 1146: 1147: } 1148: else 1149: { 1150: fprintf (asm_out_file, "\tb%c\tLF%d\n", logic ? 'f' : 't', label); 1151: } 1152: 1.1.1.3 ! root 1153: output_far_jump (insn, oldop); 1.1.1.2 root 1154: fprintf (asm_out_file, "LF%d:\n", label); 1155: return ""; 1156: } 1.1 root 1157: } 1158: return "bad"; 1159: } 1160: 1.1.1.2 root 1161: 1.1.1.3 ! root 1162: /* The SH cannot load a large constant into a register, constants have to ! 1163: come from a pc relative load. The reference of a pc relative load ! 1164: instruction must be less than 1k infront of the instruction. This ! 1165: means that we often have to dump a constant inside a function, and ! 1166: generate code to branch around it. 1.1 root 1167: 1.1.1.3 ! root 1168: It is important to minimize this, since the branches will slow things ! 1169: down and make things bigger. 1.1.1.2 root 1170: 1.1.1.3 ! root 1171: Worst case code looks like: 1.1 root 1172: 1.1.1.3 ! root 1173: mov.l L1,rn ! 1174: bra L2 ! 1175: nop ! 1176: align ! 1177: L1: .long value ! 1178: L2: ! 1179: .. 1.1 root 1180: 1.1.1.3 ! root 1181: mov.l L3,rn ! 1182: bra L4 ! 1183: nop ! 1184: align ! 1185: L3: .long value ! 1186: L4: ! 1187: .. 1.1 root 1188: 1.1.1.3 ! root 1189: We fix this by performing a scan before scheduling, which notices which ! 1190: instructions need to have their operands fetched from the constant table ! 1191: and builds the table. 1.1 root 1192: 1193: 1.1.1.3 ! root 1194: The algorithm is: 1.1 root 1195: 1.1.1.3 ! root 1196: scan, find an instruction which needs a pcrel move. Look forward, find the ! 1197: last barrier which is within MAX_COUNT bytes of the requirement. ! 1198: If there isn't one, make one. Process all the instructions between ! 1199: the find and the barrier. 1.1 root 1200: 1201: In the above example, we can tell that L3 is within 1k of L1, so 1202: the first move can be shrunk from the 3 insn+constant sequence into 1203: just 1 insn, and the constant moved to L3 to make: 1204: 1.1.1.3 ! root 1205: mov.l L1,rn 1.1 root 1206: .. 1.1.1.3 ! root 1207: mov.l L3,rn ! 1208: bra L4 1.1 root 1209: nop 1210: align 1.1.1.3 ! root 1211: L3:.long value ! 1212: L4:.long value 1.1 root 1213: 1214: Then the second move becomes the target for the shortening process. 1215: 1.1.1.3 ! root 1216: */ 1.1 root 1217: 1218: typedef struct 1219: { 1.1.1.3 ! root 1220: rtx value; /* Value in table */ ! 1221: rtx label; /* Label of value */ ! 1222: enum machine_mode mode; /* Mode of value */ ! 1223: } ! 1224: ! 1225: pool_node; 1.1 root 1226: 1227: /* The maximum number of constants that can fit into one pool, since 1228: the pc relative range is 0...1020 bytes and constants are at least 4 1229: bytes long */ 1230: 1231: #define MAX_POOL_SIZE (1020/4) 1232: static pool_node pool_vector[MAX_POOL_SIZE]; 1233: static int pool_size; 1234: 1.1.1.3 ! root 1235: /* Add a constant to the pool and return its label. */ 1.1 root 1236: 1.1.1.3 ! root 1237: static rtx 1.1 root 1238: add_constant (x, mode) 1239: rtx x; 1240: enum machine_mode mode; 1241: { 1242: int i; 1.1.1.3 ! root 1243: rtx lab; 1.1 root 1244: /* First see if we've already got it */ 1245: 1246: for (i = 0; i < pool_size; i++) 1247: { 1248: if (x->code == pool_vector[i].value->code 1249: && mode == pool_vector[i].mode) 1250: { 1251: if (x->code == CODE_LABEL) 1252: { 1253: if (XINT (x, 3) != XINT (pool_vector[i].value, 3)) 1254: continue; 1255: } 1.1.1.3 ! root 1256: if (rtx_equal_p (x, pool_vector[i].value)) ! 1257: return pool_vector[i].label; 1.1 root 1258: } 1259: } 1.1.1.2 root 1260: 1.1.1.3 ! root 1261: /* Need a new one */ 1.1 root 1262: 1263: pool_vector[pool_size].value = x; 1.1.1.3 ! root 1264: lab = gen_label_rtx (); 1.1 root 1265: pool_vector[pool_size].mode = mode; 1.1.1.3 ! root 1266: pool_vector[pool_size].label = lab; 1.1 root 1267: pool_size++; 1.1.1.3 ! root 1268: return lab; 1.1 root 1269: } 1270: 1.1.1.3 ! root 1271: /* Dump out interesting debug info */ 1.1 root 1272: 1.1.1.3 ! root 1273: void ! 1274: final_prescan_insn (insn, opvec, noperands) 1.1 root 1275: rtx insn; 1.1.1.3 ! root 1276: rtx *opvec; ! 1277: int noperands; 1.1 root 1278: { 1.1.1.3 ! root 1279: if (target_flags & ISIZE_BIT) 1.1 root 1280: { 1.1.1.3 ! root 1281: extern int *insn_addresses; ! 1282: fprintf (asm_out_file, "\n! at %04x\n", ! 1283: insn_addresses[INSN_UID (insn)]); 1.1 root 1284: } 1285: } 1.1.1.3 ! root 1286: 1.1 root 1287: 1288: 1289: 1.1.1.3 ! root 1290: /* Stuff taken from m88k.c */ 1.1.1.2 root 1291: 1.1.1.3 ! root 1292: /* Output to FILE the start of the assembler file. */ 1.1 root 1293: 1.1.1.3 ! root 1294: struct option 1.1 root 1295: { 1.1.1.3 ! root 1296: char *string; ! 1297: int *variable; ! 1298: int on_value; ! 1299: }; 1.1.1.2 root 1300: 1.1.1.3 ! root 1301: static int ! 1302: output_option (file, sep, type, name, indent, pos, max) ! 1303: FILE *file; ! 1304: char *sep; ! 1305: char *type; ! 1306: char *name; ! 1307: char *indent; ! 1308: int pos; ! 1309: int max; ! 1310: { ! 1311: if (strlen (sep) + strlen (type) + strlen (name) + pos > max) 1.1 root 1312: { 1.1.1.3 ! root 1313: fprintf (file, indent); ! 1314: return fprintf (file, "%s%s", type, name); 1.1.1.2 root 1315: } 1.1.1.3 ! root 1316: return pos + fprintf (file, "%s%s%s", sep, type, name); ! 1317: } 1.1 root 1318: 1.1.1.3 ! root 1319: static struct ! 1320: { ! 1321: char *name; ! 1322: int value; ! 1323: } 1.1 root 1324: 1.1.1.3 ! root 1325: m_options[] = TARGET_SWITCHES; 1.1 root 1326: 1.1.1.3 ! root 1327: static void ! 1328: output_options (file, f_options, f_len, W_options, W_len, ! 1329: pos, max, sep, indent, term) ! 1330: FILE *file; ! 1331: struct option *f_options; ! 1332: struct option *W_options; ! 1333: int f_len, W_len; ! 1334: int pos; ! 1335: int max; ! 1336: char *sep; ! 1337: char *indent; ! 1338: char *term; ! 1339: { ! 1340: register int j; 1.1 root 1341: 1342: 1.1.1.3 ! root 1343: if (optimize) ! 1344: pos = output_option (file, sep, "-O", "", indent, pos, max); ! 1345: if (write_symbols != NO_DEBUG) ! 1346: pos = output_option (file, sep, "-g", "", indent, pos, max); ! 1347: if (flag_traditional) ! 1348: pos = output_option (file, sep, "-traditional", "", indent, pos, max); ! 1349: if (profile_flag) ! 1350: pos = output_option (file, sep, "-p", "", indent, pos, max); ! 1351: if (profile_block_flag) ! 1352: pos = output_option (file, sep, "-a", "", indent, pos, max); 1.1 root 1353: 1.1.1.3 ! root 1354: for (j = 0; j < f_len; j++) ! 1355: if (*f_options[j].variable == f_options[j].on_value) ! 1356: pos = output_option (file, sep, "-f", f_options[j].string, ! 1357: indent, pos, max); 1.1 root 1358: 1.1.1.3 ! root 1359: for (j = 0; j < W_len; j++) ! 1360: if (*W_options[j].variable == W_options[j].on_value) ! 1361: pos = output_option (file, sep, "-W", W_options[j].string, ! 1362: indent, pos, max); 1.1 root 1363: 1.1.1.3 ! root 1364: for (j = 0; j < sizeof m_options / sizeof m_options[0]; j++) ! 1365: if (m_options[j].name[0] != '\0' ! 1366: && m_options[j].value > 0 ! 1367: && ((m_options[j].value & target_flags) ! 1368: == m_options[j].value)) ! 1369: pos = output_option (file, sep, "-m", m_options[j].name, ! 1370: indent, pos, max); 1.1 root 1371: 1372: 1.1.1.3 ! root 1373: fprintf (file, term); ! 1374: fprintf (file, "! %d %d\n", max_count_si, max_count_hi); ! 1375: } ! 1376: ! 1377: void ! 1378: output_file_start (file, f_options, f_len, W_options, W_len) ! 1379: FILE *file; ! 1380: struct option *f_options; ! 1381: struct option *W_options; ! 1382: int f_len, W_len; ! 1383: { ! 1384: register int pos; ! 1385: ! 1386: output_file_directive (file, main_input_filename); ! 1387: ! 1388: /* Switch to the data section so that the coffsem symbol and the ! 1389: gcc2_compiled. symbol aren't in the text section. */ ! 1390: data_section (); ! 1391: ! 1392: ! 1393: pos = fprintf (file, "\n! Hitachi SH cc1 (%s) (release I-1) arguments:", version_string); ! 1394: output_options (file, f_options, f_len, W_options, W_len, ! 1395: pos, 75, " ", "\n! ", "\n\n"); ! 1396: } ! 1397: ! 1398: ! 1399: ! 1400: /* Return the cost of a shift */ ! 1401: ! 1402: int ! 1403: shiftcosts (RTX) ! 1404: rtx RTX; ! 1405: { ! 1406: /* If shift by a non constant, then this will be expensive. */ ! 1407: if (GET_CODE (XEXP (RTX, 1)) != CONST_INT) ! 1408: return 20; ! 1409: ! 1410: /* otherwise, it will be very cheap if by one of the constants ! 1411: we can cope with. */ ! 1412: if (CONST_OK_FOR_K (INTVAL (XEXP (RTX, 1)))) ! 1413: return 1; ! 1414: ! 1415: /* otherwise it will be several insns, but we pretend that it will be more than ! 1416: just the components, so that combine doesn't glue together a load of shifts into ! 1417: one shift which has to be emitted as a bunch anyway - breaking scheduling */ ! 1418: return 1; ! 1419: } 1.1.1.2 root 1420: 1421: int 1.1.1.3 ! root 1422: andcosts (RTX) ! 1423: rtx RTX; 1.1 root 1424: { 1425: int i; 1.1.1.3 ! root 1426: if (GET_CODE (XEXP (RTX, 1)) != CONST_INT) ! 1427: return 2; ! 1428: i = INTVAL (XEXP (RTX, 1)); ! 1429: /* And can use the extend insns cheaply */ ! 1430: if (i == 0xff || i == 0xffff) ! 1431: return 2; ! 1432: /* Any small constant is reasonably cheap - but requires r0 */ ! 1433: if (CONST_OK_FOR_I (i)) ! 1434: return 3; ! 1435: return 5; ! 1436: } 1.1.1.2 root 1437: 1.1.1.3 ! root 1438: int ! 1439: howshift (i) ! 1440: int i; ! 1441: { ! 1442: int total = 0; ! 1443: while (i > 0) 1.1 root 1444: { 1.1.1.3 ! root 1445: if (i >= 16) ! 1446: { ! 1447: total++; ! 1448: i -= 16; ! 1449: } ! 1450: else if (i >= 8) ! 1451: { ! 1452: total++; ! 1453: i -= 8; ! 1454: } ! 1455: else if (i >= 2) ! 1456: { ! 1457: total++; ! 1458: i -= 2; ! 1459: } ! 1460: else if (i >= 1) ! 1461: { ! 1462: total++; ! 1463: i--; ! 1464: } ! 1465: } ! 1466: return total; ! 1467: } 1.1.1.2 root 1468: 1.1.1.3 ! root 1469: /* Return the cost of a multiply */ ! 1470: int ! 1471: multcosts (RTX) ! 1472: rtx RTX; ! 1473: { ! 1474: /* If mult by a power of 2 then work out how we'd shift to make it */ ! 1475: int insn_cost = 0; ! 1476: ! 1477: if (GET_CODE (XEXP (RTX, 1)) == CONST_INT) ! 1478: { ! 1479: int i = exact_log2 (INTVAL (XEXP (RTX, 1))); ! 1480: if (i >= 0) ! 1481: insn_cost = howshift (i); ! 1482: else ! 1483: insn_cost = 100000; ! 1484: } ! 1485: if (TARGET_SH2) ! 1486: { ! 1487: /* We have a mul insn, so we can never take more than the mul and the ! 1488: read of the mac reg, but count more because of the latency and extra reg ! 1489: usage */ ! 1490: if (TARGET_SMALLCODE) ! 1491: return 2; ! 1492: if (insn_cost > 5) ! 1493: return 5; ! 1494: return insn_cost; ! 1495: } ! 1496: ! 1497: /* If we we're aiming at small code, then just count the number of ! 1498: insns in a multiply call sequence */ ! 1499: ! 1500: if (TARGET_SMALLCODE) ! 1501: { ! 1502: if (insn_cost > 6) ! 1503: return 6; ! 1504: return insn_cost; ! 1505: } ! 1506: ! 1507: /* Otherwise count all the insns in the routine we'd be calling too */ ! 1508: return 20; ! 1509: } ! 1510: ! 1511: /* Code to expand a shift */ ! 1512: ! 1513: void ! 1514: gen_ashift (type, n, reg) ! 1515: int type; ! 1516: int n; ! 1517: rtx reg; ! 1518: { ! 1519: switch (type) ! 1520: { ! 1521: case ASHIFTRT: ! 1522: emit_insn (gen_ashrsi3_k (reg, reg, GEN_INT (n))); ! 1523: break; ! 1524: case LSHIFTRT: ! 1525: emit_insn (gen_lshrsi3_k (reg, reg, GEN_INT (n))); ! 1526: break; ! 1527: case ASHIFT: ! 1528: if (n == 1) ! 1529: emit_insn (gen_addsi3 (reg, reg, reg)); ! 1530: else ! 1531: emit_insn (gen_ashlsi3_k (reg, reg, GEN_INT (n))); ! 1532: break; ! 1533: } ! 1534: } ! 1535: ! 1536: int ! 1537: gen_shifty_op (code, operands) ! 1538: int code; ! 1539: rtx *operands; ! 1540: { ! 1541: rtx wrk = gen_reg_rtx (SImode); ! 1542: rtx t; ! 1543: char *func; ! 1544: if (GET_CODE (operands[2]) == CONST_INT) ! 1545: { ! 1546: int value = INTVAL (operands[2]); ! 1547: top: ! 1548: switch (code) 1.1.1.2 root 1549: { 1.1.1.3 ! root 1550: case ASHIFTRT: ! 1551: if (value < 0) ! 1552: { ! 1553: code = ASHIFT; ! 1554: value = -value; ! 1555: goto top; ! 1556: } ! 1557: ! 1558: /* Expand a short sequence inline, longer call a magic routine */ ! 1559: if (value <= 5) ! 1560: { ! 1561: emit_move_insn (wrk, operands[1]); ! 1562: while (value--) ! 1563: { ! 1564: gen_ashift (ASHIFTRT, 1, wrk); ! 1565: } ! 1566: emit_move_insn (operands[0], wrk); ! 1567: return 1; ! 1568: } ! 1569: t = gen_reg_rtx (Pmode); ! 1570: /* Load the value into an arg reg and call a helper */ ! 1571: emit_move_insn (gen_rtx (REG, SImode, 4), operands[1]); ! 1572: if (!shiftsyms[value]) ! 1573: { ! 1574: func = xmalloc (18); ! 1575: sprintf (func, "__ashiftrt_r4_%d", value); ! 1576: shiftsyms[value] = gen_rtx (SYMBOL_REF, Pmode, func); ! 1577: } ! 1578: emit_move_insn (t, shiftsyms[value]); ! 1579: emit_insn (gen_ashrsi3_n (GEN_INT (value), t)); ! 1580: emit_move_insn (operands[0], gen_rtx (REG, SImode, 4)); ! 1581: return 1; 1.1.1.2 root 1582: 1.1.1.3 ! root 1583: case ASHIFT: ! 1584: if (value < 0) ! 1585: { ! 1586: code = LSHIFTRT; ! 1587: value = -value; ! 1588: goto top; ! 1589: } ! 1590: /* Fall through */ ! 1591: case LSHIFTRT: 1.1.1.2 root 1592: 1.1.1.3 ! root 1593: if (value < 0) 1.1.1.2 root 1594: { 1.1.1.3 ! root 1595: code = ASHIFT; ! 1596: value = -value; ! 1597: goto top; 1.1.1.2 root 1598: } 1599: 1.1.1.3 ! root 1600: emit_move_insn (wrk, operands[1]); ! 1601: while (value) 1.1.1.2 root 1602: { 1.1.1.3 ! root 1603: if (value >= 16) ! 1604: { ! 1605: gen_ashift (code, 16, wrk); ! 1606: value -= 16; ! 1607: } ! 1608: else if (value >= 8) ! 1609: { ! 1610: gen_ashift (code, 8, wrk); ! 1611: value -= 8; ! 1612: } ! 1613: else if (value >= 2) ! 1614: { ! 1615: gen_ashift (code, 2, wrk); ! 1616: value -= 2; ! 1617: } ! 1618: else ! 1619: { ! 1620: gen_ashift (code, 1, wrk); ! 1621: value--; ! 1622: } 1.1.1.2 root 1623: } 1.1.1.3 ! root 1624: emit_move_insn (operands[0], wrk); ! 1625: return 1; 1.1.1.2 root 1626: 1.1 root 1627: } 1628: } 1.1.1.3 ! root 1629: return 0; ! 1630: } 1.1.1.2 root 1631: 1.1.1.3 ! root 1632: /* Dump out any constants accumulated in the final pass - ! 1633: which will only be labels */ ! 1634: char * ! 1635: output_jump_label_table () ! 1636: { ! 1637: int i; ! 1638: if (pool_size) ! 1639: { ! 1640: fprintf (asm_out_file, "\t.align 2\n"); ! 1641: for (i = 0; i < pool_size; i++) ! 1642: { ! 1643: pool_node *p = pool_vector + i; 1.1.1.2 root 1644: 1.1.1.3 ! root 1645: ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", CODE_LABEL_NUMBER (p->label)); ! 1646: output_asm_insn (".long %O0", &p->value); ! 1647: } ! 1648: pool_size = 0; ! 1649: } 1.1.1.2 root 1650: 1.1.1.3 ! root 1651: return ""; 1.1 root 1652: } 1.1.1.3 ! root 1653: /* Output the literal table */ 1.1 root 1654: 1.1.1.3 ! root 1655: static void ! 1656: dump_table (scan) ! 1657: rtx scan; ! 1658: { ! 1659: int i; ! 1660: int need_align = 1; 1.1 root 1661: 1662: 1.1.1.3 ! root 1663: /* Do two passes, first time dump out the HI sized constants */ 1.1 root 1664: 1.1.1.3 ! root 1665: for (i = 0; i < pool_size; i++) ! 1666: { ! 1667: pool_node *p = pool_vector + i; ! 1668: if (p->mode == HImode) ! 1669: { ! 1670: if (need_align) ! 1671: { ! 1672: scan = emit_insn_after (gen_align_2 (), scan); ! 1673: need_align = 0; ! 1674: } ! 1675: scan = emit_label_after (p->label, scan); ! 1676: scan = emit_insn_after (gen_consttable_2 (p->value), scan); ! 1677: } ! 1678: } ! 1679: need_align = 1; 1.1 root 1680: 1.1.1.3 ! root 1681: for (i = 0; i < pool_size; i++) 1.1 root 1682: { 1.1.1.3 ! root 1683: pool_node *p = pool_vector + i; 1.1 root 1684: 1.1.1.3 ! root 1685: switch (p->mode) ! 1686: { ! 1687: case HImode: ! 1688: break; ! 1689: case SImode: ! 1690: if (need_align) ! 1691: { ! 1692: need_align = 0; ! 1693: scan = emit_label_after (gen_label_rtx (), scan); ! 1694: scan = emit_insn_after (gen_align_4 (), scan); ! 1695: } ! 1696: scan = emit_label_after (p->label, scan); ! 1697: scan = emit_insn_after (gen_consttable_4 (p->value), scan); ! 1698: break; ! 1699: case DImode: ! 1700: if (need_align) ! 1701: { ! 1702: need_align = 0; ! 1703: scan = emit_label_after (gen_label_rtx (), scan); ! 1704: scan = emit_insn_after (gen_align_4 (), scan); ! 1705: } ! 1706: scan = emit_label_after (p->label, scan); ! 1707: scan = emit_insn_after (gen_consttable_8 (p->value), scan); ! 1708: break; ! 1709: default: ! 1710: abort (); ! 1711: break; ! 1712: } 1.1.1.2 root 1713: } 1714: 1.1.1.3 ! root 1715: scan = emit_insn_after (gen_consttable_end (), scan); ! 1716: scan = emit_barrier_after (scan); ! 1717: pool_size = 0; ! 1718: } ! 1719: 1.1.1.2 root 1720: 1721: 1.1.1.3 ! root 1722: /* Non zero if the src operand needs to be fixed up */ ! 1723: static ! 1724: int ! 1725: fixit (src, mode) ! 1726: rtx src; ! 1727: enum machine_mode mode; ! 1728: { ! 1729: if (mode == QImode) ! 1730: return 0; /* QIs never need to be fixed */ ! 1731: if (GET_CODE (src) == CONST) ! 1732: return 1; 1.1.1.2 root 1733: 1.1.1.3 ! root 1734: if (GET_CODE (src) == SYMBOL_REF) 1.1 root 1735: { 1.1.1.3 ! root 1736: return 1; 1.1 root 1737: } 1.1.1.3 ! root 1738: if (GET_CODE (src) == LABEL_REF) ! 1739: { ! 1740: return 1; ! 1741: } ! 1742: if (GET_CODE (src) == CONST_INT) ! 1743: { ! 1744: /* All QI insns are ok */ ! 1745: if (mode == QImode) ! 1746: return 1; ! 1747: /* The rest may need to be fixed */ ! 1748: return !CONST_OK_FOR_I (INTVAL (src)); ! 1749: } ! 1750: return 0; 1.1 root 1751: } 1752: 1.1.1.3 ! root 1753: /* Return Non-zero if constant would be an ok source for a ! 1754: mov.w instead of a mov.l */ ! 1755: int ! 1756: hi_const (src) ! 1757: rtx src; 1.1 root 1758: { 1.1.1.3 ! root 1759: if (GET_CODE (src) == CONST ! 1760: && GET_CODE (XEXP (src, 0)) == SIGN_EXTEND ! 1761: && GET_CODE (XEXP (XEXP (src, 0), 0)) == SYMBOL_REF) ! 1762: return 1; 1.1 root 1763: 1.1.1.3 ! root 1764: if (TARGET_SHORTADDR ! 1765: && GET_CODE (src) == SYMBOL_REF) ! 1766: return 1; 1.1 root 1767: 1.1.1.3 ! root 1768: return (GET_CODE (src) == CONST_INT ! 1769: && INTVAL (src) >= -32768 ! 1770: && INTVAL (src) <= 32767); ! 1771: } 1.1 root 1772: 1.1.1.3 ! root 1773: /* Find the last barrier less than MAX_COUNT bytes from FROM, or create one. ! 1774: If an HI move is found, then make sure that MAX_COUNT_HI isn't broken from that one. */ 1.1 root 1775: 1.1.1.3 ! root 1776: static ! 1777: rtx ! 1778: find_barrier (from) ! 1779: rtx from; ! 1780: { ! 1781: int count_si = 0; ! 1782: int count_hi = 0; ! 1783: int found_hi = 0; ! 1784: int found_si = 0; ! 1785: rtx found_barrier = 0; ! 1786: while (from ! 1787: && count_si < max_count_si ! 1788: && count_hi < max_count_hi) ! 1789: { ! 1790: int inc; ! 1791: if (GET_CODE (from) == BARRIER) ! 1792: { ! 1793: found_barrier = from; ! 1794: } ! 1795: /* Count the length of this insn - we assume that all moves will ! 1796: be 2 bytes long, except the DIs */ ! 1797: ! 1798: if (GET_CODE (from) == INSN && ! 1799: GET_CODE (PATTERN (from)) == SET) ! 1800: { ! 1801: rtx src = SET_SRC (PATTERN (from)); ! 1802: if (hi_const (src)) ! 1803: found_hi = 1; ! 1804: else ! 1805: found_si = 1; ! 1806: inc = (GET_MODE_SIZE (GET_MODE (src)) > 4) ? 4 : 2; ! 1807: } ! 1808: else ! 1809: { ! 1810: inc = get_attr_length (from); ! 1811: } ! 1812: if (found_si) ! 1813: count_si += inc; ! 1814: if (found_hi) ! 1815: count_hi += inc; ! 1816: from = NEXT_INSN (from); 1.1 root 1817: } 1818: 1.1.1.3 ! root 1819: if (!found_barrier) 1.1 root 1820: { 1.1.1.3 ! root 1821: /* We didn't find a barrier in time to ! 1822: dump our stuff, so we'll make one */ ! 1823: rtx label = gen_label_rtx (); ! 1824: /* Walk back to be just before any jump */ ! 1825: from = PREV_INSN (from); ! 1826: while (GET_CODE (from) == JUMP_INSN ! 1827: || GET_CODE (from) == NOTE ! 1828: || GET_CODE (from) == CODE_LABEL) ! 1829: { ! 1830: from = PREV_INSN (from); ! 1831: } ! 1832: from = emit_jump_insn_after (gen_jump (label), from); ! 1833: JUMP_LABEL (from) = label; ! 1834: found_barrier = emit_barrier_after (from); ! 1835: emit_label_after (label, found_barrier); ! 1836: return found_barrier; 1.1 root 1837: } 1.1.1.3 ! root 1838: return found_barrier; 1.1 root 1839: } 1840: 1.1.1.3 ! root 1841: /* Non zero if the insn is a move instruction which needs to be fixed. */ 1.1 root 1842: 1.1.1.3 ! root 1843: static ! 1844: int ! 1845: broken_move (insn) ! 1846: rtx insn; 1.1.1.2 root 1847: { 1.1.1.3 ! root 1848: if (!INSN_DELETED_P (insn) ! 1849: && GET_CODE (insn) == INSN ! 1850: && GET_CODE (PATTERN (insn)) == SET) ! 1851: { ! 1852: rtx pat = PATTERN (insn); ! 1853: rtx src = SET_SRC (pat); ! 1854: rtx dst = SET_DEST (pat); ! 1855: enum machine_mode mode = GET_MODE (dst); ! 1856: if (dst == pc_rtx) ! 1857: return 0; ! 1858: return fixit (src, mode); ! 1859: } ! 1860: return 0; ! 1861: } 1.1.1.2 root 1862: 1863: 1.1.1.3 ! root 1864: /* Exported to toplev.c 1.1.1.2 root 1865: 1.1.1.3 ! root 1866: Scan the function looking for move instructions which have to be changed to ! 1867: pcrel loads and insert the literal tables. */ 1.1.1.2 root 1868: 1.1.1.3 ! root 1869: void ! 1870: machine_dependent_reorg (first) ! 1871: rtx first; ! 1872: { ! 1873: rtx insn; ! 1874: for (insn = first; insn; insn = NEXT_INSN (insn)) 1.1.1.2 root 1875: { 1.1.1.3 ! root 1876: if (broken_move (insn)) 1.1.1.2 root 1877: { 1.1.1.3 ! root 1878: /* This is a broken move instruction, scan ahead looking for ! 1879: a barrier to stick the constant table behind */ ! 1880: rtx scan; ! 1881: rtx barrier = find_barrier (insn); ! 1882: ! 1883: /* Now find all the moves between the points and modify them */ ! 1884: for (scan = insn; scan != barrier; scan = NEXT_INSN (scan)) 1.1.1.2 root 1885: { 1.1.1.3 ! root 1886: if (broken_move (scan)) ! 1887: { ! 1888: rtx pat = PATTERN (scan); ! 1889: rtx src = SET_SRC (pat); ! 1890: rtx dst = SET_DEST (pat); ! 1891: enum machine_mode mode = GET_MODE (dst); ! 1892: rtx lab; ! 1893: rtx newinsn; ! 1894: rtx newsrc; ! 1895: /* This is a broken move instruction, add it to the pool */ ! 1896: ! 1897: if (mode == SImode && hi_const (src)) ! 1898: { ! 1899: /* This is an HI source, clobber the dest to get the mode right too */ ! 1900: mode = HImode; ! 1901: while (GET_CODE (dst) == SUBREG) ! 1902: dst = SUBREG_REG (dst); ! 1903: dst = gen_rtx (REG, HImode, REGNO (dst)); ! 1904: } ! 1905: lab = add_constant (src, mode); ! 1906: newsrc = gen_rtx (MEM, mode, ! 1907: gen_rtx (LABEL_REF, VOIDmode, lab)); ! 1908: ! 1909: /* Build a jump insn wrapper around the move instead ! 1910: of an ordinary insn, because we want to have room for ! 1911: the target label rtx in fld[7], which an ordinary ! 1912: insn doesn't have. */ ! 1913: newinsn = emit_jump_insn_after (gen_rtx (SET, VOIDmode, ! 1914: dst, newsrc), scan); ! 1915: JUMP_LABEL (newinsn) = lab; ! 1916: ! 1917: /* But it's still an ordinary insn */ ! 1918: PUT_CODE (newinsn, INSN); ! 1919: ! 1920: /* Kill old insn */ ! 1921: delete_insn (scan); ! 1922: scan = newinsn; ! 1923: } 1.1.1.2 root 1924: } 1.1.1.3 ! root 1925: dump_table (barrier); 1.1.1.2 root 1926: } 1927: } 1928: } 1929: 1.1.1.3 ! root 1930: /* Called from the md file, set up the operands of a compare instruction */ ! 1931: 1.1.1.2 root 1932: void 1.1.1.3 ! root 1933: from_compare (operands, code) 1.1.1.2 root 1934: rtx *operands; 1.1.1.3 ! root 1935: int code; 1.1.1.2 root 1936: { 1.1.1.3 ! root 1937: if (code != EQ && code != NE) 1.1.1.2 root 1938: { 1.1.1.3 ! root 1939: /* Force args into regs, since we can't use constants here */ ! 1940: sh_compare_op0 = force_reg (SImode, sh_compare_op0); ! 1941: if (sh_compare_op1 != const0_rtx) ! 1942: sh_compare_op1 = force_reg (SImode, sh_compare_op1); 1.1.1.2 root 1943: } 1.1.1.3 ! root 1944: operands[1] = sh_compare_op0; ! 1945: operands[2] = sh_compare_op1; 1.1.1.2 root 1946: } 1947: 1.1.1.3 ! root 1948: /* Non-zero if x is EQ or NE */ 1.1.1.2 root 1949: 1.1.1.3 ! root 1950: int ! 1951: equality_operator (x, mode) ! 1952: rtx x; ! 1953: enum machine_mode mode; 1.1.1.2 root 1954: { 1.1.1.3 ! root 1955: enum rtx_code code = GET_CODE (x); ! 1956: return (code == EQ || code == NE); 1.1.1.2 root 1957: } 1958: 1959: 1.1.1.3 ! root 1960: /* Add this function to the list of ones seen - temporary ! 1961: gross hack to try out bsrs. */ ! 1962: struct flist 1.1.1.2 root 1963: { 1.1.1.3 ! root 1964: char *name; ! 1965: struct flist *next; 1.1.1.2 root 1966: }; 1.1.1.3 ! root 1967: struct flist *head; 1.1.1.2 root 1968: 1.1.1.3 ! root 1969: static void ! 1970: add_function (name) 1.1.1.2 root 1971: char *name; 1972: { 1.1.1.3 ! root 1973: struct flist *n = (struct flist *) xmalloc (sizeof (struct flist)); ! 1974: int l = strlen (name) + 1; ! 1975: n->name = xmalloc (l); ! 1976: memcpy (n->name, name, l); ! 1977: n->next = head; ! 1978: head = n; 1.1.1.2 root 1979: } 1980: 1.1.1.3 ! root 1981: static int ! 1982: seen_function (name) ! 1983: char *name; 1.1.1.2 root 1984: { 1.1.1.3 ! root 1985: struct flist *p = head; ! 1986: for (p = head; p; p = p->next) ! 1987: { ! 1988: if (strcmp (p->name, name) == 0) ! 1989: return 1; ! 1990: } ! 1991: return 0; 1.1.1.2 root 1992: } 1993: 1.1.1.3 ! root 1994: /* Framefull frame looks like: 1.1.1.2 root 1995: 1.1.1.3 ! root 1996: arg-5 ! 1997: arg-4 ! 1998: [ if current_function_anonymous_args ! 1999: arg-3 ! 2000: arg-2 ! 2001: arg-1 ! 2002: arg-0 ] ! 2003: saved-fp ! 2004: saved-r10 ! 2005: saved-r11 ! 2006: saved-r12 ! 2007: saved-pr ! 2008: local-n ! 2009: .. ! 2010: local-1 ! 2011: local-0 <- fp points here ! 2012: ! 2013: ! 2014: If TARGET_SMALLCALL, then the preserved registers are pushed by a ! 2015: wrapper before the routine is entered, so the regs are always pushed ! 2016: and there are two pr's on the stack - the caller and the wrapper. ! 2017: */ 1.1.1.2 root 2018: 2019: 1.1.1.3 ! root 2020: /* Code to generate prologue and epilogue sequences */ 1.1.1.2 root 2021: 2022: 2023: void 2024: sh_expand_prologue () 2025: { 2026: int live_regs_mask; 2027: int d; 1.1.1.3 ! root 2028: extern tree current_function_decl; 1.1.1.2 root 2029: live_regs_mask = calc_live_regs (&d); 2030: 1.1.1.3 ! root 2031: /* We have pretend args if we had an object sent partially in registers ! 2032: and partially on the stack - eg a large structure */ ! 2033: output_stack_adjust (-current_function_pretend_args_size); 1.1.1.2 root 2034: 2035: if (current_function_anonymous_args) 2036: { 2037: /* Push arg regs as if they'd been provided by caller in stack */ 2038: int i; 2039: for (i = 0; i < NPARM_REGS; i++) 2040: { 2041: int rn = NPARM_REGS + FIRST_PARM_REG - i - 1; 2042: if (i > NPARM_REGS - current_function_args_info) 2043: break; 2044: push (rn); 2045: extra_push += 4; 2046: } 2047: } 1.1.1.3 ! root 2048: push_regs (live_regs_mask); ! 2049: output_stack_adjust (-get_frame_size ()); 1.1.1.2 root 2050: 2051: if (frame_pointer_needed) 2052: { 2053: emit_insn (gen_movsi (frame_pointer_rtx, stack_pointer_rtx)); 2054: } 1.1.1.3 ! root 2055: if (TARGET_BSR) 1.1.1.2 root 2056: { 1.1.1.3 ! root 2057: add_function (IDENTIFIER_POINTER (DECL_NAME (current_function_decl))); 1.1.1.2 root 2058: } 2059: 1.1.1.3 ! root 2060: 1.1.1.2 root 2061: } 2062: 2063: void 2064: sh_expand_epilogue () 2065: { 2066: int live_regs_mask; 2067: int d; 2068: int i; 2069: 2070: live_regs_mask = calc_live_regs (&d); 2071: 1.1.1.3 ! root 2072: 1.1.1.2 root 2073: if (frame_pointer_needed) 2074: { 2075: emit_insn (gen_movsi (stack_pointer_rtx, frame_pointer_rtx)); 2076: } 1.1.1.3 ! root 2077: output_stack_adjust (get_frame_size ()); 1.1.1.2 root 2078: 2079: /* Pop all the registers */ 1.1.1.3 ! root 2080: 1.1.1.2 root 2081: for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) 2082: { 2083: int j = (FIRST_PSEUDO_REGISTER - 1) - i; 2084: if (live_regs_mask & (1 << j)) 2085: { 2086: pop (j); 2087: } 2088: } 2089: 1.1.1.3 ! root 2090: output_stack_adjust (extra_push + current_function_pretend_args_size); 1.1.1.2 root 2091: 1.1.1.3 ! root 2092: extra_push = 0; ! 2093: current_function_pretend_args_size = 0; 1.1.1.2 root 2094: current_function_anonymous_args = 0; 1.1.1.3 ! root 2095: for (i = 0; i < 32; i++) ! 2096: shiftsyms[i] = 0; ! 2097: 1.1.1.2 root 2098: } 2099: 1.1.1.3 ! root 2100: /* Define the offset between two registers, one to be eliminated, and ! 2101: the other its replacement, at the start of a routine. */ 1.1.1.2 root 2102: 1.1.1.3 ! root 2103: int ! 2104: initial_elimination_offset (from, to) ! 2105: int from; ! 2106: int to; ! 2107: { ! 2108: int regs_saved; ! 2109: int total_saved_regs_space; ! 2110: int total_auto_space = get_frame_size (); ! 2111: ! 2112: calc_live_regs (®s_saved); ! 2113: total_saved_regs_space = (regs_saved) * 4; ! 2114: ! 2115: if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM) ! 2116: { ! 2117: return total_saved_regs_space + total_auto_space; ! 2118: } ! 2119: if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM) ! 2120: { ! 2121: return total_saved_regs_space + total_auto_space; ! 2122: } ! 2123: if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM) ! 2124: { ! 2125: /* Initial gap between fp and sp is 0 */ ! 2126: return 0; ! 2127: } ! 2128: abort (); ! 2129: } ! 2130: ! 2131: /* Handle machine specific pragmas to be semi-compatible with Hitachi ! 2132: compiler */ 1.1.1.2 root 2133: 2134: int 1.1.1.3 ! root 2135: handle_pragma (file) ! 2136: FILE *file; 1.1.1.2 root 2137: { 1.1.1.3 ! root 2138: int c; ! 2139: char pbuf[200]; ! 2140: int psize = 0; 1.1.1.2 root 2141: 1.1.1.3 ! root 2142: c = getc (file); ! 2143: while (c == ' ' || c == '\t') ! 2144: c = getc (file); ! 2145: ! 2146: if (c == '\n' || c == EOF) ! 2147: return c; ! 2148: ! 2149: while (psize < sizeof (pbuf) - 1 && c != '\n') ! 2150: { ! 2151: pbuf[psize++] = c; ! 2152: if (psize == 9 && strncmp (pbuf, "interrupt", 9) == 0) ! 2153: { ! 2154: pragma_interrupt = 1; ! 2155: return ' '; ! 2156: } ! 2157: if (psize == 5 && strncmp (pbuf, "trapa", 5) == 0) ! 2158: { ! 2159: pragma_interrupt = pragma_trapa = 1; ! 2160: return ' '; ! 2161: } ! 2162: c = getc (file); ! 2163: } ! 2164: return c; ! 2165: } ! 2166: ! 2167: /* insn expand helpers */ ! 2168: ! 2169: /* Emit insns to perform a call. ! 2170: If TARGET_SHORTADDR then use a bsr. If TARGET_SMALLCALL, then load the ! 2171: target address into r1 and call __saveargs, otherwise ! 2172: perform the standard call sequence */ ! 2173: ! 2174: void ! 2175: expand_acall (isa_retval, operands) ! 2176: int isa_retval; ! 2177: rtx *operands; ! 2178: { ! 2179: rtx call; ! 2180: rtx ret = operands[0]; ! 2181: rtx call_target = operands[isa_retval + 0]; ! 2182: rtx numargs = operands[isa_retval + 1]; ! 2183: ! 2184: if (TARGET_BSR && bsr_operand (call_target, VOIDmode)) ! 2185: { ! 2186: call = gen_rtx (CALL, VOIDmode, call_target, numargs); ! 2187: } ! 2188: else ! 2189: { ! 2190: if (GET_CODE (call_target) == MEM) ! 2191: { ! 2192: call_target = force_reg (Pmode, ! 2193: XEXP (call_target, 0)); ! 2194: } ! 2195: if (TARGET_SMALLCALL) ! 2196: { ! 2197: rtx tmp = gen_reg_rtx (SImode); ! 2198: rtx r1 = gen_rtx (REG, SImode, 1); ! 2199: emit_move_insn (tmp, gen_rtx (SYMBOL_REF, SImode, "__saveargs")); ! 2200: emit_move_insn (r1, call_target); ! 2201: emit_insn (gen_rtx (USE, VOIDmode, r1)); ! 2202: call_target = tmp; ! 2203: } ! 2204: ! 2205: call = gen_rtx (CALL, VOIDmode, gen_rtx (MEM, SImode, call_target), numargs); ! 2206: } ! 2207: if (isa_retval) ! 2208: { ! 2209: call = gen_rtx (SET, VOIDmode, ret, call); ! 2210: } ! 2211: ! 2212: emit_call_insn (gen_rtx (PARALLEL, VOIDmode, ! 2213: gen_rtvec (2, ! 2214: call, ! 2215: gen_rtx (CLOBBER, VOIDmode, gen_rtx (REG, SImode, 17))))); ! 2216: ! 2217: } ! 2218: ! 2219: ! 2220: /* Predicates used by the templates */ ! 2221: ! 2222: ! 2223: /* Returns 1 if OP can be source of a simple move operation. ! 2224: Same as general_operand, but a LABEL_REF is valid, PRE_DEC is ! 2225: invalid as are subregs of system registers. */ ! 2226: ! 2227: int ! 2228: general_movsrc_operand (op, mode) ! 2229: rtx op; ! 2230: enum machine_mode mode; ! 2231: { ! 2232: /* Any MEM(label_ref) is ok, that's a pcrel load */ ! 2233: if (GET_CODE (op) == MEM ! 2234: && GET_CODE (XEXP (op, 0)) == LABEL_REF) 1.1.1.2 root 2235: return 1; 2236: 1.1.1.3 ! root 2237: if (GET_CODE (op) == MEM) ! 2238: { ! 2239: rtx inside = XEXP (op, 0); ! 2240: if (GET_CODE (inside) == CONST) ! 2241: inside = XEXP (inside, 0); ! 2242: ! 2243: if (GET_CODE (inside) == LABEL_REF) ! 2244: return 1; ! 2245: ! 2246: if (GET_CODE (inside) == PLUS ! 2247: && GET_CODE (XEXP (inside,0)) == LABEL_REF ! 2248: && GET_CODE (XEXP (inside,1)) == CONST_INT) ! 2249: return 1; ! 2250: ! 2251: /* No post inc allowed */ ! 2252: if (GET_CODE (inside) == POST_DEC ! 2253: || GET_CODE (inside) == PRE_INC ! 2254: || GET_CODE (inside) == PRE_DEC) ! 2255: return 0; ! 2256: ! 2257: /* Can't do that with large modes */ ! 2258: if (GET_CODE (inside) == POST_INC ! 2259: && GET_MODE_SIZE (mode) > 4) ! 2260: return 0; ! 2261: } ! 2262: ! 2263: if ((mode == QImode || mode == HImode) ! 2264: && (GET_CODE (op) == SUBREG ! 2265: && GET_CODE (XEXP (op, 0)) == REG ! 2266: && system_reg_operand (XEXP (op, 0), mode))) ! 2267: return 0; ! 2268: ! 2269: if (GET_CODE (op) == CONST_INT) ! 2270: { ! 2271: int i = INTVAL (op); ! 2272: return CONST_OK_FOR_I (i); ! 2273: } ! 2274: return general_operand (op, mode); 1.1.1.2 root 2275: } 2276: 1.1.1.3 ! root 2277: ! 2278: /* Returns 1 if OP can be a destination of a move. ! 2279: Same as general_operand, but no preinc allowed. */ ! 2280: 1.1.1.2 root 2281: int 1.1.1.3 ! root 2282: general_movdst_operand (op, mode) ! 2283: rtx op; ! 2284: enum machine_mode mode; 1.1.1.2 root 2285: { 1.1.1.3 ! root 2286: /* No pre dec allowed */ ! 2287: if (GET_CODE (op) == MEM ! 2288: && (GET_CODE (XEXP (op, 0)) == PRE_INC ! 2289: || GET_CODE (XEXP (op, 0)) == POST_INC ! 2290: || GET_CODE (XEXP (op, 0)) == POST_DEC)) ! 2291: return 0; ! 2292: ! 2293: if (GET_CODE (op) == MEM ! 2294: && GET_CODE (XEXP (op, 0)) == PRE_DEC ! 2295: && GET_MODE_SIZE (mode) > 4) ! 2296: return 0; ! 2297: ! 2298: return general_operand (op, mode); ! 2299: } ! 2300: ! 2301: ! 2302: ! 2303: /* Returns 1 if OP is valid destination for a bsr. */ ! 2304: ! 2305: int ! 2306: bsr_operand (op, mode) ! 2307: rtx op; ! 2308: enum machine_mode mode; ! 2309: { ! 2310: if (TARGET_BSR) ! 2311: { ! 2312: if (GET_CODE (op) == SYMBOL_REF) ! 2313: { ! 2314: if (!strcmp (XSTR (op, 0), ! 2315: IDENTIFIER_POINTER (DECL_NAME (current_function_decl)))) ! 2316: return 1; ! 2317: return (seen_function (XSTR (op, 0))); ! 2318: } ! 2319: } ! 2320: return 0; ! 2321: } ! 2322: ! 2323: /* Returns 1 if OP is an immediate ok for a byte index. */ ! 2324: ! 2325: int ! 2326: byte_index_operand (op, mode) ! 2327: rtx op; ! 2328: enum machine_mode mode; ! 2329: { ! 2330: return (GET_CODE (op) == CONST_INT ! 2331: && INTVAL (op) >= 0 ! 2332: && INTVAL (op) <= 15); ! 2333: } ! 2334: ! 2335: /* Returns 1 if OP is a pop operand. */ ! 2336: ! 2337: int ! 2338: pop_operand (op, mode) ! 2339: rtx op; ! 2340: enum machine_mode mode; ! 2341: { ! 2342: if (GET_CODE (op) != MEM) ! 2343: return 0; ! 2344: ! 2345: if (GET_MODE (op) != mode) ! 2346: return 0; ! 2347: ! 2348: op = XEXP (op, 0); ! 2349: ! 2350: if (GET_CODE (op) != POST_INC) ! 2351: return 0; ! 2352: ! 2353: return XEXP (op, 0) == stack_pointer_rtx; ! 2354: } ! 2355: ! 2356: ! 2357: /* Returns 1 if OP is a normal arithmetic register. */ ! 2358: ! 2359: int ! 2360: arith_reg_operand (op, mode) ! 2361: rtx op; ! 2362: enum machine_mode mode; ! 2363: { ! 2364: if (register_operand (op, mode)) ! 2365: { ! 2366: if (GET_CODE (op) == REG) ! 2367: return (REGNO (op) != T_REG ! 2368: && REGNO (op) != PR_REG); ! 2369: return 1; ! 2370: } ! 2371: return 0; ! 2372: } ! 2373: ! 2374: /* Returns 1 if OP is MACL, MACH or PR. */ ! 2375: ! 2376: int ! 2377: system_reg_operand (op, mode) ! 2378: rtx op; ! 2379: enum machine_mode mode; ! 2380: { ! 2381: if (GET_CODE (op) == REG) ! 2382: { ! 2383: switch (REGNO (op)) ! 2384: { ! 2385: case PR_REG: ! 2386: case MACL_REG: ! 2387: case MACH_REG: ! 2388: return 1; ! 2389: } ! 2390: } ! 2391: return 0; ! 2392: } ! 2393: ! 2394: ! 2395: /* Returns 1 if OP is a valid source operand for an arithmetic insn. */ ! 2396: ! 2397: int ! 2398: arith_operand (op, mode) ! 2399: rtx op; ! 2400: enum machine_mode mode; ! 2401: { ! 2402: if (arith_reg_operand (op, mode)) ! 2403: return 1; ! 2404: ! 2405: if (GET_CODE (op) == CONST_INT) ! 2406: { ! 2407: if (CONST_OK_FOR_I (INTVAL (op))) ! 2408: return 1; ! 2409: } ! 2410: return 0; ! 2411: } ! 2412: ! 2413: ! 2414: /* Returns 1 if OP is a valid source operand for a logical operation. */ ! 2415: ! 2416: int ! 2417: logical_operand (op, mode) ! 2418: rtx op; ! 2419: enum machine_mode mode; ! 2420: { ! 2421: if (arith_reg_operand (op, mode)) ! 2422: return 1; ! 2423: ! 2424: if (GET_CODE (op) == CONST_INT) ! 2425: { ! 2426: if (CONST_OK_FOR_L (INTVAL (op))) ! 2427: return 1; ! 2428: } ! 2429: return 0; ! 2430: } ! 2431: ! 2432: /* Returns 1 if OP is a valid operand for a MAC instruction, ! 2433: either a register or indirect memory. For now we don't ! 2434: try and recognise a mac insn */ ! 2435: ! 2436: int ! 2437: mac_operand (op, mode) ! 2438: rtx op; ! 2439: enum machine_mode mode; ! 2440: { ! 2441: if (arith_reg_operand (op, mode)) ! 2442: return 1; ! 2443: #if 0 ! 2444: Turned off till mac is understood ! 2445: if (GET_CODE (op) == MEM) ! 2446: return 1; ! 2447: #endif ! 2448: return 0; ! 2449: } ! 2450: ! 2451: /* Determine where to put an argument to a function. ! 2452: Value is zero to push the argument on the stack, ! 2453: or a hard register in which to store the argument. ! 2454: ! 2455: MODE is the argument's machine mode. ! 2456: TYPE is the data type of the argument (as a tree). ! 2457: This is null for libcalls where that information may ! 2458: not be available. ! 2459: CUM is a variable of type CUMULATIVE_ARGS which gives info about ! 2460: the preceding args and about the function being called. ! 2461: NAMED is nonzero if this argument is a named parameter ! 2462: (otherwise it is an extra parameter matching an ellipsis). */ ! 2463: ! 2464: rtx ! 2465: sh_function_arg (cum, mode, type, named) ! 2466: CUMULATIVE_ARGS cum; ! 2467: enum machine_mode mode; ! 2468: tree type; ! 2469: int named; ! 2470: { ! 2471: if (named) ! 2472: { ! 2473: int rr = (ROUND_REG ((cum), (mode))); ! 2474: ! 2475: if (rr < NPARM_REGS) ! 2476: { ! 2477: return (((type) == 0 || !TREE_ADDRESSABLE ((tree) (type))) ! 2478: && ((type) == 0 || (mode) != BLKmode ! 2479: || (TYPE_ALIGN ((type)) % PARM_BOUNDARY == 0)) ! 2480: ? gen_rtx (REG, (mode), ! 2481: (FIRST_PARM_REG + rr)) ! 2482: : 0); ! 2483: ! 2484: } ! 2485: } ! 2486: return 0; ! 2487: } ! 2488: ! 2489: /* For an arg passed partly in registers and partly in memory, ! 2490: this is the number of registers used. ! 2491: For args passed entirely in registers or entirely in memory, zero. ! 2492: Any arg that starts in the first 4 regs but won't entirely fit in them ! 2493: needs partial registers on the SH. */ ! 2494: ! 2495: int ! 2496: sh_function_arg_partial_nregs (CUM, MODE, TYPE, NAMED) ! 2497: CUMULATIVE_ARGS CUM; ! 2498: enum machine_mode MODE; ! 2499: tree TYPE; ! 2500: int NAMED; ! 2501: { ! 2502: if ((CUM) < NPARM_REGS) ! 2503: { ! 2504: if (((TYPE) == 0 || !TREE_ADDRESSABLE ((tree) (TYPE))) ! 2505: && ((TYPE) == 0 || (TYPE_ALIGN ((TYPE)) % PARM_BOUNDARY == 0)) ! 2506: && ((CUM) + ((MODE) == BLKmode ! 2507: ? ROUND_ADVANCE (int_size_in_bytes (TYPE)) ! 2508: : ROUND_ADVANCE (GET_MODE_SIZE (MODE))) - NPARM_REGS > 0)) ! 2509: { ! 2510: return NPARM_REGS - CUM; ! 2511: } ! 2512: } ! 2513: return 0; ! 2514: } ! 2515: ! 2516: /* Turn this on to recognise shift insns which aren't supported in the ! 2517: hardware. This will allow the combiner to notice more patterns, ! 2518: but the down side is that the asm outputter will have to emit ! 2519: several instructions for each shift which isn't possible in the ! 2520: hardware, this makes scheduling perform badly .*/ ! 2521: ! 2522: int fake_shift() ! 2523: { ! 2524: return 0; 1.1.1.2 root 2525: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.