|
|
1.1 ! root 1: /* Output routines for GCC for Hitachi Super-H ! 2: Copyright (C) 1993 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: /* 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" ! 35: #include "insn-attr.h" ! 36: #include "flags.h" ! 37: #include "obstack.h" ! 38: #include "expr.h" ! 39: ! 40: ! 41: static int add_constant (); ! 42: static int dump_constants (); ! 43: ! 44: int current_function_anonymous_args; ! 45: extern int current_function_pretend_args_size; ! 46: ! 47: /* Global variables for machine-dependent things. */ ! 48: ! 49: /* Saved operands from the last compare to use when we generate an scc ! 50: or bcc insn. */ ! 51: ! 52: rtx sh_compare_op0; ! 53: rtx sh_compare_op1; ! 54: ! 55: /* Provides the class number of the smallest class containing ! 56: reg number */ ! 57: ! 58: int regno_reg_class[FIRST_PSEUDO_REGISTER] = ! 59: { ! 60: R0_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, ! 61: GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, ! 62: GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, ! 63: GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, ! 64: GENERAL_REGS, PR_REGS, T_REGS, NO_REGS, MAC_REGS, ! 65: MAC_REGS, ! 66: }; ! 67: ! 68: /* Provide reg_class from a letter such as appears in the machine ! 69: description. */ ! 70: ! 71: enum reg_class reg_class_from_letter[] = ! 72: { ! 73: /* a */ NO_REGS, /* b */ NO_REGS, /* c */ NO_REGS, /* d */ NO_REGS, ! 74: /* e */ NO_REGS, /* f */ NO_REGS, /* g */ NO_REGS, /* h */ NO_REGS, ! 75: /* i */ NO_REGS, /* j */ NO_REGS, /* k */ NO_REGS, /* l */ PR_REGS, ! 76: /* m */ NO_REGS, /* n */ NO_REGS, /* o */ NO_REGS, /* p */ NO_REGS, ! 77: /* q */ NO_REGS, /* r */ NO_REGS, /* s */ NO_REGS, /* t */ T_REGS, ! 78: /* u */ NO_REGS, /* v */ NO_REGS, /* w */ NO_REGS, /* x */ MAC_REGS, ! 79: /* y */ NO_REGS, /* z */ R0_REGS ! 80: }; ! 81: ! 82: ! 83: /* Local label counter, used for constants in the pool and inside ! 84: pattern branches. */ ! 85: ! 86: static int lf = 100; ! 87: ! 88: /* Used to work out sizes of instructions */ ! 89: static int first_pc; ! 90: static int pc; ! 91: #define MAYBE_DUMP_LEVEL 900 ! 92: #define MUST_DUMP_LEVEL 1000 ! 93: static int dumpnext; ! 94: ! 95: /* Functions for generating procedure prologue and epilogue code */ ! 96: ! 97: /* Adjust the stack and return the number of bytes taken to do it */ ! 98: ! 99: static int ! 100: output_stack_adjust (file, direction, size) ! 101: FILE *file; ! 102: int direction; ! 103: int size; ! 104: { ! 105: int code_size; ! 106: ! 107: if (size > 127) ! 108: { ! 109: fprintf (file, "\tmov.l LK%d,r13\n", ! 110: add_constant (GEN_INT (size * direction), SImode)); ! 111: ! 112: fprintf (file, "\tadd r13,r15\n"); ! 113: code_size += 4; ! 114: } ! 115: else if (size) ! 116: { ! 117: fprintf (file, "\tadd #%d,r15\n", direction * size); ! 118: code_size += 2; ! 119: } ! 120: return code_size; ! 121: } ! 122: ! 123: /* Generate code to push the regs specified in the mask, and return ! 124: the number of bytes the insns take. */ ! 125: ! 126: static int ! 127: push_regs (f, mask) ! 128: FILE *f; ! 129: int mask; ! 130: { ! 131: int i; ! 132: int size = 0; ! 133: ! 134: for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) ! 135: { ! 136: if (mask & (1 << i)) ! 137: { ! 138: fprintf (f, "\tmov.l r%d,@-r15\n", i); ! 139: size += 2; ! 140: } ! 141: } ! 142: return size; ! 143: } ! 144: ! 145: ! 146: /* Working out the right code to use for an epilogue can get quite ! 147: hairy, since there are only certain insns which can go in the delay ! 148: slot, and there may or may not be a delay insn provided already. ! 149: ! 150: We generate a canonical list of the instructions to use to perform ! 151: the exit, massage that and output from that list */ ! 152: ! 153: ! 154: /* The structure of a canonical element. */ ! 155: ! 156: typedef struct ! 157: { ! 158: enum epi_type ! 159: { ! 160: STACK_ADJUST, /* add i to stack pointer */ ! 161: POP, /* pop into register i */ ! 162: RTS, /* rts instruction */ ! 163: DELAY, /* delay slot instruction */ ! 164: NOP, /* a nop */ ! 165: DELETED, ! 166: } type; ! 167: int i; ! 168: } ! 169: ! 170: epilogue_insn; ! 171: ! 172: static epilogue_insn epilogue_vec[20]; ! 173: static int epilogue_vec_len; ! 174: ! 175: static void ! 176: set_epilogue_insn (type, l) ! 177: enum epi_type type; ! 178: int l; ! 179: { ! 180: epilogue_vec[epilogue_vec_len].type = type; ! 181: epilogue_vec[epilogue_vec_len].i = l; ! 182: epilogue_vec_len++; ! 183: } ! 184: ! 185: /* Delete an insn from the epilogue list. */ ! 186: ! 187: static void ! 188: delete_epilogue_insn (n) ! 189: int n; ! 190: { ! 191: int j; ! 192: ! 193: for (j = n; j < epilogue_vec_len; j++) ! 194: epilogue_vec[j] = epilogue_vec[j + 1]; ! 195: ! 196: epilogue_vec_len--; ! 197: } ! 198: ! 199: /* Run through the epilogue list and optimize it. */ ! 200: ! 201: static void ! 202: optimize_epilogue_vec () ! 203: { ! 204: int i; ! 205: ! 206: /* Turn two adds in a row into one add and kill empty adds */ ! 207: for (i = 0; i < epilogue_vec_len - 1; i++) ! 208: { ! 209: if (epilogue_vec[i].type == STACK_ADJUST ! 210: && epilogue_vec[i + 1].type == STACK_ADJUST) ! 211: { ! 212: epilogue_vec[i].i += epilogue_vec[i + 1].i; ! 213: delete_epilogue_insn (i + 1); ! 214: } ! 215: if (epilogue_vec[i].type == STACK_ADJUST ! 216: && epilogue_vec[i].i == 0) ! 217: delete_epilogue_insn (i); ! 218: } ! 219: ! 220: /* If the instruction after the RTS is a nop, see if it can be ! 221: changed */ ! 222: ! 223: for (i = 1; i < epilogue_vec_len - 1; i++) ! 224: { ! 225: if (epilogue_vec[i].type == RTS ! 226: && epilogue_vec[i + 1].type == NOP) ! 227: { ! 228: epilogue_vec[i + 1] = epilogue_vec[i - 1]; ! 229: delete_epilogue_insn (i - 1); ! 230: } ! 231: } ! 232: ! 233: /* Delete all the instructions after the rts's delay slot */ ! 234: for (i = 0; i < epilogue_vec_len; i++) ! 235: { ! 236: if (epilogue_vec[i].type == RTS) ! 237: { ! 238: int j; ! 239: ! 240: for (j = i + 2; j < epilogue_vec_len; j++) ! 241: epilogue_vec[j].type = DELETED; ! 242: return; ! 243: } ! 244: } ! 245: } ! 246: ! 247: /* Dump out the insns in epilogue vector. */ ! 248: ! 249: static void ! 250: output_epilogue_vec () ! 251: { ! 252: int i; ! 253: ! 254: for (i = 0; i < epilogue_vec_len; i++) ! 255: { ! 256: switch (epilogue_vec[i].type) ! 257: { ! 258: case STACK_ADJUST: ! 259: fprintf (asm_out_file, "\tadd #%d,r15\n", epilogue_vec[i].i); ! 260: break; ! 261: ! 262: case NOP: ! 263: fprintf (asm_out_file, "\tor r0,r0\n"); ! 264: break; ! 265: ! 266: case DELAY: ! 267: final_scan_insn (XEXP (current_function_epilogue_delay_list, 0), ! 268: asm_out_file, 1, 0, 1); ! 269: break; ! 270: ! 271: case DELETED: ! 272: fprintf (asm_out_file, "\t!delete_epilogue_insnd\n"); ! 273: break; ! 274: ! 275: case RTS: ! 276: fprintf (asm_out_file, "\trts\n"); ! 277: break; ! 278: ! 279: case POP: ! 280: fprintf (asm_out_file, "\tmov.l @r15+,r%d\n", ! 281: epilogue_vec[i].i); ! 282: break; ! 283: } ! 284: } ! 285: epilogue_vec_len = 0; ! 286: } ! 287: ! 288: /* Number of bytes pushed for anonymous args */ ! 289: ! 290: static int extra_push; ! 291: ! 292: /* Work out the registers which need to be saved, both as a mask and a ! 293: count */ ! 294: ! 295: int ! 296: calc_live_regs (count) ! 297: int *count; ! 298: { ! 299: int reg; ! 300: int live_regs_mask = 0; ! 301: *count = 0; ! 302: ! 303: for (reg = 0; reg < FIRST_PSEUDO_REGISTER; reg++) ! 304: { ! 305: if (regs_ever_live[reg] && !call_used_regs[reg]) ! 306: { ! 307: (*count)++; ! 308: live_regs_mask |= (1 << reg); ! 309: } ! 310: } ! 311: return live_regs_mask; ! 312: } ! 313: ! 314: /* Generate a procedure prologue. */ ! 315: ! 316: void ! 317: output_prologue (f, frame_size) ! 318: FILE *f; ! 319: int frame_size; ! 320: { ! 321: int live_regs_mask; ! 322: int d; ! 323: ! 324: pc = 0; ! 325: ! 326: /* This only happens when an arg has been split, part in ! 327: registers, part in memory. Allocate the stack space so there is ! 328: somewhere to put the value */ ! 329: ! 330: output_stack_adjust (f, -1, current_function_pretend_args_size); ! 331: ! 332: live_regs_mask = calc_live_regs (&d); ! 333: ! 334: extra_push = 0; ! 335: ! 336: if (current_function_anonymous_args) ! 337: { ! 338: /* Push arg regs as if they'd been provided by caller in stack */ ! 339: int i; ! 340: for (i = 0; i < NPARM_REGS; i++) ! 341: { ! 342: int rn = NPARM_REGS + FIRST_PARM_REG - i - 1; ! 343: if (i > NPARM_REGS - current_function_args_info) ! 344: break; ! 345: fprintf (f, "\tmov.l r%d,@-r15\n", rn); ! 346: extra_push += 4; ! 347: pc += 2; ! 348: } ! 349: } ! 350: ! 351: if (frame_pointer_needed) ! 352: { ! 353: /* Don't need to push the fp with the rest of the registers. */ ! 354: live_regs_mask &= ~(1 << FRAME_POINTER_REGNUM); ! 355: pc += push_regs (f, live_regs_mask); ! 356: if (regs_ever_live[PR_REG]) ! 357: { ! 358: ! 359: fprintf (f, "\tsts.l pr,@-r15\n"); ! 360: pc += 2; ! 361: } ! 362: ! 363: fprintf (f, "\tmov.l r14,@-r15\n"); ! 364: fprintf (f, "\tmov r15,r14\n"); ! 365: pc += 4; ! 366: pc += output_stack_adjust (f, -1, frame_size); ! 367: } ! 368: else ! 369: { ! 370: pc += push_regs (f, live_regs_mask); ! 371: ! 372: if (regs_ever_live[PR_REG]) ! 373: { ! 374: ! 375: fprintf (f, "\tsts.l pr,@-r15\n"); ! 376: pc += 2; ! 377: } ! 378: pc += output_stack_adjust (f, -1, frame_size); ! 379: } ! 380: } ! 381: ! 382: ! 383: /* Generate a procedure epilogue. */ ! 384: ! 385: void ! 386: output_epilogue (f, frame_size) ! 387: FILE *f; ! 388: int frame_size; ! 389: { ! 390: int live_regs_mask = 0; ! 391: int d; ! 392: int i; ! 393: rtx delay_insn; ! 394: ! 395: live_regs_mask = calc_live_regs (&d); ! 396: ! 397: ! 398: /* See if the delay insn is really ok for the slot. */ ! 399: if (current_function_epilogue_delay_list) { ! 400: delay_insn = PATTERN (XEXP (current_function_epilogue_delay_list, 0)); ! 401: ! 402: if (GET_CODE (delay_insn) == SET ! 403: && SET_DEST (delay_insn) == stack_pointer_rtx) ! 404: { ! 405: /* Can not use this instruction in the delay slot because ! 406: it changes the stack pointer, so emit it now. */ ! 407: final_scan_insn (XEXP (current_function_epilogue_delay_list, 0), ! 408: asm_out_file, 1, 0, 1); ! 409: current_function_epilogue_delay_list = 0; ! 410: } ! 411: } ! 412: ! 413: ! 414: /* Reclaim the room for the automatics. */ ! 415: ! 416: output_stack_adjust (f, 1, frame_size); ! 417: ! 418: /* Make the frame pointer. */ ! 419: ! 420: if (frame_pointer_needed) ! 421: { ! 422: fprintf (f, "\tmov r14,r15\n"); ! 423: fprintf (f, "\tmov.l @r15+,r14\n"); ! 424: live_regs_mask &= ~(1 << FRAME_POINTER_REGNUM); ! 425: } ! 426: ! 427: /* Get the PR register if it was clobbered in the function. */ ! 428: ! 429: if (regs_ever_live[PR_REG]) ! 430: fprintf (f, "\tlds.l @r15+,pr\n"); ! 431: ! 432: /* Pop all the registers */ ! 433: for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) ! 434: { ! 435: int j = (FIRST_PSEUDO_REGISTER - 1) - i; ! 436: if (live_regs_mask & (1 << j)) ! 437: { ! 438: set_epilogue_insn (POP, j); ! 439: } ! 440: } ! 441: ! 442: /* Need to adjust the stack by some amount of bytes since we've pushed ! 443: some of the args which normally come in registers */ ! 444: ! 445: set_epilogue_insn (STACK_ADJUST, extra_push); ! 446: ! 447: /* Need to adjust the stack by some amount of bytes if there ! 448: an arg has been split part register and part stack */ ! 449: ! 450: set_epilogue_insn (STACK_ADJUST, current_function_pretend_args_size); ! 451: ! 452: set_epilogue_insn (RTS, 0); ! 453: ! 454: /* Got here without dumping a register pop into the delay slot */ ! 455: if (current_function_epilogue_delay_list) ! 456: { ! 457: set_epilogue_insn (DELAY, 0); ! 458: } ! 459: set_epilogue_insn (NOP, 0); ! 460: ! 461: optimize_epilogue_vec (); ! 462: ! 463: output_epilogue_vec (); ! 464: ! 465: dump_constants (0); ! 466: current_function_anonymous_args = 0; ! 467: } ! 468: ! 469: /* Print the operand address in x to the stream */ ! 470: ! 471: void ! 472: print_operand_address (stream, x) ! 473: FILE *stream; ! 474: rtx x; ! 475: { ! 476: switch (GET_CODE (x)) ! 477: { ! 478: case REG: ! 479: fprintf (stream, "@%s", reg_names[REGNO (x)]); ! 480: break; ! 481: ! 482: case PLUS: ! 483: { ! 484: rtx base = XEXP (x, 0); ! 485: rtx index = XEXP (x, 1); ! 486: ! 487: if (GET_CODE (base) != REG) ! 488: { ! 489: /* Ensure that BASE is a register (one of them must be). */ ! 490: rtx temp = base; ! 491: base = index; ! 492: index = temp; ! 493: } ! 494: ! 495: switch (GET_CODE (index)) ! 496: { ! 497: case CONST_INT: ! 498: fprintf (stream, "@(%d,%s)", ! 499: INTVAL (index), ! 500: reg_names[REGNO (base)]); ! 501: break; ! 502: ! 503: case REG: ! 504: fprintf (stream, "@(%s,%s)", ! 505: reg_names[REGNO (base)], ! 506: reg_names[REGNO (index)]); ! 507: break; ! 508: ! 509: default: ! 510: abort (); ! 511: } ! 512: } ! 513: ! 514: break; ! 515: case PRE_DEC: ! 516: fprintf (stream, "@-%s", reg_names[REGNO (XEXP (x, 0))]); ! 517: break; ! 518: ! 519: case POST_INC: ! 520: fprintf (stream, "@%s+", reg_names[REGNO (XEXP (x, 0))]); ! 521: break; ! 522: ! 523: default: ! 524: output_addr_const (stream, x); ! 525: break; ! 526: } ! 527: } ! 528: ! 529: /* Print operand x (an rtx) in assembler syntax to file stream ! 530: according to modifier code. ! 531: ! 532: '*' print a local label ! 533: '^' increment the local label number ! 534: '!' dump the constant table ! 535: '#' output a nop if there is nothing to put in the delay slot ! 536: 'R' print the next register or memory location along, ie the lsw in ! 537: a double word value ! 538: 'I' put something into the constant pool and print its label */ ! 539: ! 540: void ! 541: print_operand (stream, x, code) ! 542: FILE *stream; ! 543: rtx x; ! 544: int code; ! 545: { ! 546: switch (code) ! 547: { ! 548: case '*': ! 549: fprintf (stream, "LF%d", lf); ! 550: break; ! 551: case '!': ! 552: dump_constants (0); ! 553: break; ! 554: case '^': ! 555: lf++; ! 556: break; ! 557: ! 558: case '#': ! 559: /* Output a nop if there's nothing in the delay slot */ ! 560: if (dbr_sequence_length () == 0) ! 561: { ! 562: fprintf (stream, "\n\tor r0,r0\t!wasted slot"); ! 563: } ! 564: break; ! 565: ! 566: case 'I': ! 567: fprintf (asm_out_file, "LK%d", add_constant (x, SImode)); ! 568: break; ! 569: ! 570: case 'R': ! 571: /* Next location along in memory or register*/ ! 572: switch (GET_CODE (x)) ! 573: { ! 574: case REG: ! 575: fputs (reg_names[REGNO (x) + 1], (stream)); ! 576: break; ! 577: case MEM: ! 578: print_operand_address (stream, ! 579: XEXP (adj_offsettable_operand (x, 4), 0), 0); ! 580: break; ! 581: } ! 582: break; ! 583: ! 584: default: ! 585: switch (GET_CODE (x)) ! 586: { ! 587: case REG: ! 588: fputs (reg_names[REGNO (x)], (stream)); ! 589: break; ! 590: case MEM: ! 591: output_address (XEXP (x, 0)); ! 592: break; ! 593: default: ! 594: fputc ('#', stream); ! 595: output_addr_const (stream, x); ! 596: break; ! 597: ! 598: } ! 599: break; ! 600: } ! 601: } ! 602: ! 603: ! 604: ! 605: /* Define the offset between two registers, one to be eliminated, and ! 606: the other its replacement, at the start of a routine. */ ! 607: ! 608: int ! 609: initial_elimination_offset (from, to) ! 610: { ! 611: int regs_saved; ! 612: int d = calc_live_regs (®s_saved); ! 613: int total_saved_regs_space = (regs_saved + regs_ever_live[PR_REG]) * 4; ! 614: int total_auto_space = get_frame_size (); ! 615: ! 616: ! 617: if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM) ! 618: { ! 619: return total_saved_regs_space; ! 620: } ! 621: ! 622: if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM) ! 623: { ! 624: return total_saved_regs_space + total_auto_space; ! 625: } ! 626: ! 627: if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM) ! 628: { ! 629: return total_auto_space; ! 630: } ! 631: } ! 632: ! 633: delay_slots_for_epilogue () ! 634: { ! 635: /* We need to find something to fill the epilogue if there won't be ! 636: any instructions to make the stack or pop registers which can be ! 637: moved into the slot */ ! 638: ! 639: int d; ! 640: calc_live_regs (&d); ! 641: return !(get_frame_size () + d); ! 642: } ! 643: ! 644: ! 645: /* Prepare operands for a move define_expand; specifically, one of the ! 646: operands must be in a register */ ! 647: ! 648: void ! 649: prepare_move_operands (operands, mode) ! 650: rtx operands[]; ! 651: enum machine_mode mode; ! 652: { ! 653: /* One of the operands has to be a register */ ! 654: if ((!register_operand (operands[0], mode) ! 655: && !register_operand (operands[1], mode)) ! 656: || GET_CODE(operands[1]) == PLUS) ! 657: { ! 658: /* copy the source to a register */ ! 659: operands[1] = copy_to_mode_reg (mode, operands[1]); ! 660: } ! 661: } ! 662: ! 663: ! 664: /* Prepare the operands for an scc instruction; make sure that the ! 665: compare has been done. */ ! 666: rtx ! 667: prepare_scc_operands (code) ! 668: { ! 669: if (GET_CODE(sh_compare_op0) != REG ! 670: || REGNO(sh_compare_op0) != T_REG) ! 671: { ! 672: /* First need a compare insn */ ! 673: emit_insn (gen_rtx (SET, SImode, ! 674: gen_rtx (REG, SImode, T_REG), ! 675: gen_rtx (code, SImode, sh_compare_op0, ! 676: sh_compare_op1))); ! 677: } ! 678: return gen_rtx(REG, SImode, T_REG); ! 679: } ! 680: ! 681: /* Functions to output assembly */ ! 682: ! 683: /* Return a sequence of instructions to perform DI move, taking into ! 684: account overlapping source and dest registers */ ! 685: ! 686: char * ! 687: output_movedouble (operands, mode) ! 688: rtx operands[]; ! 689: enum machine_mode mode; ! 690: { ! 691: if (register_operand (operands[0], mode) ! 692: && register_operand (operands[1], mode)) ! 693: { ! 694: if (REGNO (operands[1]) == MACH_REG) ! 695: return "sts mach,%0\n\tsts macl,%R0"; ! 696: if (REGNO (operands[1]) > REGNO (operands[0])) ! 697: { ! 698: return "mov %1,%0\n\tmov %R1,%R0"; ! 699: } ! 700: else ! 701: { ! 702: return "mov %R1,%R0\n\tmov %1,%0"; ! 703: } ! 704: } ! 705: ! 706: if (GET_CODE (operands[1]) == CONST_INT) ! 707: { ! 708: if (INTVAL (operands[1]) < 0) ! 709: return "mov #-1,%0\n\tmov %1,%R0"; ! 710: else ! 711: return "mov #0,%0\n\tmov %1,%R0"; ! 712: } ! 713: ! 714: if (GET_CODE (operands[1]) == MEM) ! 715: { ! 716: int idxreg = -1; ! 717: rtx inside = XEXP (operands[1], 0); ! 718: ! 719: if (GET_CODE (inside) == REG) ! 720: idxreg = REGNO (inside); ! 721: else if (GET_CODE (inside) == PLUS) ! 722: { ! 723: rtx lhs = XEXP (inside, 0); ! 724: rtx rhs = XEXP (inside, 1); ! 725: if (GET_CODE (lhs) == REG) ! 726: idxreg = REGNO (lhs); ! 727: else if (GET_CODE (rhs) == REG) ! 728: idxreg = REGNO (rhs); ! 729: else ! 730: abort (); ! 731: } ! 732: else ! 733: abort (); ! 734: ! 735: if (REGNO (operands[0]) != idxreg) ! 736: { ! 737: /* The dest register is mentioned in the addressing mode, ! 738: so print them the other way around */ ! 739: return "mov.l %1,%0\n\tmov.l %R1,%R0 ! one way"; ! 740: } ! 741: return "mov.l %R1,%R0\n\tmov.l %1,%0 ! other way"; ! 742: } ! 743: ! 744: return "mov.l %R1,%R0\n\tmov.l %1,%0"; ! 745: } ! 746: ! 747: /* Emit assembly to shift reg by k bits */ ! 748: ! 749: char * ! 750: output_shift (string, reg, k) ! 751: char *string; ! 752: rtx reg; ! 753: rtx k; ! 754: { ! 755: int s = INTVAL (k); ! 756: while (s) ! 757: { ! 758: char *out; ! 759: int d; ! 760: ! 761: if (s >= 16) ! 762: { ! 763: d = 16; ! 764: out = "16"; ! 765: } ! 766: else if (s >= 8) ! 767: { ! 768: d = 8; ! 769: out = "8"; ! 770: } ! 771: else if (s >= 2) ! 772: { ! 773: d = 2; ! 774: out = "2"; ! 775: } ! 776: else ! 777: { ! 778: d = 1; ! 779: out = ""; ! 780: } ! 781: fprintf (asm_out_file, "\t%s%s\tr%d\n", string, out, REGNO (reg)); ! 782: s -= d; ! 783: } ! 784: return ""; ! 785: } ! 786: ! 787: /* Return the text of the branch instruction which matches its length ! 788: attribute. */ ! 789: ! 790: char * ! 791: output_branch (logic, insn) ! 792: int logic; ! 793: rtx *insn; ! 794: { ! 795: extern rtx recog_operand[]; ! 796: int label = lf++; ! 797: ! 798: switch (get_attr_length (insn)) ! 799: { ! 800: case 2: ! 801: /* Simple branch in range -200..+200 bytes */ ! 802: return logic ? "bt %l0" : "bf %l0"; ! 803: ! 804: case 6: ! 805: /* Branch in range -4000..+4000 bytes */ ! 806: fprintf (asm_out_file, "\tb%c\tLF%d\n", logic ? 'f' : 't', label); ! 807: output_asm_insn ("bra %l0 ! 12 bit cond ", recog_operand); ! 808: fprintf (asm_out_file, "\tor r0,r0\n"); ! 809: label = dump_constants (label); ! 810: fprintf (asm_out_file, "LF%d:\n", label); ! 811: return ""; ! 812: ! 813: case 8: ! 814: /* Branches a long way away */ ! 815: ! 816: fprintf (asm_out_file, "\tb%c\tLF%d\n", logic ? 'f' : 't', label); ! 817: output_asm_insn ("mov.l %I0,r13", recog_operand); ! 818: fprintf (asm_out_file, "\tjmp @r13 ! 32 cond \n"); ! 819: fprintf (asm_out_file, "\tor r0,r0\n"); ! 820: fprintf (asm_out_file, "LF%d:\n", label); ! 821: return ""; ! 822: } ! 823: return "bad"; ! 824: ! 825: } ! 826: ! 827: /* Predicates used by the templates */ ! 828: ! 829: /* Nonzero if OP is a normal arithmetic register. */ ! 830: ! 831: int ! 832: arith_reg_operand(op, mode) ! 833: rtx op; ! 834: enum machine_mode mode; ! 835: { ! 836: if (register_operand (op, mode)) ! 837: { ! 838: if (GET_CODE (op) == REG) ! 839: return REGNO (op) != T_REG; ! 840: return 1; ! 841: } ! 842: return 0; ! 843: } ! 844: ! 845: ! 846: /* Nonzero if OP is a valid source operand for an arithmetic insn. */ ! 847: ! 848: int ! 849: arith_operand (op, mode) ! 850: rtx op; ! 851: enum machine_mode mode; ! 852: { ! 853: if (register_operand (op, mode)) ! 854: return 1; ! 855: ! 856: if (GET_CODE (op) == CONST_INT) ! 857: { ! 858: if (CONST_OK_FOR_I (INTVAL (op))) ! 859: return 1; ! 860: } ! 861: return 0; ! 862: } ! 863: ! 864: ! 865: /* Nonzero if OP is a valid source operand for a logical operation */ ! 866: ! 867: int ! 868: logical_operand (op, mode) ! 869: rtx op; ! 870: enum machine_mode mode; ! 871: { ! 872: if (register_operand (op, mode)) ! 873: return 1; ! 874: ! 875: if (GET_CODE (op) == CONST_INT) ! 876: { ! 877: if (CONST_OK_FOR_L (INTVAL (op))) ! 878: return 1; ! 879: } ! 880: return 0; ! 881: } ! 882: ! 883: /* Nonzero if p is a valid shift operand for lshr and ashl */ ! 884: ! 885: int ! 886: ok_shift_value (p) ! 887: rtx p; ! 888: { ! 889: if (GET_CODE (p) == CONST_INT) ! 890: { ! 891: switch (INTVAL (p)) ! 892: { ! 893: case 1: ! 894: case 2: ! 895: case 8: ! 896: case 16: ! 897: return 1; ! 898: default: ! 899: if (TARGET_FASTCODE) ! 900: return INTVAL(p) >= 0; ! 901: } ! 902: } ! 903: return 0; ! 904: } ! 905: ! 906: /* Nonzero if the arg is an immediate which has to be loaded from ! 907: memory */ ! 908: ! 909: int ! 910: hard_immediate_operand (op, mode) ! 911: rtx op; ! 912: enum machine_mode mode; ! 913: { ! 914: if (immediate_operand (op, mode)) ! 915: { ! 916: if (GET_CODE (op) == CONST_INT ! 917: && INTVAL (op) >= -128 && INTVAL (op) < 127) ! 918: return 0; ! 919: return 1; ! 920: } ! 921: return 0; ! 922: } ! 923: ! 924: /* The SH cannot load a large constant into a register, constants have to ! 925: come from a pc relative load. The reference of a pc relative load ! 926: instruction must be less than 1k infront of the instruction. This ! 927: means that we often have to dump a constant inside a function, and ! 928: generate code to branch around it. ! 929: ! 930: It is important to minimize this, since the branches will slow things ! 931: down and make things bigger. ! 932: ! 933: Worst case code looks like: ! 934: ! 935: mov.l L1,rn ! 936: bra L2 ! 937: nop ! 938: align ! 939: L1: .long value ! 940: L2: ! 941: .. ! 942: ! 943: mov.l L3,rn ! 944: bra L4 ! 945: nop ! 946: align ! 947: L3: .long value ! 948: L4: ! 949: .. ! 950: ! 951: During shorten_branches we notice the instructions which can have a ! 952: constant table in them, if we see two that are close enough ! 953: together, we move the constants from the first table to the second ! 954: table and continue. This process can happen again and again, and ! 955: in the best case, moves the constant table outside of the function. ! 956: ! 957: In the above example, we can tell that L3 is within 1k of L1, so ! 958: the first move can be shrunk from the 3 insn+constant sequence into ! 959: just 1 insn, and the constant moved to L3 to make: ! 960: ! 961: mov.l L1,rn ! 962: .. ! 963: mov.l L3,rn ! 964: bra L4 ! 965: nop ! 966: align ! 967: L3:.long value ! 968: L4:.long value ! 969: ! 970: Then the second move becomes the target for the shortening process. ! 971: ! 972: We keep a simple list of all the constants accumulated in the ! 973: current pool so there are no duplicates in a single table, but ! 974: they are not factored into the size estimates. ! 975: ! 976: */ ! 977: ! 978: typedef struct ! 979: { ! 980: rtx value; ! 981: int number; ! 982: enum machine_mode mode; ! 983: } pool_node; ! 984: ! 985: /* The maximum number of constants that can fit into one pool, since ! 986: the pc relative range is 0...1020 bytes and constants are at least 4 ! 987: bytes long */ ! 988: ! 989: #define MAX_POOL_SIZE (1020/4) ! 990: static pool_node pool_vector[MAX_POOL_SIZE]; ! 991: static int pool_size; ! 992: ! 993: ! 994: /* Add a constant to the pool and return its label number. */ ! 995: ! 996: static int ! 997: add_constant (x, mode) ! 998: rtx x; ! 999: enum machine_mode mode; ! 1000: { ! 1001: int i; ! 1002: ! 1003: /* Start the countdown on the first constant */ ! 1004: ! 1005: if (!pool_size) ! 1006: { ! 1007: first_pc = pc; ! 1008: } ! 1009: ! 1010: /* First see if we've already got it */ ! 1011: ! 1012: for (i = 0; i < pool_size; i++) ! 1013: { ! 1014: ! 1015: if (x->code == pool_vector[i].value->code ! 1016: && mode == pool_vector[i].mode) ! 1017: { ! 1018: if (x->code == CODE_LABEL) ! 1019: { ! 1020: if (XINT (x, 3) != XINT (pool_vector[i].value, 3)) ! 1021: continue; ! 1022: } ! 1023: } ! 1024: ! 1025: if (rtx_equal_p (x, pool_vector[i].value)) ! 1026: return pool_vector[i].number; ! 1027: } ! 1028: ! 1029: ! 1030: pool_vector[pool_size].value = x; ! 1031: pool_vector[pool_size].mode = mode; ! 1032: pool_vector[pool_size].number = lf; ! 1033: pool_size++; ! 1034: ! 1035: return lf++; ! 1036: } ! 1037: ! 1038: /* Nonzero if the insn could take a constant table. */ ! 1039: ! 1040: static int ! 1041: has_constant_table (insn) ! 1042: rtx insn; ! 1043: { ! 1044: rtx body; ! 1045: ! 1046: if (GET_CODE (insn) == NOTE ! 1047: || GET_CODE (insn) == BARRIER ! 1048: || GET_CODE (insn) == CODE_LABEL) ! 1049: return 0; ! 1050: ! 1051: body = PATTERN (insn); ! 1052: if (GET_CODE (body) == SEQUENCE) ! 1053: return 0; ! 1054: if (GET_CODE (body) == ADDR_VEC) ! 1055: return 0; ! 1056: if (GET_CODE (body) == USE) ! 1057: return 0; ! 1058: if (GET_CODE (body) == CLOBBER) ! 1059: return 0; ! 1060: if (get_attr_constneed (insn) == CONSTNEED_YES) ! 1061: return 1; ! 1062: ! 1063: if (GET_CODE (body) == UNSPEC_VOLATILE) ! 1064: { ! 1065: return INTVAL (XVECEXP (body, 0, 0)) == 1; ! 1066: } ! 1067: return 0; ! 1068: } ! 1069: ! 1070: /* Adjust the length of an instruction. ! 1071: ! 1072: We'll look at the previous instruction which holds a constant ! 1073: table and see if we can move the table to here instead. */ ! 1074: ! 1075: int target_insn_uid; ! 1076: int target_insn_smallest_size; ! 1077: ! 1078: int target_pc; ! 1079: int target_insn_range; ! 1080: int current_pc; ! 1081: int table_size; ! 1082: ! 1083: void ! 1084: adjust_insn_length (insn, insn_lengths) ! 1085: rtx insn; ! 1086: short *insn_lengths; ! 1087: { ! 1088: int uid = INSN_UID (insn); ! 1089: ! 1090: current_pc += insn_lengths[uid]; ! 1091: ! 1092: if (has_constant_table (insn)) ! 1093: { ! 1094: if (current_pc >= target_insn_range) ! 1095: { ! 1096: /* This instruction is further away from the referencing ! 1097: instruction than it can reach, so we'll stop accumulating ! 1098: from that one and start fresh. */ ! 1099: target_pc = current_pc; ! 1100: target_insn_range = current_pc + MAYBE_DUMP_LEVEL; ! 1101: } ! 1102: else ! 1103: { ! 1104: /* This instruction is within the reach of the target, ! 1105: remove the constant table from the target by adjusting ! 1106: downwards, and increase the size of this one to ! 1107: compensate. */ ! 1108: ! 1109: ! 1110: /* Add the stuff from this insn to what will go in the ! 1111: growing table. */ ! 1112: ! 1113: table_size += get_attr_constantsize (insn); ! 1114: ! 1115: /* The target shinks to its smallest natural size */ ! 1116: insn_lengths[target_insn_uid] = target_insn_smallest_size; ! 1117: ! 1118: /* The current insn grows to be its larger size plust the ! 1119: table size. */ ! 1120: ! 1121: insn_lengths[uid] = get_attr_largestsize (insn) + table_size; ! 1122: ! 1123: } ! 1124: /* Current insn becomes the target. */ ! 1125: target_insn_uid = uid; ! 1126: target_insn_smallest_size = get_attr_smallestsize (insn); ! 1127: ! 1128: } ! 1129: ! 1130: } ! 1131: ! 1132: ! 1133: /* Dump out the pending constant pool. ! 1134: If label provided then insert an branch in the middle of the table ! 1135: */ ! 1136: ! 1137: static int ! 1138: dump_constants (label) ! 1139: { ! 1140: int i; ! 1141: int rlabel = label; ! 1142: int size = 0; ! 1143: ! 1144: for (i = 0; i < pool_size; i++) ! 1145: { ! 1146: pool_node *p = pool_vector + i; ! 1147: fprintf (asm_out_file, "\n\t! constants - waited %d\n", pc - first_pc); ! 1148: fprintf (asm_out_file, "\t.align\t2\n"); ! 1149: fprintf (asm_out_file, "LK%d:", p->number); ! 1150: size += GET_MODE_SIZE (p->mode); ! 1151: ! 1152: switch (GET_MODE_CLASS (p->mode)) ! 1153: { ! 1154: case MODE_INT: ! 1155: case MODE_PARTIAL_INT: ! 1156: assemble_integer (p->value, GET_MODE_SIZE (p->mode), 1); ! 1157: break; ! 1158: case MODE_FLOAT: ! 1159: { ! 1160: union real_extract u; ! 1161: bcopy (&CONST_DOUBLE_LOW (p->value), &u, sizeof u); ! 1162: assemble_real (u.d, p->mode); ! 1163: } ! 1164: } ! 1165: ! 1166: /* After 200 bytes of table, stick in another branch */ ! 1167: if (label && size > 200) ! 1168: { ! 1169: rlabel = lf ++; ! 1170: fprintf (asm_out_file,"LF%d:\tbra LF%d\n", label, rlabel); ! 1171: fprintf (asm_out_file,"\tor r0,r0\n"); ! 1172: label = 0; ! 1173: } ! 1174: ! 1175: fprintf (asm_out_file, "\n"); ! 1176: } ! 1177: pool_size = 0; ! 1178: current_pc = 0; ! 1179: target_insn_range = 0; ! 1180: return rlabel; ! 1181: ! 1182: } ! 1183: ! 1184: ! 1185: /* Emit the text to load a value from a constant table. */ ! 1186: ! 1187: char * ! 1188: output_movepcrel (insn, operands, mode) ! 1189: rtx insn; ! 1190: rtx operands[]; ! 1191: enum machine_mode mode; ! 1192: { ! 1193: int len = GET_MODE_SIZE (mode); ! 1194: int rn = REGNO (operands[0]); ! 1195: ! 1196: fprintf (asm_out_file, "\tmov.l LK%d,r%d\n", ! 1197: add_constant (operands[1], mode), rn); ! 1198: ! 1199: if (GET_MODE_SIZE(mode) > 4) ! 1200: { ! 1201: fprintf (asm_out_file, ! 1202: "\tmov.l LK%d+4,r%d\n", ! 1203: add_constant (operands[1], mode), ! 1204: rn + 1); ! 1205: ! 1206: } ! 1207: /* If this instruction is as small as it can be, there can be no ! 1208: constant table attached to it. */ ! 1209: if (get_attr_length (insn) != get_attr_smallestsize (insn)) ! 1210: { ! 1211: /* This needs a constant table */ ! 1212: fprintf (asm_out_file, "\t!constant table start\n"); ! 1213: fprintf (asm_out_file, "\tbra LF%d\n", lf); ! 1214: fprintf (asm_out_file, "\tor r0,r0 ! wasted slot\n"); ! 1215: dump_constants (0); ! 1216: fprintf (asm_out_file, "LF%d:\n", lf++); ! 1217: fprintf (asm_out_file, "\t!constant table end\n"); ! 1218: } ! 1219: return ""; ! 1220: } ! 1221: ! 1222: ! 1223: /* Dump out interesting debug info */ ! 1224: ! 1225: void ! 1226: final_prescan_insn (insn, opvec, noperands) ! 1227: rtx insn; ! 1228: rtx *opvec; ! 1229: int noperands; ! 1230: { ! 1231: register rtx body = PATTERN (insn); ! 1232: ! 1233: if (target_flags & ISIZE_BIT) ! 1234: { ! 1235: extern int *insn_addresses; ! 1236: ! 1237: fprintf (asm_out_file, "\n!%04x*\n", ! 1238: insn_addresses[INSN_UID (insn)] + 0x10); ! 1239: ! 1240: fprintf (asm_out_file, "\n!%04x %d %04x len=%d\n", ! 1241: pc, pool_size, first_pc, get_attr_length (insn)); ! 1242: ! 1243: if (TARGET_DUMP_RTL) ! 1244: print_rtl (asm_out_file, body); ! 1245: ! 1246: ! 1247: } ! 1248: ! 1249: pc += get_attr_length (insn); ! 1250: ! 1251: if (pool_size && pc - first_pc > MUST_DUMP_LEVEL) ! 1252: { ! 1253: /* For some reason we have not dumped out a constant table, and ! 1254: we have emitted a lot of code. This can happen if the think ! 1255: which wants the table is a long conditional branch (which has no ! 1256: room for a constant table), and there has not been a move ! 1257: constant anywhere. */ ! 1258: int label = lf++; ! 1259: fprintf (asm_out_file, "\t!forced constant table\n"); ! 1260: fprintf (asm_out_file, "\tbra LF%d\n", label); ! 1261: fprintf (asm_out_file, "\tor r0,r0 ! wasted slot\n"); ! 1262: label = dump_constants (label); ! 1263: fprintf (asm_out_file, "LF%d:\n", label); ! 1264: fprintf (asm_out_file, "\t!constant table end\n"); ! 1265: } ! 1266: ! 1267: } ! 1268: ! 1269:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.