|
|
1.1 ! root 1: /* Subroutines for insn-output.c for MIPS ! 2: Contributed by A. Lichnewsky, [email protected]. ! 3: Changes by Michael Meissner, [email protected]. ! 4: Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc. ! 5: ! 6: This file is part of GNU CC. ! 7: ! 8: GNU CC is free software; you can redistribute it and/or modify ! 9: it under the terms of the GNU General Public License as published by ! 10: the Free Software Foundation; either version 2, or (at your option) ! 11: any later version. ! 12: ! 13: GNU CC is distributed in the hope that it will be useful, ! 14: but WITHOUT ANY WARRANTY; without even the implied warranty of ! 15: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ! 16: GNU General Public License for more details. ! 17: ! 18: You should have received a copy of the GNU General Public License ! 19: along with GNU CC; see the file COPYING. If not, write to ! 20: the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ ! 21: ! 22: #include "config.h" ! 23: #include "rtl.h" ! 24: #include "regs.h" ! 25: #include "hard-reg-set.h" ! 26: #include "real.h" ! 27: #include "insn-config.h" ! 28: #include "conditions.h" ! 29: #include "insn-flags.h" ! 30: #include "insn-attr.h" ! 31: #include "insn-codes.h" ! 32: #include "recog.h" ! 33: #include "output.h" ! 34: ! 35: #undef MAX /* sys/param.h may also define these */ ! 36: #undef MIN ! 37: ! 38: #include <stdio.h> ! 39: #include <signal.h> ! 40: #include <sys/types.h> ! 41: #include <sys/file.h> ! 42: #include <ctype.h> ! 43: #include "tree.h" ! 44: #include "expr.h" ! 45: #include "flags.h" ! 46: ! 47: #ifndef R_OK ! 48: #define R_OK 4 ! 49: #define W_OK 2 ! 50: #define X_OK 1 ! 51: #endif ! 52: ! 53: #if defined(USG) || defined(NO_STAB_H) ! 54: #include "gstab.h" /* If doing DBX on sysV, use our own stab.h. */ ! 55: #else ! 56: #include <stab.h> /* On BSD, use the system's stab.h. */ ! 57: #endif /* not USG */ ! 58: ! 59: #ifdef __GNU_STAB__ ! 60: #define STAB_CODE_TYPE enum __stab_debug_code ! 61: #else ! 62: #define STAB_CODE_TYPE int ! 63: #endif ! 64: ! 65: extern void abort (); ! 66: extern int atoi (); ! 67: extern char *getenv (); ! 68: extern char *mktemp (); ! 69: ! 70: extern rtx adj_offsettable_operand (); ! 71: extern rtx copy_to_reg (); ! 72: extern void error (); ! 73: extern void fatal (); ! 74: extern tree lookup_name (); ! 75: extern void pfatal_with_name (); ! 76: extern void warning (); ! 77: ! 78: extern tree current_function_decl; ! 79: extern FILE *asm_out_file; ! 80: ! 81: /* Enumeration for all of the relational tests, so that we can build ! 82: arrays indexed by the test type, and not worry about the order ! 83: of EQ, NE, etc. */ ! 84: ! 85: enum internal_test { ! 86: ITEST_EQ, ! 87: ITEST_NE, ! 88: ITEST_GT, ! 89: ITEST_GE, ! 90: ITEST_LT, ! 91: ITEST_LE, ! 92: ITEST_GTU, ! 93: ITEST_GEU, ! 94: ITEST_LTU, ! 95: ITEST_LEU, ! 96: ITEST_MAX ! 97: }; ! 98: ! 99: /* Global variables for machine-dependent things. */ ! 100: ! 101: /* Threshold for data being put into the small data/bss area, instead ! 102: of the normal data area (references to the small data/bss area take ! 103: 1 instruction, and use the global pointer, references to the normal ! 104: data area takes 2 instructions). */ ! 105: int mips_section_threshold = -1; ! 106: ! 107: /* Count the number of .file directives, so that .loc is up to date. */ ! 108: int num_source_filenames = 0; ! 109: ! 110: /* Count the number of sdb related labels are generated (to find block ! 111: start and end boundaries). */ ! 112: int sdb_label_count = 0; ! 113: ! 114: /* Next label # for each statment for Silicon Graphics IRIS systems. */ ! 115: int sym_lineno = 0; ! 116: ! 117: /* Non-zero if inside of a function, because the stupid MIPS asm can't ! 118: handle .files inside of functions. */ ! 119: int inside_function = 0; ! 120: ! 121: /* Files to separate the text and the data output, so that all of the data ! 122: can be emitted before the text, which will mean that the assembler will ! 123: generate smaller code, based on the global pointer. */ ! 124: FILE *asm_out_data_file; ! 125: FILE *asm_out_text_file; ! 126: ! 127: /* Linked list of all externals that are to be emitted when optimizing ! 128: for the global pointer if they haven't been declared by the end of ! 129: the program with an appropriate .comm or initialization. */ ! 130: ! 131: struct extern_list { ! 132: struct extern_list *next; /* next external */ ! 133: char *name; /* name of the external */ ! 134: int size; /* size in bytes */ ! 135: } *extern_head = 0; ! 136: ! 137: /* Name of the file containing the current function. */ ! 138: char *current_function_file = ""; ! 139: ! 140: /* Warning given that Mips ECOFF can't support changing files ! 141: within a function. */ ! 142: int file_in_function_warning = FALSE; ! 143: ! 144: /* Whether to suppress issuing .loc's because the user attempted ! 145: to change the filename within a function. */ ! 146: int ignore_line_number = FALSE; ! 147: ! 148: /* Number of nested .set noreorder, noat, nomacro, and volatile requests. */ ! 149: int set_noreorder; ! 150: int set_noat; ! 151: int set_nomacro; ! 152: int set_volatile; ! 153: ! 154: /* The next branch instruction is a branch likely, not branch normal. */ ! 155: int mips_branch_likely; ! 156: ! 157: /* Count of delay slots and how many are filled. */ ! 158: int dslots_load_total; ! 159: int dslots_load_filled; ! 160: int dslots_jump_total; ! 161: int dslots_jump_filled; ! 162: ! 163: /* # of nops needed by previous insn */ ! 164: int dslots_number_nops; ! 165: ! 166: /* Number of 1/2/3 word references to data items (ie, not jal's). */ ! 167: int num_refs[3]; ! 168: ! 169: /* registers to check for load delay */ ! 170: rtx mips_load_reg, mips_load_reg2, mips_load_reg3, mips_load_reg4; ! 171: ! 172: /* Cached operands, and operator to compare for use in set/branch on ! 173: condition codes. */ ! 174: rtx branch_cmp[2]; ! 175: ! 176: /* what type of branch to use */ ! 177: enum cmp_type branch_type; ! 178: ! 179: /* Number of previously seen half-pic pointers and references. */ ! 180: static int prev_half_pic_ptrs = 0; ! 181: static int prev_half_pic_refs = 0; ! 182: ! 183: /* which cpu are we scheduling for */ ! 184: enum processor_type mips_cpu; ! 185: ! 186: /* which instruction set architecture to use. */ ! 187: int mips_isa; ! 188: ! 189: /* Strings to hold which cpu and instruction set architecture to use. */ ! 190: char *mips_cpu_string; /* for -mcpu=<xxx> */ ! 191: char *mips_isa_string; /* for -mips{1,2,3} */ ! 192: ! 193: /* Array to RTX class classification. At present, we care about ! 194: whether the operator is an add-type operator, or a divide/modulus, ! 195: and if divide/modulus, whether it is unsigned. This is for the ! 196: peephole code. */ ! 197: char mips_rtx_classify[NUM_RTX_CODE]; ! 198: ! 199: /* Array giving truth value on whether or not a given hard register ! 200: can support a given mode. */ ! 201: char mips_hard_regno_mode_ok[(int)MAX_MACHINE_MODE][FIRST_PSEUDO_REGISTER]; ! 202: ! 203: /* Current frame information calculated by compute_frame_size. */ ! 204: struct mips_frame_info current_frame_info; ! 205: ! 206: /* Zero structure to initialize current_frame_info. */ ! 207: struct mips_frame_info zero_frame_info; ! 208: ! 209: /* Temporary filename used to buffer .text until end of program ! 210: for -mgpopt. */ ! 211: static char *temp_filename; ! 212: ! 213: /* List of all MIPS punctuation characters used by print_operand. */ ! 214: char mips_print_operand_punct[256]; ! 215: ! 216: /* Map GCC register number to debugger register number. */ ! 217: int mips_dbx_regno[FIRST_PSEUDO_REGISTER]; ! 218: ! 219: /* Buffer to use to enclose a load/store operation with %{ %} to ! 220: turn on .set volatile. */ ! 221: static char volatile_buffer[60]; ! 222: ! 223: /* Hardware names for the registers. If -mrnames is used, this ! 224: will be overwritten with mips_sw_reg_names. */ ! 225: ! 226: char mips_reg_names[][8] = ! 227: { ! 228: "$0", "$1", "$2", "$3", "$4", "$5", "$6", "$7", ! 229: "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", ! 230: "$16", "$17", "$18", "$19", "$20", "$21", "$22", "$23", ! 231: "$24", "$25", "$26", "$27", "$28", "$sp", "$fp", "$31", ! 232: "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", ! 233: "$f8", "$f9", "$f10", "$f11", "$f12", "$f13", "$f14", "$f15", ! 234: "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23", ! 235: "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31", ! 236: "hi", "lo", "$fcr31" ! 237: }; ! 238: ! 239: /* Mips software names for the registers, used to overwrite the ! 240: mips_reg_names array. */ ! 241: ! 242: char mips_sw_reg_names[][8] = ! 243: { ! 244: "$0", "at", "v0", "v1", "a0", "a1", "a2", "a3", ! 245: "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", ! 246: "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", ! 247: "t8", "t9", "k0", "k1", "gp", "sp", "$fp", "ra", ! 248: "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", ! 249: "$f8", "$f9", "$f10", "$f11", "$f12", "$f13", "$f14", "$f15", ! 250: "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23", ! 251: "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31", ! 252: "hi", "lo", "$fcr31" ! 253: }; ! 254: ! 255: /* Map hard register number to register class */ ! 256: enum reg_class mips_regno_to_class[] = ! 257: { ! 258: GR_REGS, GR_REGS, GR_REGS, GR_REGS, ! 259: GR_REGS, GR_REGS, GR_REGS, GR_REGS, ! 260: GR_REGS, GR_REGS, GR_REGS, GR_REGS, ! 261: GR_REGS, GR_REGS, GR_REGS, GR_REGS, ! 262: GR_REGS, GR_REGS, GR_REGS, GR_REGS, ! 263: GR_REGS, GR_REGS, GR_REGS, GR_REGS, ! 264: GR_REGS, GR_REGS, GR_REGS, GR_REGS, ! 265: GR_REGS, GR_REGS, GR_REGS, GR_REGS, ! 266: FP_REGS, FP_REGS, FP_REGS, FP_REGS, ! 267: FP_REGS, FP_REGS, FP_REGS, FP_REGS, ! 268: FP_REGS, FP_REGS, FP_REGS, FP_REGS, ! 269: FP_REGS, FP_REGS, FP_REGS, FP_REGS, ! 270: FP_REGS, FP_REGS, FP_REGS, FP_REGS, ! 271: FP_REGS, FP_REGS, FP_REGS, FP_REGS, ! 272: FP_REGS, FP_REGS, FP_REGS, FP_REGS, ! 273: FP_REGS, FP_REGS, FP_REGS, FP_REGS, ! 274: HI_REG, LO_REG, ST_REGS ! 275: }; ! 276: ! 277: /* Map register constraint character to register class. */ ! 278: enum reg_class mips_char_to_class[256] = ! 279: { ! 280: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 281: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 282: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 283: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 284: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 285: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 286: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 287: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 288: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 289: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 290: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 291: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 292: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 293: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 294: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 295: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 296: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 297: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 298: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 299: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 300: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 301: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 302: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 303: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 304: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 305: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 306: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 307: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 308: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 309: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 310: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 311: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 312: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 313: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 314: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 315: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 316: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 317: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 318: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 319: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 320: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 321: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 322: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 323: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 324: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 325: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 326: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 327: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 328: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 329: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 330: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 331: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 332: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 333: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 334: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 335: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 336: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 337: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 338: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 339: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 340: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 341: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 342: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 343: NO_REGS, NO_REGS, NO_REGS, NO_REGS, ! 344: }; ! 345: ! 346: ! 347: /* Return truth value of whether OP can be used as an operands ! 348: where a register or 16 bit unsigned integer is needed. */ ! 349: ! 350: int ! 351: uns_arith_operand (op, mode) ! 352: rtx op; ! 353: enum machine_mode mode; ! 354: { ! 355: if (GET_CODE (op) == CONST_INT && SMALL_INT_UNSIGNED (op)) ! 356: return TRUE; ! 357: ! 358: return register_operand (op, mode); ! 359: } ! 360: ! 361: /* Return truth value of whether OP can be used as an operands ! 362: where a 16 bit integer is needed */ ! 363: ! 364: int ! 365: arith_operand (op, mode) ! 366: rtx op; ! 367: enum machine_mode mode; ! 368: { ! 369: if (GET_CODE (op) == CONST_INT && SMALL_INT (op)) ! 370: return TRUE; ! 371: ! 372: return register_operand (op, mode); ! 373: } ! 374: ! 375: /* Return truth value of whether OP can be used as an operand in a two ! 376: address arithmetic insn (such as set 123456,%o4) of mode MODE. */ ! 377: ! 378: int ! 379: arith32_operand (op, mode) ! 380: rtx op; ! 381: enum machine_mode mode; ! 382: { ! 383: if (GET_CODE (op) == CONST_INT) ! 384: return TRUE; ! 385: ! 386: return register_operand (op, mode); ! 387: } ! 388: ! 389: /* Return truth value of whether OP is a integer which fits in 16 bits */ ! 390: ! 391: int ! 392: small_int (op, mode) ! 393: rtx op; ! 394: enum machine_mode mode; ! 395: { ! 396: return (GET_CODE (op) == CONST_INT && SMALL_INT (op)); ! 397: } ! 398: ! 399: /* Return truth value of whether OP is an integer which is too big to ! 400: be loaded with one instruction. */ ! 401: ! 402: int ! 403: large_int (op, mode) ! 404: rtx op; ! 405: enum machine_mode mode; ! 406: { ! 407: HOST_WIDE_INT value; ! 408: ! 409: if (GET_CODE (op) != CONST_INT) ! 410: return FALSE; ! 411: ! 412: value = INTVAL (op); ! 413: if ((value & ~0x0000ffff) == 0) /* ior reg,$r0,value */ ! 414: return FALSE; ! 415: ! 416: if (((unsigned long)(value + 32768)) <= 32767) /* subu reg,$r0,value */ ! 417: return FALSE; ! 418: ! 419: if ((value & 0xffff0000) == value) /* lui reg,value>>16 */ ! 420: return FALSE; ! 421: ! 422: return TRUE; ! 423: } ! 424: ! 425: /* Return truth value of whether OP is a register or the constant 0. */ ! 426: ! 427: int ! 428: reg_or_0_operand (op, mode) ! 429: rtx op; ! 430: enum machine_mode mode; ! 431: { ! 432: switch (GET_CODE (op)) ! 433: { ! 434: default: ! 435: break; ! 436: ! 437: case CONST_INT: ! 438: return (INTVAL (op) == 0); ! 439: ! 440: case CONST_DOUBLE: ! 441: if (CONST_DOUBLE_HIGH (op) != 0 || CONST_DOUBLE_LOW (op) != 0) ! 442: return FALSE; ! 443: ! 444: return TRUE; ! 445: ! 446: case REG: ! 447: case SUBREG: ! 448: return register_operand (op, mode); ! 449: } ! 450: ! 451: return FALSE; ! 452: } ! 453: ! 454: /* Return truth value of whether OP is one of the special multiply/divide ! 455: registers (hi, lo). */ ! 456: ! 457: int ! 458: md_register_operand (op, mode) ! 459: rtx op; ! 460: enum machine_mode mode; ! 461: { ! 462: return (GET_MODE_CLASS (mode) == MODE_INT ! 463: && GET_CODE (op) == REG ! 464: && MD_REG_P (REGNO (op))); ! 465: } ! 466: ! 467: /* Return truth value of whether OP is the FP status register. */ ! 468: ! 469: int ! 470: fpsw_register_operand (op, mode) ! 471: rtx op; ! 472: enum machine_mode mode; ! 473: { ! 474: return (GET_CODE (op) == REG && ST_REG_P (REGNO (op))); ! 475: } ! 476: ! 477: /* Return truth value if a CONST_DOUBLE is ok to be a legitimate constant. */ ! 478: ! 479: int ! 480: mips_const_double_ok (op, mode) ! 481: rtx op; ! 482: enum machine_mode mode; ! 483: { ! 484: if (GET_CODE (op) != CONST_DOUBLE) ! 485: return FALSE; ! 486: ! 487: if (mode == DImode) ! 488: return TRUE; ! 489: ! 490: if (mode != SFmode && mode != DFmode) ! 491: return FALSE; ! 492: ! 493: if (CONST_DOUBLE_HIGH (op) == 0 && CONST_DOUBLE_LOW (op) == 0) ! 494: return TRUE; ! 495: ! 496: #if HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT ! 497: if (TARGET_MIPS_AS) /* gas doesn't like li.d/li.s yet */ ! 498: { ! 499: union { double d; int i[2]; } u; ! 500: double d; ! 501: ! 502: u.i[0] = CONST_DOUBLE_LOW (op); ! 503: u.i[1] = CONST_DOUBLE_HIGH (op); ! 504: d = u.d; ! 505: ! 506: if (d != d) ! 507: return FALSE; /* NAN */ ! 508: ! 509: if (d < 0.0) ! 510: d = - d; ! 511: ! 512: /* Rather than trying to get the accuracy down to the last bit, ! 513: just use approximate ranges. */ ! 514: ! 515: if (mode == DFmode && d > 1.0e-300 && d < 1.0e300) ! 516: return TRUE; ! 517: ! 518: if (mode == SFmode && d > 1.0e-38 && d < 1.0e+38) ! 519: return TRUE; ! 520: } ! 521: #endif ! 522: ! 523: return FALSE; ! 524: } ! 525: ! 526: /* Return truth value if a memory operand fits in a single instruction ! 527: (ie, register + small offset). */ ! 528: ! 529: int ! 530: simple_memory_operand (op, mode) ! 531: rtx op; ! 532: enum machine_mode mode; ! 533: { ! 534: rtx addr, plus0, plus1; ! 535: ! 536: /* Eliminate non-memory operations */ ! 537: if (GET_CODE (op) != MEM) ! 538: return FALSE; ! 539: ! 540: /* dword operations really put out 2 instructions, so eliminate them. */ ! 541: if (GET_MODE_SIZE (GET_MODE (op)) > (HAVE_64BIT_P () ? 8 : 4)) ! 542: return FALSE; ! 543: ! 544: /* Decode the address now. */ ! 545: addr = XEXP (op, 0); ! 546: switch (GET_CODE (addr)) ! 547: { ! 548: default: ! 549: break; ! 550: ! 551: case REG: ! 552: return TRUE; ! 553: ! 554: case CONST_INT: ! 555: return SMALL_INT (op); ! 556: ! 557: case PLUS: ! 558: plus0 = XEXP (addr, 0); ! 559: plus1 = XEXP (addr, 1); ! 560: if (GET_CODE (plus0) == REG ! 561: && GET_CODE (plus1) == CONST_INT ! 562: && SMALL_INT (plus1)) ! 563: return TRUE; ! 564: ! 565: else if (GET_CODE (plus1) == REG ! 566: && GET_CODE (plus0) == CONST_INT ! 567: && SMALL_INT (plus0)) ! 568: return TRUE; ! 569: ! 570: else ! 571: return FALSE; ! 572: ! 573: #if 0 ! 574: /* We used to allow small symbol refs here (ie, stuff in .sdata ! 575: or .sbss), but this causes some bugs in G++. Also, it won't ! 576: interfere if the MIPS linker rewrites the store instruction ! 577: because the function is PIC. */ ! 578: ! 579: case LABEL_REF: /* never gp relative */ ! 580: break; ! 581: ! 582: case CONST: ! 583: /* If -G 0, we can never have a GP relative memory operation. ! 584: Also, save some time if not optimizing. */ ! 585: if (mips_section_threshold == 0 || !optimize || !TARGET_GP_OPT) ! 586: return FALSE; ! 587: ! 588: { ! 589: rtx offset = const0_rtx; ! 590: addr = eliminate_constant_term (addr, &offset); ! 591: if (GET_CODE (op) != SYMBOL_REF) ! 592: return FALSE; ! 593: ! 594: /* let's be paranoid.... */ ! 595: if (INTVAL (offset) < 0 || INTVAL (offset) > 0xffff) ! 596: return FALSE; ! 597: } ! 598: /* fall through */ ! 599: ! 600: case SYMBOL_REF: ! 601: return SYMBOL_REF_FLAG (addr); ! 602: #endif ! 603: } ! 604: ! 605: return FALSE; ! 606: } ! 607: ! 608: /* Return true if the code of this rtx pattern is EQ or NE. */ ! 609: ! 610: int ! 611: equality_op (op, mode) ! 612: rtx op; ! 613: enum machine_mode mode; ! 614: { ! 615: if (mode != GET_MODE (op)) ! 616: return FALSE; ! 617: ! 618: return (classify_op (op, mode) & CLASS_EQUALITY_OP) != 0; ! 619: } ! 620: ! 621: /* Return true if the code is a relational operations (EQ, LE, etc.) */ ! 622: ! 623: int ! 624: cmp_op (op, mode) ! 625: rtx op; ! 626: enum machine_mode mode; ! 627: { ! 628: if (mode != GET_MODE (op)) ! 629: return FALSE; ! 630: ! 631: return (classify_op (op, mode) & CLASS_CMP_OP) != 0; ! 632: } ! 633: ! 634: ! 635: /* Genrecog does not take the type of match_operator into consideration, ! 636: and would complain about two patterns being the same if the same ! 637: function is used, so make it believe they are different. */ ! 638: ! 639: int ! 640: cmp2_op (op, mode) ! 641: rtx op; ! 642: enum machine_mode mode; ! 643: { ! 644: if (mode != GET_MODE (op)) ! 645: return FALSE; ! 646: ! 647: return (classify_op (op, mode) & CLASS_CMP_OP) != 0; ! 648: } ! 649: ! 650: /* Return true if the code is an unsigned relational operations (LEU, etc.) */ ! 651: ! 652: int ! 653: uns_cmp_op (op,mode) ! 654: rtx op; ! 655: enum machine_mode mode; ! 656: { ! 657: if (mode != GET_MODE (op)) ! 658: return FALSE; ! 659: ! 660: return (classify_op (op, mode) & CLASS_UNS_CMP_OP) == CLASS_UNS_CMP_OP; ! 661: } ! 662: ! 663: /* Return true if the code is a relational operation FP can use. */ ! 664: ! 665: int ! 666: fcmp_op (op, mode) ! 667: rtx op; ! 668: enum machine_mode mode; ! 669: { ! 670: if (mode != GET_MODE (op)) ! 671: return FALSE; ! 672: ! 673: return (classify_op (op, mode) & CLASS_FCMP_OP) != 0; ! 674: } ! 675: ! 676: ! 677: /* Return true if the operand is either the PC or a label_ref. */ ! 678: ! 679: int ! 680: pc_or_label_operand (op, mode) ! 681: rtx op; ! 682: enum machine_mode mode; ! 683: { ! 684: if (op == pc_rtx) ! 685: return TRUE; ! 686: ! 687: if (GET_CODE (op) == LABEL_REF) ! 688: return TRUE; ! 689: ! 690: return FALSE; ! 691: } ! 692: ! 693: /* Test for a valid operand for a call instruction. ! 694: Don't allow the arg pointer register or virtual regs ! 695: since they may change into reg + const, which the patterns ! 696: can't handle yet. */ ! 697: ! 698: int ! 699: call_insn_operand (op, mode) ! 700: rtx op; ! 701: enum machine_mode mode; ! 702: { ! 703: if (GET_CODE (op) == MEM ! 704: && (CONSTANT_ADDRESS_P (XEXP (op, 0)) ! 705: || (GET_CODE (XEXP (op, 0)) == REG ! 706: && XEXP (op, 0) != arg_pointer_rtx ! 707: && !(REGNO (XEXP (op, 0)) >= FIRST_PSEUDO_REGISTER ! 708: && REGNO (XEXP (op, 0)) <= LAST_VIRTUAL_REGISTER)))) ! 709: return 1; ! 710: return 0; ! 711: } ! 712: ! 713: /* Return an operand string if the given instruction's delay slot or ! 714: wrap it in a .set noreorder section. This is for filling delay ! 715: slots on load type instructions under GAS, which does no reordering ! 716: on its own. For the MIPS assembler, all we do is update the filled ! 717: delay slot statistics. ! 718: ! 719: We assume that operands[0] is the target register that is set. ! 720: ! 721: In order to check the next insn, most of this functionality is moved ! 722: to FINAL_PRESCAN_INSN, and we just set the global variables that ! 723: it needs. */ ! 724: ! 725: char * ! 726: mips_fill_delay_slot (ret, type, operands, cur_insn) ! 727: char *ret; /* normal string to return */ ! 728: enum delay_type type; /* type of delay */ ! 729: rtx operands[]; /* operands to use */ ! 730: rtx cur_insn; /* current insn */ ! 731: { ! 732: register rtx set_reg; ! 733: register enum machine_mode mode; ! 734: register rtx next_insn = (cur_insn) ? NEXT_INSN (cur_insn) : (rtx)0; ! 735: register int num_nops; ! 736: ! 737: if (type == DELAY_LOAD || type == DELAY_FCMP) ! 738: num_nops = 1; ! 739: ! 740: else if (type == DELAY_HILO) ! 741: num_nops = 2; ! 742: ! 743: else ! 744: num_nops = 0; ! 745: ! 746: /* Make sure that we don't put nop's after labels. */ ! 747: next_insn = NEXT_INSN (cur_insn); ! 748: while (next_insn != (rtx)0 && GET_CODE (next_insn) == NOTE) ! 749: next_insn = NEXT_INSN (next_insn); ! 750: ! 751: dslots_load_total += num_nops; ! 752: if (TARGET_DEBUG_F_MODE ! 753: || !optimize ! 754: || type == DELAY_NONE ! 755: || operands == (rtx *)0 ! 756: || cur_insn == (rtx)0 ! 757: || next_insn == (rtx)0 ! 758: || GET_CODE (next_insn) == CODE_LABEL ! 759: || (set_reg = operands[0]) == (rtx)0) ! 760: { ! 761: dslots_number_nops = 0; ! 762: mips_load_reg = (rtx)0; ! 763: mips_load_reg2 = (rtx)0; ! 764: mips_load_reg3 = (rtx)0; ! 765: mips_load_reg4 = (rtx)0; ! 766: return ret; ! 767: } ! 768: ! 769: set_reg = operands[0]; ! 770: if (set_reg == (rtx)0) ! 771: return ret; ! 772: ! 773: while (GET_CODE (set_reg) == SUBREG) ! 774: set_reg = SUBREG_REG (set_reg); ! 775: ! 776: mode = GET_MODE (set_reg); ! 777: dslots_number_nops = num_nops; ! 778: mips_load_reg = set_reg; ! 779: mips_load_reg2 = (mode == DImode || mode == DFmode) ! 780: ? gen_rtx (REG, SImode, REGNO (set_reg) + 1) ! 781: : (rtx)0; ! 782: ! 783: if (type == DELAY_HILO) ! 784: { ! 785: mips_load_reg3 = gen_rtx (REG, SImode, MD_REG_FIRST); ! 786: mips_load_reg4 = gen_rtx (REG, SImode, MD_REG_FIRST+1); ! 787: } ! 788: else ! 789: { ! 790: mips_load_reg3 = 0; ! 791: mips_load_reg4 = 0; ! 792: } ! 793: ! 794: if (TARGET_GAS && set_noreorder++ == 0) ! 795: fputs ("\t.set\tnoreorder\n", asm_out_file); ! 796: ! 797: return ret; ! 798: } ! 799: ! 800: ! 801: /* Determine whether a memory reference takes one (based off of the GP pointer), ! 802: two (normal), or three (label + reg) instructions, and bump the appropriate ! 803: counter for -mstats. */ ! 804: ! 805: void ! 806: mips_count_memory_refs (op, num) ! 807: rtx op; ! 808: int num; ! 809: { ! 810: int additional = 0; ! 811: int n_words = 0; ! 812: rtx addr, plus0, plus1; ! 813: enum rtx_code code0, code1; ! 814: int looping; ! 815: ! 816: if (TARGET_DEBUG_B_MODE) ! 817: { ! 818: fprintf (stderr, "\n========== mips_count_memory_refs:\n"); ! 819: debug_rtx (op); ! 820: } ! 821: ! 822: /* Skip MEM if passed, otherwise handle movsi of address. */ ! 823: addr = (GET_CODE (op) != MEM) ? op : XEXP (op, 0); ! 824: ! 825: /* Loop, going through the address RTL */ ! 826: do ! 827: { ! 828: looping = FALSE; ! 829: switch (GET_CODE (addr)) ! 830: { ! 831: default: ! 832: break; ! 833: ! 834: case REG: ! 835: case CONST_INT: ! 836: break; ! 837: ! 838: case PLUS: ! 839: plus0 = XEXP (addr, 0); ! 840: plus1 = XEXP (addr, 1); ! 841: code0 = GET_CODE (plus0); ! 842: code1 = GET_CODE (plus1); ! 843: ! 844: if (code0 == REG) ! 845: { ! 846: additional++; ! 847: addr = plus1; ! 848: looping = TRUE; ! 849: continue; ! 850: } ! 851: ! 852: if (code0 == CONST_INT) ! 853: { ! 854: addr = plus1; ! 855: looping = TRUE; ! 856: continue; ! 857: } ! 858: ! 859: if (code1 == REG) ! 860: { ! 861: additional++; ! 862: addr = plus0; ! 863: looping = TRUE; ! 864: continue; ! 865: } ! 866: ! 867: if (code1 == CONST_INT) ! 868: { ! 869: addr = plus0; ! 870: looping = TRUE; ! 871: continue; ! 872: } ! 873: ! 874: if (code0 == SYMBOL_REF || code0 == LABEL_REF || code0 == CONST) ! 875: { ! 876: addr = plus0; ! 877: looping = TRUE; ! 878: continue; ! 879: } ! 880: ! 881: if (code1 == SYMBOL_REF || code1 == LABEL_REF || code1 == CONST) ! 882: { ! 883: addr = plus1; ! 884: looping = TRUE; ! 885: continue; ! 886: } ! 887: ! 888: break; ! 889: ! 890: case LABEL_REF: ! 891: n_words = 2; /* always 2 words */ ! 892: break; ! 893: ! 894: case CONST: ! 895: addr = XEXP (addr, 0); ! 896: looping = TRUE; ! 897: continue; ! 898: ! 899: case SYMBOL_REF: ! 900: n_words = SYMBOL_REF_FLAG (addr) ? 1 : 2; ! 901: break; ! 902: } ! 903: } ! 904: while (looping); ! 905: ! 906: if (n_words == 0) ! 907: return; ! 908: ! 909: n_words += additional; ! 910: if (n_words > 3) ! 911: n_words = 3; ! 912: ! 913: num_refs[n_words-1] += num; ! 914: } ! 915: ! 916: ! 917: /* Return the appropriate instructions to move one operand to another. */ ! 918: ! 919: char * ! 920: mips_move_1word (operands, insn, unsignedp) ! 921: rtx operands[]; ! 922: rtx insn; ! 923: int unsignedp; ! 924: { ! 925: char *ret = 0; ! 926: rtx op0 = operands[0]; ! 927: rtx op1 = operands[1]; ! 928: enum rtx_code code0 = GET_CODE (op0); ! 929: enum rtx_code code1 = GET_CODE (op1); ! 930: enum machine_mode mode = GET_MODE (op0); ! 931: int subreg_word0 = 0; ! 932: int subreg_word1 = 0; ! 933: enum delay_type delay = DELAY_NONE; ! 934: ! 935: while (code0 == SUBREG) ! 936: { ! 937: subreg_word0 += SUBREG_WORD (op0); ! 938: op0 = SUBREG_REG (op0); ! 939: code0 = GET_CODE (op0); ! 940: } ! 941: ! 942: while (code1 == SUBREG) ! 943: { ! 944: subreg_word1 += SUBREG_WORD (op1); ! 945: op1 = SUBREG_REG (op1); ! 946: code1 = GET_CODE (op1); ! 947: } ! 948: ! 949: if (code0 == REG) ! 950: { ! 951: int regno0 = REGNO (op0) + subreg_word0; ! 952: ! 953: if (code1 == REG) ! 954: { ! 955: int regno1 = REGNO (op1) + subreg_word1; ! 956: ! 957: /* Just in case, don't do anything for assigning a register ! 958: to itself, unless we are filling a delay slot. */ ! 959: if (regno0 == regno1 && set_nomacro == 0) ! 960: ret = ""; ! 961: ! 962: else if (GP_REG_P (regno0)) ! 963: { ! 964: if (GP_REG_P (regno1)) ! 965: ret = "move\t%0,%1"; ! 966: ! 967: else if (MD_REG_P (regno1)) ! 968: { ! 969: delay = DELAY_HILO; ! 970: ret = "mf%1\t%0"; ! 971: } ! 972: ! 973: else ! 974: { ! 975: delay = DELAY_LOAD; ! 976: if (FP_REG_P (regno1)) ! 977: ret = "mfc1\t%0,%1"; ! 978: ! 979: else if (regno1 == FPSW_REGNUM) ! 980: ret = "cfc1\t%0,$31"; ! 981: } ! 982: } ! 983: ! 984: else if (FP_REG_P (regno0)) ! 985: { ! 986: if (GP_REG_P (regno1)) ! 987: { ! 988: delay = DELAY_LOAD; ! 989: ret = "mtc1\t%1,%0"; ! 990: } ! 991: ! 992: if (FP_REG_P (regno1)) ! 993: ret = "mov.s\t%0,%1"; ! 994: } ! 995: ! 996: else if (MD_REG_P (regno0)) ! 997: { ! 998: if (GP_REG_P (regno1)) ! 999: { ! 1000: delay = DELAY_HILO; ! 1001: ret = "mt%0\t%1"; ! 1002: } ! 1003: } ! 1004: ! 1005: else if (regno0 == FPSW_REGNUM) ! 1006: { ! 1007: if (GP_REG_P (regno1)) ! 1008: { ! 1009: delay = DELAY_LOAD; ! 1010: ret = "ctc1\t%0,$31"; ! 1011: } ! 1012: } ! 1013: } ! 1014: ! 1015: else if (code1 == MEM) ! 1016: { ! 1017: delay = DELAY_LOAD; ! 1018: ! 1019: if (TARGET_STATS) ! 1020: mips_count_memory_refs (op1, 1); ! 1021: ! 1022: if (GP_REG_P (regno0)) ! 1023: { ! 1024: /* For loads, use the mode of the memory item, instead of the ! 1025: target, so zero/sign extend can use this code as well. */ ! 1026: switch (GET_MODE (op1)) ! 1027: { ! 1028: default: break; ! 1029: case SFmode: ret = "lw\t%0,%1"; break; ! 1030: case SImode: ret = "lw\t%0,%1"; break; ! 1031: case HImode: ret = (unsignedp) ? "lhu\t%0,%1" : "lh\t%0,%1"; break; ! 1032: case QImode: ret = (unsignedp) ? "lbu\t%0,%1" : "lb\t%0,%1"; break; ! 1033: } ! 1034: } ! 1035: ! 1036: else if (FP_REG_P (regno0) && (mode == SImode || mode == SFmode)) ! 1037: ret = "l.s\t%0,%1"; ! 1038: ! 1039: if (ret != (char *)0 && MEM_VOLATILE_P (op1)) ! 1040: { ! 1041: int i = strlen (ret); ! 1042: if (i > sizeof (volatile_buffer) - sizeof ("%{%}")) ! 1043: abort (); ! 1044: ! 1045: sprintf (volatile_buffer, "%%{%s%%}", ret); ! 1046: ret = volatile_buffer; ! 1047: } ! 1048: } ! 1049: ! 1050: else if (code1 == CONST_INT) ! 1051: { ! 1052: if (INTVAL (op1) == 0) ! 1053: { ! 1054: if (GP_REG_P (regno0)) ! 1055: ret = "move\t%0,%z1"; ! 1056: ! 1057: else if (FP_REG_P (regno0)) ! 1058: { ! 1059: delay = DELAY_LOAD; ! 1060: ret = "mtc1\t%z1,%0"; ! 1061: } ! 1062: } ! 1063: ! 1064: else if (GP_REG_P (regno0)) ! 1065: ret = (INTVAL (op1) < 0) ? "li\t%0,%1\t\t\t# %X1" : "li\t%0,%X1\t\t# %1"; ! 1066: } ! 1067: ! 1068: else if (code1 == CONST_DOUBLE && mode == SFmode) ! 1069: { ! 1070: if (CONST_DOUBLE_HIGH (op1) == 0 && CONST_DOUBLE_LOW (op1) == 0) ! 1071: { ! 1072: if (GP_REG_P (regno0)) ! 1073: ret = "move\t%0,%."; ! 1074: ! 1075: else if (FP_REG_P (regno0)) ! 1076: { ! 1077: delay = DELAY_LOAD; ! 1078: ret = "mtc1\t%.,%0"; ! 1079: } ! 1080: } ! 1081: ! 1082: else ! 1083: { ! 1084: delay = DELAY_LOAD; ! 1085: ret = "li.s\t%0,%1"; ! 1086: } ! 1087: } ! 1088: ! 1089: else if (code1 == LABEL_REF) ! 1090: { ! 1091: if (TARGET_STATS) ! 1092: mips_count_memory_refs (op1, 1); ! 1093: ! 1094: ret = "la\t%0,%a1"; ! 1095: } ! 1096: ! 1097: else if (code1 == SYMBOL_REF || code1 == CONST) ! 1098: { ! 1099: if (HALF_PIC_P () && CONSTANT_P (op1) && HALF_PIC_ADDRESS_P (op1)) ! 1100: { ! 1101: rtx offset = const0_rtx; ! 1102: ! 1103: if (GET_CODE (op1) == CONST) ! 1104: op1 = eliminate_constant_term (XEXP (op1, 0), &offset); ! 1105: ! 1106: if (GET_CODE (op1) == SYMBOL_REF) ! 1107: { ! 1108: operands[2] = HALF_PIC_PTR (op1); ! 1109: ! 1110: if (TARGET_STATS) ! 1111: mips_count_memory_refs (operands[2], 1); ! 1112: ! 1113: if (INTVAL (offset) == 0) ! 1114: { ! 1115: delay = DELAY_LOAD; ! 1116: ret = "lw\t%0,%2"; ! 1117: } ! 1118: else ! 1119: { ! 1120: dslots_load_total++; ! 1121: operands[3] = offset; ! 1122: ret = (SMALL_INT (offset)) ! 1123: ? "lw\t%0,%2%#\n\tadd\t%0,%0,%3" ! 1124: : "lw\t%0,%2%#\n\t%[li\t%@,%3\n\tadd\t%0,%0,%@%]"; ! 1125: } ! 1126: } ! 1127: } ! 1128: else ! 1129: { ! 1130: if (TARGET_STATS) ! 1131: mips_count_memory_refs (op1, 1); ! 1132: ! 1133: ret = "la\t%0,%a1"; ! 1134: } ! 1135: } ! 1136: ! 1137: else if (code1 == PLUS) ! 1138: { ! 1139: rtx add_op0 = XEXP (op1, 0); ! 1140: rtx add_op1 = XEXP (op1, 1); ! 1141: ! 1142: if (GET_CODE (XEXP (op1, 1)) == REG && GET_CODE (XEXP (op1, 0)) == CONST_INT) ! 1143: { ! 1144: add_op0 = XEXP (op1, 1); /* reverse operands */ ! 1145: add_op1 = XEXP (op1, 0); ! 1146: } ! 1147: ! 1148: operands[2] = add_op0; ! 1149: operands[3] = add_op1; ! 1150: ret = "add%:\t%0,%2,%3"; ! 1151: } ! 1152: } ! 1153: ! 1154: else if (code0 == MEM) ! 1155: { ! 1156: if (TARGET_STATS) ! 1157: mips_count_memory_refs (op0, 1); ! 1158: ! 1159: if (code1 == REG) ! 1160: { ! 1161: int regno1 = REGNO (op1) + subreg_word1; ! 1162: ! 1163: if (GP_REG_P (regno1)) ! 1164: { ! 1165: switch (mode) ! 1166: { ! 1167: default: break; ! 1168: case SFmode: ret = "sw\t%1,%0"; break; ! 1169: case SImode: ret = "sw\t%1,%0"; break; ! 1170: case HImode: ret = "sh\t%1,%0"; break; ! 1171: case QImode: ret = "sb\t%1,%0"; break; ! 1172: } ! 1173: } ! 1174: ! 1175: else if (FP_REG_P (regno1) && (mode == SImode || mode == SFmode)) ! 1176: ret = "s.s\t%1,%0"; ! 1177: } ! 1178: ! 1179: else if (code1 == CONST_INT && INTVAL (op1) == 0) ! 1180: { ! 1181: switch (mode) ! 1182: { ! 1183: default: break; ! 1184: case SFmode: ret = "sw\t%z1,%0"; break; ! 1185: case SImode: ret = "sw\t%z1,%0"; break; ! 1186: case HImode: ret = "sh\t%z1,%0"; break; ! 1187: case QImode: ret = "sb\t%z1,%0"; break; ! 1188: } ! 1189: } ! 1190: ! 1191: else if (code1 == CONST_DOUBLE && CONST_DOUBLE_HIGH (op1) == 0 && CONST_DOUBLE_LOW (op1) == 0) ! 1192: { ! 1193: switch (mode) ! 1194: { ! 1195: default: break; ! 1196: case SFmode: ret = "sw\t%.,%0"; break; ! 1197: case SImode: ret = "sw\t%.,%0"; break; ! 1198: case HImode: ret = "sh\t%.,%0"; break; ! 1199: case QImode: ret = "sb\t%.,%0"; break; ! 1200: } ! 1201: } ! 1202: ! 1203: if (ret != (char *)0 && MEM_VOLATILE_P (op0)) ! 1204: { ! 1205: int i = strlen (ret); ! 1206: if (i > sizeof (volatile_buffer) - sizeof ("%{%}")) ! 1207: abort (); ! 1208: ! 1209: sprintf (volatile_buffer, "%%{%s%%}", ret); ! 1210: ret = volatile_buffer; ! 1211: } ! 1212: } ! 1213: ! 1214: if (ret == (char *)0) ! 1215: { ! 1216: abort_with_insn (insn, "Bad move"); ! 1217: return 0; ! 1218: } ! 1219: ! 1220: if (delay != DELAY_NONE) ! 1221: return mips_fill_delay_slot (ret, delay, operands, insn); ! 1222: ! 1223: return ret; ! 1224: } ! 1225: ! 1226: ! 1227: /* Return the appropriate instructions to move 2 words */ ! 1228: ! 1229: char * ! 1230: mips_move_2words (operands, insn) ! 1231: rtx operands[]; ! 1232: rtx insn; ! 1233: { ! 1234: char *ret = 0; ! 1235: rtx op0 = operands[0]; ! 1236: rtx op1 = operands[1]; ! 1237: enum rtx_code code0 = GET_CODE (operands[0]); ! 1238: enum rtx_code code1 = GET_CODE (operands[1]); ! 1239: int subreg_word0 = 0; ! 1240: int subreg_word1 = 0; ! 1241: enum delay_type delay = DELAY_NONE; ! 1242: ! 1243: while (code0 == SUBREG) ! 1244: { ! 1245: subreg_word0 += SUBREG_WORD (op0); ! 1246: op0 = SUBREG_REG (op0); ! 1247: code0 = GET_CODE (op0); ! 1248: } ! 1249: ! 1250: while (code1 == SUBREG) ! 1251: { ! 1252: subreg_word1 += SUBREG_WORD (op1); ! 1253: op1 = SUBREG_REG (op1); ! 1254: code1 = GET_CODE (op1); ! 1255: } ! 1256: ! 1257: if (code0 == REG) ! 1258: { ! 1259: int regno0 = REGNO (op0) + subreg_word0; ! 1260: ! 1261: if (code1 == REG) ! 1262: { ! 1263: int regno1 = REGNO (op1) + subreg_word1; ! 1264: ! 1265: /* Just in case, don't do anything for assigning a register ! 1266: to itself, unless we are filling a delay slot. */ ! 1267: if (regno0 == regno1 && set_nomacro == 0) ! 1268: ret = ""; ! 1269: ! 1270: else if (FP_REG_P (regno0)) ! 1271: { ! 1272: if (FP_REG_P (regno1)) ! 1273: ret = "mov.d\t%0,%1"; ! 1274: ! 1275: else ! 1276: { ! 1277: delay = DELAY_LOAD; ! 1278: ret = (TARGET_FLOAT64) ! 1279: ? "dmtc1\t%1,%0" ! 1280: : "mtc1\t%L1,%0\n\tmtc1\t%M1,%D0"; ! 1281: } ! 1282: } ! 1283: ! 1284: else if (FP_REG_P (regno1)) ! 1285: { ! 1286: delay = DELAY_LOAD; ! 1287: ret = (TARGET_FLOAT64) ! 1288: ? "dmfc1\t%0,%1" ! 1289: : "mfc1\t%L0,%1\n\tmfc1\t%M0,%D1"; ! 1290: } ! 1291: ! 1292: else if (MD_REG_P (regno0) && GP_REG_P (regno1)) ! 1293: { ! 1294: delay = DELAY_HILO; ! 1295: ret = "mthi\t%M1\n\tmtlo\t%L1"; ! 1296: } ! 1297: ! 1298: else if (GP_REG_P (regno0) && MD_REG_P (regno1)) ! 1299: { ! 1300: delay = DELAY_HILO; ! 1301: ret = "mfhi\t%M0\n\tmflo\t%L0"; ! 1302: } ! 1303: ! 1304: else if (regno0 != (regno1+1)) ! 1305: ret = "move\t%0,%1\n\tmove\t%D0,%D1"; ! 1306: ! 1307: else ! 1308: ret = "move\t%D0,%D1\n\tmove\t%0,%1"; ! 1309: } ! 1310: ! 1311: else if (code1 == CONST_DOUBLE) ! 1312: { ! 1313: if (CONST_DOUBLE_HIGH (op1) != 0 || CONST_DOUBLE_LOW (op1) != 0) ! 1314: { ! 1315: if (GET_MODE (op1) == DFmode) ! 1316: { ! 1317: delay = DELAY_LOAD; ! 1318: ret = "li.d\t%0,%1"; ! 1319: } ! 1320: ! 1321: else ! 1322: { ! 1323: operands[2] = GEN_INT (CONST_DOUBLE_LOW (op1)); ! 1324: operands[3] = GEN_INT (CONST_DOUBLE_HIGH (op1)); ! 1325: ret = "li\t%M0,%3\n\tli\t%L0,%2"; ! 1326: } ! 1327: } ! 1328: ! 1329: else ! 1330: { ! 1331: if (GP_REG_P (regno0)) ! 1332: ret = "move\t%0,%.\n\tmove\t%D0,%."; ! 1333: ! 1334: else if (FP_REG_P (regno0)) ! 1335: { ! 1336: delay = DELAY_LOAD; ! 1337: ret = (TARGET_FLOAT64) ! 1338: ? "dmtc1\t%.,%0" ! 1339: : "mtc1\t%.,%0\n\tmtc1\t%.,%D0"; ! 1340: } ! 1341: } ! 1342: } ! 1343: ! 1344: else if (code1 == CONST_INT && INTVAL (op1) == 0) ! 1345: { ! 1346: if (GP_REG_P (regno0)) ! 1347: ret = "move\t%0,%.\n\tmove\t%D0,%."; ! 1348: ! 1349: else if (FP_REG_P (regno0)) ! 1350: { ! 1351: delay = DELAY_LOAD; ! 1352: ret = (TARGET_FLOAT64) ! 1353: ? "dmtc1\t%.,%0" ! 1354: : "mtc1\t%.,%0\n\tmtc1\t%.,%D0"; ! 1355: } ! 1356: } ! 1357: ! 1358: else if (code1 == CONST_INT && GET_MODE (op0) == DImode && GP_REG_P (regno0)) ! 1359: { ! 1360: operands[2] = GEN_INT (INTVAL (operands[1]) >= 0 ? 0 : -1); ! 1361: ret = "li\t%M0,%2\n\tli\t%L0,%1"; ! 1362: } ! 1363: ! 1364: else if (code1 == MEM) ! 1365: { ! 1366: delay = DELAY_LOAD; ! 1367: ! 1368: if (TARGET_STATS) ! 1369: mips_count_memory_refs (op1, 2); ! 1370: ! 1371: if (FP_REG_P (regno0)) ! 1372: ret = "l.d\t%0,%1"; ! 1373: ! 1374: else if (offsettable_address_p (1, DFmode, XEXP (op1, 0))) ! 1375: { ! 1376: operands[2] = adj_offsettable_operand (op1, 4); ! 1377: if (reg_mentioned_p (op0, op1)) ! 1378: ret = "lw\t%D0,%2\n\tlw\t%0,%1"; ! 1379: else ! 1380: ret = "lw\t%0,%1\n\tlw\t%D0,%2"; ! 1381: } ! 1382: ! 1383: if (ret != (char *)0 && MEM_VOLATILE_P (op1)) ! 1384: { ! 1385: int i = strlen (ret); ! 1386: if (i > sizeof (volatile_buffer) - sizeof ("%{%}")) ! 1387: abort (); ! 1388: ! 1389: sprintf (volatile_buffer, "%%{%s%%}", ret); ! 1390: ret = volatile_buffer; ! 1391: } ! 1392: } ! 1393: } ! 1394: ! 1395: else if (code0 == MEM) ! 1396: { ! 1397: if (code1 == REG) ! 1398: { ! 1399: int regno1 = REGNO (op1) + subreg_word1; ! 1400: ! 1401: if (FP_REG_P (regno1)) ! 1402: ret = "s.d\t%1,%0"; ! 1403: ! 1404: else if (offsettable_address_p (1, DFmode, XEXP (op0, 0))) ! 1405: { ! 1406: operands[2] = adj_offsettable_operand (op0, 4); ! 1407: ret = "sw\t%1,%0\n\tsw\t%D1,%2"; ! 1408: } ! 1409: } ! 1410: ! 1411: else if (code1 == CONST_DOUBLE ! 1412: && CONST_DOUBLE_HIGH (op1) == 0 ! 1413: && CONST_DOUBLE_LOW (op1) == 0 ! 1414: && offsettable_address_p (1, DFmode, XEXP (op0, 0))) ! 1415: { ! 1416: if (TARGET_FLOAT64) ! 1417: ret = "sd\t%.,%0"; ! 1418: else ! 1419: { ! 1420: operands[2] = adj_offsettable_operand (op0, 4); ! 1421: ret = "sw\t%.,%0\n\tsw\t%.,%2"; ! 1422: } ! 1423: } ! 1424: ! 1425: if (TARGET_STATS) ! 1426: mips_count_memory_refs (op0, 2); ! 1427: ! 1428: if (ret != (char *)0 && MEM_VOLATILE_P (op0)) ! 1429: { ! 1430: int i = strlen (ret); ! 1431: if (i > sizeof (volatile_buffer) - sizeof ("%{%}")) ! 1432: abort (); ! 1433: ! 1434: sprintf (volatile_buffer, "%%{%s%%}", ret); ! 1435: ret = volatile_buffer; ! 1436: } ! 1437: } ! 1438: ! 1439: if (ret == (char *)0) ! 1440: { ! 1441: abort_with_insn (insn, "Bad move"); ! 1442: return 0; ! 1443: } ! 1444: ! 1445: if (delay != DELAY_NONE) ! 1446: return mips_fill_delay_slot (ret, delay, operands, insn); ! 1447: ! 1448: return ret; ! 1449: } ! 1450: ! 1451: ! 1452: /* Provide the costs of an addressing mode that contains ADDR. ! 1453: If ADDR is not a valid address, its cost is irrelevant. */ ! 1454: ! 1455: int ! 1456: mips_address_cost (addr) ! 1457: rtx addr; ! 1458: { ! 1459: switch (GET_CODE (addr)) ! 1460: { ! 1461: default: ! 1462: break; ! 1463: ! 1464: case LO_SUM: ! 1465: case HIGH: ! 1466: return 1; ! 1467: ! 1468: case LABEL_REF: ! 1469: return 2; ! 1470: ! 1471: case CONST: ! 1472: { ! 1473: rtx offset = const0_rtx; ! 1474: addr = eliminate_constant_term (addr, &offset); ! 1475: if (GET_CODE (addr) == LABEL_REF) ! 1476: return 2; ! 1477: ! 1478: if (GET_CODE (addr) != SYMBOL_REF) ! 1479: return 4; ! 1480: ! 1481: if (INTVAL (offset) < -32768 || INTVAL (offset) > 32767) ! 1482: return 2; ! 1483: } ! 1484: /* fall through */ ! 1485: ! 1486: case SYMBOL_REF: ! 1487: return SYMBOL_REF_FLAG (addr) ? 1 : 2; ! 1488: ! 1489: case PLUS: ! 1490: { ! 1491: register rtx plus0 = XEXP (addr, 0); ! 1492: register rtx plus1 = XEXP (addr, 1); ! 1493: ! 1494: if (GET_CODE (plus0) != REG && GET_CODE (plus1) == REG) ! 1495: { ! 1496: plus0 = XEXP (addr, 1); ! 1497: plus1 = XEXP (addr, 0); ! 1498: } ! 1499: ! 1500: if (GET_CODE (plus0) != REG) ! 1501: break; ! 1502: ! 1503: switch (GET_CODE (plus1)) ! 1504: { ! 1505: default: ! 1506: break; ! 1507: ! 1508: case CONST_INT: ! 1509: { ! 1510: int value = INTVAL (plus1); ! 1511: return (value < -32768 || value > 32767) ? 2 : 1; ! 1512: } ! 1513: ! 1514: case CONST: ! 1515: case SYMBOL_REF: ! 1516: case LABEL_REF: ! 1517: case HIGH: ! 1518: case LO_SUM: ! 1519: return mips_address_cost (plus1) + 1; ! 1520: } ! 1521: } ! 1522: } ! 1523: ! 1524: return 4; ! 1525: } ! 1526: ! 1527: ! 1528: /* Make normal rtx_code into something we can index from an array */ ! 1529: ! 1530: static enum internal_test ! 1531: map_test_to_internal_test (test_code) ! 1532: enum rtx_code test_code; ! 1533: { ! 1534: enum internal_test test = ITEST_MAX; ! 1535: ! 1536: switch (test_code) ! 1537: { ! 1538: default: break; ! 1539: case EQ: test = ITEST_EQ; break; ! 1540: case NE: test = ITEST_NE; break; ! 1541: case GT: test = ITEST_GT; break; ! 1542: case GE: test = ITEST_GE; break; ! 1543: case LT: test = ITEST_LT; break; ! 1544: case LE: test = ITEST_LE; break; ! 1545: case GTU: test = ITEST_GTU; break; ! 1546: case GEU: test = ITEST_GEU; break; ! 1547: case LTU: test = ITEST_LTU; break; ! 1548: case LEU: test = ITEST_LEU; break; ! 1549: } ! 1550: ! 1551: return test; ! 1552: } ! 1553: ! 1554: ! 1555: /* Generate the code to compare two integer values. The return value is: ! 1556: (reg:SI xx) The pseudo register the comparison is in ! 1557: (rtx)0 No register, generate a simple branch. */ ! 1558: ! 1559: rtx ! 1560: gen_int_relational (test_code, result, cmp0, cmp1, p_invert) ! 1561: enum rtx_code test_code; /* relational test (EQ, etc) */ ! 1562: rtx result; /* result to store comp. or 0 if branch */ ! 1563: rtx cmp0; /* first operand to compare */ ! 1564: rtx cmp1; /* second operand to compare */ ! 1565: int *p_invert; /* NULL or ptr to hold whether branch needs */ ! 1566: /* to reverse its test */ ! 1567: { ! 1568: struct cmp_info { ! 1569: enum rtx_code test_code; /* code to use in instruction (LT vs. LTU) */ ! 1570: int const_low; /* low bound of constant we can accept */ ! 1571: int const_high; /* high bound of constant we can accept */ ! 1572: int const_add; /* constant to add (convert LE -> LT) */ ! 1573: int reverse_regs; /* reverse registers in test */ ! 1574: int invert_const; /* != 0 if invert value if cmp1 is constant */ ! 1575: int invert_reg; /* != 0 if invert value if cmp1 is register */ ! 1576: int unsignedp; /* != 0 for unsigned comparisons. */ ! 1577: }; ! 1578: ! 1579: static struct cmp_info info[ (int)ITEST_MAX ] = { ! 1580: ! 1581: { XOR, 0, 65535, 0, 0, 0, 0, 0 }, /* EQ */ ! 1582: { XOR, 0, 65535, 0, 0, 1, 1, 0 }, /* NE */ ! 1583: { LT, -32769, 32766, 1, 1, 1, 0, 0 }, /* GT */ ! 1584: { LT, -32768, 32767, 0, 0, 1, 1, 0 }, /* GE */ ! 1585: { LT, -32768, 32767, 0, 0, 0, 0, 0 }, /* LT */ ! 1586: { LT, -32769, 32766, 1, 1, 0, 1, 0 }, /* LE */ ! 1587: { LTU, -32769, 32766, 1, 1, 1, 0, 1 }, /* GTU */ ! 1588: { LTU, -32768, 32767, 0, 0, 1, 1, 1 }, /* GEU */ ! 1589: { LTU, -32768, 32767, 0, 0, 0, 0, 1 }, /* LTU */ ! 1590: { LTU, -32769, 32766, 1, 1, 0, 1, 1 }, /* LEU */ ! 1591: }; ! 1592: ! 1593: enum internal_test test; ! 1594: struct cmp_info *p_info; ! 1595: int branch_p; ! 1596: int eqne_p; ! 1597: int invert; ! 1598: rtx reg; ! 1599: rtx reg2; ! 1600: ! 1601: test = map_test_to_internal_test (test_code); ! 1602: if (test == ITEST_MAX) ! 1603: abort (); ! 1604: ! 1605: p_info = &info[ (int)test ]; ! 1606: eqne_p = (p_info->test_code == XOR); ! 1607: ! 1608: /* Eliminate simple branches */ ! 1609: branch_p = (result == (rtx)0); ! 1610: if (branch_p) ! 1611: { ! 1612: if (GET_CODE (cmp0) == REG || GET_CODE (cmp0) == SUBREG) ! 1613: { ! 1614: /* Comparisons against zero are simple branches */ ! 1615: if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) == 0) ! 1616: return (rtx)0; ! 1617: ! 1618: /* Test for beq/bne. */ ! 1619: if (eqne_p) ! 1620: return (rtx)0; ! 1621: } ! 1622: ! 1623: /* allocate a pseudo to calculate the value in. */ ! 1624: result = gen_reg_rtx (SImode); ! 1625: } ! 1626: ! 1627: /* Make sure we can handle any constants given to us. */ ! 1628: if (GET_CODE (cmp0) == CONST_INT) ! 1629: cmp0 = force_reg (SImode, cmp0); ! 1630: ! 1631: if (GET_CODE (cmp1) == CONST_INT) ! 1632: { ! 1633: HOST_WIDE_INT value = INTVAL (cmp1); ! 1634: if (value < p_info->const_low || value > p_info->const_high) ! 1635: cmp1 = force_reg (SImode, cmp1); ! 1636: } ! 1637: ! 1638: /* See if we need to invert the result. */ ! 1639: invert = (GET_CODE (cmp1) == CONST_INT) ! 1640: ? p_info->invert_const ! 1641: : p_info->invert_reg; ! 1642: ! 1643: if (p_invert != (int *)0) ! 1644: { ! 1645: *p_invert = invert; ! 1646: invert = FALSE; ! 1647: } ! 1648: ! 1649: /* Comparison to constants, may involve adding 1 to change a LT into LE. ! 1650: Comparison between two registers, may involve switching operands. */ ! 1651: if (GET_CODE (cmp1) == CONST_INT) ! 1652: { ! 1653: if (p_info->const_add != 0) ! 1654: { ! 1655: HOST_WIDE_INT new = INTVAL (cmp1) + p_info->const_add; ! 1656: /* If modification of cmp1 caused overflow, ! 1657: we would get the wrong answer if we follow the usual path; ! 1658: thus, x > 0xffffffffu would turn into x > 0u. */ ! 1659: if ((p_info->unsignedp ! 1660: ? (unsigned HOST_WIDE_INT) new > INTVAL (cmp1) ! 1661: : new > INTVAL (cmp1)) ! 1662: != (p_info->const_add > 0)) ! 1663: /* 1 is the right value in the LE and LEU case. ! 1664: In the GT and GTU case, *p_invert is already set, ! 1665: so this is effectively 0. */ ! 1666: return force_reg (SImode, const1_rtx); ! 1667: else ! 1668: cmp1 = GEN_INT (new); ! 1669: } ! 1670: } ! 1671: else if (p_info->reverse_regs) ! 1672: { ! 1673: rtx temp = cmp0; ! 1674: cmp0 = cmp1; ! 1675: cmp1 = temp; ! 1676: } ! 1677: ! 1678: if (test == ITEST_NE && GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) == 0) ! 1679: reg = cmp0; ! 1680: else ! 1681: { ! 1682: reg = (invert || eqne_p) ? gen_reg_rtx (SImode) : result; ! 1683: emit_move_insn (reg, gen_rtx (p_info->test_code, SImode, cmp0, cmp1)); ! 1684: } ! 1685: ! 1686: if (test == ITEST_NE) ! 1687: { ! 1688: emit_move_insn (result, gen_rtx (GTU, SImode, reg, const0_rtx)); ! 1689: invert = FALSE; ! 1690: } ! 1691: ! 1692: else if (test == ITEST_EQ) ! 1693: { ! 1694: reg2 = (invert) ? gen_reg_rtx (SImode) : result; ! 1695: emit_move_insn (reg2, gen_rtx (LTU, SImode, reg, const1_rtx)); ! 1696: reg = reg2; ! 1697: } ! 1698: ! 1699: if (invert) ! 1700: emit_move_insn (result, gen_rtx (XOR, SImode, reg, const1_rtx)); ! 1701: ! 1702: return result; ! 1703: } ! 1704: ! 1705: ! 1706: /* Emit the common code for doing conditional branches. ! 1707: operand[0] is the label to jump to. ! 1708: The comparison operands are saved away by cmp{si,sf,df}. */ ! 1709: ! 1710: void ! 1711: gen_conditional_branch (operands, test_code) ! 1712: rtx operands[]; ! 1713: enum rtx_code test_code; ! 1714: { ! 1715: static enum machine_mode mode_map[(int)CMP_MAX][(int)ITEST_MAX] = { ! 1716: { /* CMP_SI */ ! 1717: SImode, /* eq */ ! 1718: SImode, /* ne */ ! 1719: SImode, /* gt */ ! 1720: SImode, /* ge */ ! 1721: SImode, /* lt */ ! 1722: SImode, /* le */ ! 1723: SImode, /* gtu */ ! 1724: SImode, /* geu */ ! 1725: SImode, /* ltu */ ! 1726: SImode, /* leu */ ! 1727: }, ! 1728: { /* CMP_SF */ ! 1729: CC_FPmode, /* eq */ ! 1730: CC_REV_FPmode, /* ne */ ! 1731: CC_FPmode, /* gt */ ! 1732: CC_FPmode, /* ge */ ! 1733: CC_FPmode, /* lt */ ! 1734: CC_FPmode, /* le */ ! 1735: VOIDmode, /* gtu */ ! 1736: VOIDmode, /* geu */ ! 1737: VOIDmode, /* ltu */ ! 1738: VOIDmode, /* leu */ ! 1739: }, ! 1740: { /* CMP_DF */ ! 1741: CC_FPmode, /* eq */ ! 1742: CC_REV_FPmode, /* ne */ ! 1743: CC_FPmode, /* gt */ ! 1744: CC_FPmode, /* ge */ ! 1745: CC_FPmode, /* lt */ ! 1746: CC_FPmode, /* le */ ! 1747: VOIDmode, /* gtu */ ! 1748: VOIDmode, /* geu */ ! 1749: VOIDmode, /* ltu */ ! 1750: VOIDmode, /* leu */ ! 1751: }, ! 1752: }; ! 1753: ! 1754: enum machine_mode mode; ! 1755: enum cmp_type type = branch_type; ! 1756: rtx cmp0 = branch_cmp[0]; ! 1757: rtx cmp1 = branch_cmp[1]; ! 1758: rtx label1 = gen_rtx (LABEL_REF, VOIDmode, operands[0]); ! 1759: rtx label2 = pc_rtx; ! 1760: rtx reg = (rtx)0; ! 1761: int invert = 0; ! 1762: enum internal_test test = map_test_to_internal_test (test_code); ! 1763: ! 1764: if (test == ITEST_MAX) ! 1765: { ! 1766: mode = SImode; ! 1767: goto fail; ! 1768: } ! 1769: ! 1770: /* Get the machine mode to use (CCmode, CC_EQmode, CC_FPmode, or CC_REV_FPmode). */ ! 1771: mode = mode_map[(int)type][(int)test]; ! 1772: if (mode == VOIDmode) ! 1773: goto fail; ! 1774: ! 1775: switch (branch_type) ! 1776: { ! 1777: default: ! 1778: goto fail; ! 1779: ! 1780: case CMP_SI: ! 1781: reg = gen_int_relational (test_code, (rtx)0, cmp0, cmp1, &invert); ! 1782: if (reg != (rtx)0) ! 1783: { ! 1784: cmp0 = reg; ! 1785: cmp1 = const0_rtx; ! 1786: test_code = NE; ! 1787: } ! 1788: ! 1789: /* Make sure not non-zero constant if ==/!= */ ! 1790: else if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) != 0) ! 1791: cmp1 = force_reg (SImode, cmp1); ! 1792: ! 1793: break; ! 1794: ! 1795: case CMP_DF: ! 1796: case CMP_SF: ! 1797: { ! 1798: rtx reg = gen_rtx (REG, mode, FPSW_REGNUM); ! 1799: emit_insn (gen_rtx (SET, VOIDmode, reg, gen_rtx (test_code, mode, cmp0, cmp1))); ! 1800: cmp0 = reg; ! 1801: cmp1 = const0_rtx; ! 1802: test_code = NE; ! 1803: } ! 1804: break; ! 1805: } ! 1806: ! 1807: /* Generate the jump */ ! 1808: if (invert) ! 1809: { ! 1810: label2 = label1; ! 1811: label1 = pc_rtx; ! 1812: } ! 1813: ! 1814: emit_jump_insn (gen_rtx (SET, VOIDmode, ! 1815: pc_rtx, ! 1816: gen_rtx (IF_THEN_ELSE, VOIDmode, ! 1817: gen_rtx (test_code, mode, cmp0, cmp1), ! 1818: label1, ! 1819: label2))); ! 1820: ! 1821: return; ! 1822: ! 1823: fail: ! 1824: abort_with_insn (gen_rtx (test_code, mode, cmp0, cmp1), "bad test"); ! 1825: } ! 1826: ! 1827: ! 1828: #define UNITS_PER_SHORT (SHORT_TYPE_SIZE / BITS_PER_UNIT) ! 1829: ! 1830: /* Internal code to generate the load and store of one word/short/byte. ! 1831: The load is emitted directly, and the store insn is returned. */ ! 1832: ! 1833: #if 0 ! 1834: static rtx ! 1835: block_move_load_store (dest_reg, src_reg, p_bytes, p_offset, align, orig_src) ! 1836: rtx src_reg; /* register holding source memory address */ ! 1837: rtx dest_reg; /* register holding dest. memory address */ ! 1838: int *p_bytes; /* pointer to # bytes remaining */ ! 1839: int *p_offset; /* pointer to current offset */ ! 1840: int align; /* alignment */ ! 1841: rtx orig_src; /* original source for making a reg note */ ! 1842: { ! 1843: int bytes; /* # bytes remaining */ ! 1844: int offset; /* offset to use */ ! 1845: int size; /* size in bytes of load/store */ ! 1846: enum machine_mode mode; /* mode to use for load/store */ ! 1847: rtx reg; /* temporary register */ ! 1848: rtx src_addr; /* source address */ ! 1849: rtx dest_addr; /* destination address */ ! 1850: rtx insn; /* insn of the load */ ! 1851: rtx orig_src_addr; /* original source address */ ! 1852: rtx (*load_func)(); /* function to generate load insn */ ! 1853: rtx (*store_func)(); /* function to generate destination insn */ ! 1854: ! 1855: bytes = *p_bytes; ! 1856: if (bytes <= 0 || align <= 0) ! 1857: abort (); ! 1858: ! 1859: if (bytes >= UNITS_PER_WORD && align >= UNITS_PER_WORD) ! 1860: { ! 1861: mode = SImode; ! 1862: size = UNITS_PER_WORD; ! 1863: load_func = gen_movsi; ! 1864: store_func = gen_movsi; ! 1865: } ! 1866: ! 1867: #if 0 ! 1868: /* Don't generate unaligned moves here, rather defer those to the ! 1869: general movestrsi_internal pattern. */ ! 1870: else if (bytes >= UNITS_PER_WORD) ! 1871: { ! 1872: mode = SImode; ! 1873: size = UNITS_PER_WORD; ! 1874: load_func = gen_movsi_ulw; ! 1875: store_func = gen_movsi_usw; ! 1876: } ! 1877: #endif ! 1878: ! 1879: else if (bytes >= UNITS_PER_SHORT && align >= UNITS_PER_SHORT) ! 1880: { ! 1881: mode = HImode; ! 1882: size = UNITS_PER_SHORT; ! 1883: load_func = gen_movhi; ! 1884: store_func = gen_movhi; ! 1885: } ! 1886: ! 1887: else ! 1888: { ! 1889: mode = QImode; ! 1890: size = 1; ! 1891: load_func = gen_movqi; ! 1892: store_func = gen_movqi; ! 1893: } ! 1894: ! 1895: offset = *p_offset; ! 1896: *p_offset = offset + size; ! 1897: *p_bytes = bytes - size; ! 1898: ! 1899: if (offset == 0) ! 1900: { ! 1901: src_addr = src_reg; ! 1902: dest_addr = dest_reg; ! 1903: } ! 1904: else ! 1905: { ! 1906: src_addr = gen_rtx (PLUS, Pmode, src_reg, GEN_INT (offset)); ! 1907: dest_addr = gen_rtx (PLUS, Pmode, dest_reg, GEN_INT (offset)); ! 1908: } ! 1909: ! 1910: reg = gen_reg_rtx (mode); ! 1911: insn = emit_insn ((*load_func) (reg, gen_rtx (MEM, mode, src_addr))); ! 1912: orig_src_addr = XEXP (orig_src, 0); ! 1913: if (CONSTANT_P (orig_src_addr)) ! 1914: REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_EQUIV, ! 1915: plus_constant (orig_src_addr, offset), ! 1916: REG_NOTES (insn)); ! 1917: ! 1918: return (*store_func) (gen_rtx (MEM, mode, dest_addr), reg); ! 1919: } ! 1920: #endif ! 1921: ! 1922: ! 1923: /* Write a series of loads/stores to move some bytes. Generate load/stores as follows: ! 1924: ! 1925: load 1 ! 1926: load 2 ! 1927: load 3 ! 1928: store 1 ! 1929: load 4 ! 1930: store 2 ! 1931: load 5 ! 1932: store 3 ! 1933: ... ! 1934: ! 1935: This way, no NOP's are needed, except at the end, and only ! 1936: two temp registers are needed. Two delay slots are used ! 1937: in deference to the R4000. */ ! 1938: ! 1939: #if 0 ! 1940: static void ! 1941: block_move_sequence (dest_reg, src_reg, bytes, align, orig_src) ! 1942: rtx dest_reg; /* register holding destination address */ ! 1943: rtx src_reg; /* register holding source address */ ! 1944: int bytes; /* # bytes to move */ ! 1945: int align; /* max alignment to assume */ ! 1946: rtx orig_src; /* original source for making a reg note */ ! 1947: { ! 1948: int offset = 0; ! 1949: rtx prev2_store = (rtx)0; ! 1950: rtx prev_store = (rtx)0; ! 1951: rtx cur_store = (rtx)0; ! 1952: ! 1953: while (bytes > 0) ! 1954: { ! 1955: /* Is there a store to do? */ ! 1956: if (prev2_store) ! 1957: emit_insn (prev2_store); ! 1958: ! 1959: prev2_store = prev_store; ! 1960: prev_store = cur_store; ! 1961: cur_store = block_move_load_store (dest_reg, src_reg, ! 1962: &bytes, &offset, ! 1963: align, orig_src); ! 1964: } ! 1965: ! 1966: /* Finish up last three stores. */ ! 1967: if (prev2_store) ! 1968: emit_insn (prev2_store); ! 1969: ! 1970: if (prev_store) ! 1971: emit_insn (prev_store); ! 1972: ! 1973: if (cur_store) ! 1974: emit_insn (cur_store); ! 1975: } ! 1976: #endif ! 1977: ! 1978: ! 1979: /* Write a loop to move a constant number of bytes. Generate load/stores as follows: ! 1980: ! 1981: do { ! 1982: temp1 = src[0]; ! 1983: temp2 = src[1]; ! 1984: ... ! 1985: temp<last> = src[MAX_MOVE_REGS-1]; ! 1986: dest[0] = temp1; ! 1987: dest[1] = temp2; ! 1988: ... ! 1989: dest[MAX_MOVE_REGS-1] = temp<last>; ! 1990: src += MAX_MOVE_REGS; ! 1991: dest += MAX_MOVE_REGS; ! 1992: } while (src != final); ! 1993: ! 1994: This way, no NOP's are needed, and only MAX_MOVE_REGS+3 temp ! 1995: registers are needed. ! 1996: ! 1997: Aligned moves move MAX_MOVE_REGS*4 bytes every (2*MAX_MOVE_REGS)+3 ! 1998: cycles, unaligned moves move MAX_MOVE_REGS*4 bytes every ! 1999: (4*MAX_MOVE_REGS)+3 cycles, assuming no cache misses. */ ! 2000: ! 2001: #define MAX_MOVE_REGS 4 ! 2002: #define MAX_MOVE_BYTES (MAX_MOVE_REGS * UNITS_PER_WORD) ! 2003: ! 2004: static void ! 2005: block_move_loop (dest_reg, src_reg, bytes, align, orig_src) ! 2006: rtx dest_reg; /* register holding destination address */ ! 2007: rtx src_reg; /* register holding source address */ ! 2008: int bytes; /* # bytes to move */ ! 2009: int align; /* alignment */ ! 2010: rtx orig_src; /* original source for making a reg note */ ! 2011: { ! 2012: rtx dest_mem = gen_rtx (MEM, BLKmode, dest_reg); ! 2013: rtx src_mem = gen_rtx (MEM, BLKmode, src_reg); ! 2014: rtx align_rtx = GEN_INT (align); ! 2015: rtx label; ! 2016: rtx final_src; ! 2017: rtx bytes_rtx; ! 2018: int leftover; ! 2019: ! 2020: if (bytes < 2*MAX_MOVE_BYTES) ! 2021: abort (); ! 2022: ! 2023: leftover = bytes % MAX_MOVE_BYTES; ! 2024: bytes -= leftover; ! 2025: ! 2026: label = gen_label_rtx (); ! 2027: final_src = gen_reg_rtx (Pmode); ! 2028: bytes_rtx = GEN_INT (bytes); ! 2029: ! 2030: if (bytes > 0x7fff) ! 2031: { ! 2032: emit_insn (gen_movsi (final_src, bytes_rtx)); ! 2033: emit_insn (gen_addsi3 (final_src, final_src, src_reg)); ! 2034: } ! 2035: else ! 2036: emit_insn (gen_addsi3 (final_src, src_reg, bytes_rtx)); ! 2037: ! 2038: emit_label (label); ! 2039: ! 2040: bytes_rtx = GEN_INT (MAX_MOVE_BYTES); ! 2041: emit_insn (gen_movstrsi_internal (dest_mem, src_mem, bytes_rtx, align_rtx)); ! 2042: emit_insn (gen_addsi3 (src_reg, src_reg, bytes_rtx)); ! 2043: emit_insn (gen_addsi3 (dest_reg, dest_reg, bytes_rtx)); ! 2044: emit_insn (gen_cmpsi (src_reg, final_src)); ! 2045: emit_jump_insn (gen_bne (label)); ! 2046: ! 2047: if (leftover) ! 2048: emit_insn (gen_movstrsi_internal (dest_mem, src_mem, ! 2049: GEN_INT (leftover), ! 2050: align_rtx)); ! 2051: } ! 2052: ! 2053: ! 2054: /* Use a library function to move some bytes. */ ! 2055: ! 2056: static void ! 2057: block_move_call (dest_reg, src_reg, bytes_rtx) ! 2058: rtx dest_reg; ! 2059: rtx src_reg; ! 2060: rtx bytes_rtx; ! 2061: { ! 2062: #ifdef TARGET_MEM_FUNCTIONS ! 2063: emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "memcpy"), 0, ! 2064: VOIDmode, 3, ! 2065: dest_reg, Pmode, ! 2066: src_reg, Pmode, ! 2067: bytes_rtx, SImode); ! 2068: #else ! 2069: emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "bcopy"), 0, ! 2070: VOIDmode, 3, ! 2071: src_reg, Pmode, ! 2072: dest_reg, Pmode, ! 2073: bytes_rtx, SImode); ! 2074: #endif ! 2075: } ! 2076: ! 2077: ! 2078: /* Expand string/block move operations. ! 2079: ! 2080: operands[0] is the pointer to the destination. ! 2081: operands[1] is the pointer to the source. ! 2082: operands[2] is the number of bytes to move. ! 2083: operands[3] is the alignment. */ ! 2084: ! 2085: void ! 2086: expand_block_move (operands) ! 2087: rtx operands[]; ! 2088: { ! 2089: rtx bytes_rtx = operands[2]; ! 2090: rtx align_rtx = operands[3]; ! 2091: int constp = (GET_CODE (bytes_rtx) == CONST_INT); ! 2092: int bytes = (constp ? INTVAL (bytes_rtx) : 0); ! 2093: int align = INTVAL (align_rtx); ! 2094: rtx orig_src = operands[1]; ! 2095: rtx src_reg; ! 2096: rtx dest_reg; ! 2097: ! 2098: if (constp && bytes <= 0) ! 2099: return; ! 2100: ! 2101: if (align > UNITS_PER_WORD) ! 2102: align = UNITS_PER_WORD; ! 2103: ! 2104: /* Move the address into scratch registers. */ ! 2105: dest_reg = copy_addr_to_reg (XEXP (operands[0], 0)); ! 2106: src_reg = copy_addr_to_reg (XEXP (orig_src, 0)); ! 2107: ! 2108: if (TARGET_MEMCPY) ! 2109: block_move_call (dest_reg, src_reg, bytes_rtx); ! 2110: ! 2111: #if 0 ! 2112: else if (constp && bytes <= 3*align) ! 2113: block_move_sequence (dest_reg, src_reg, bytes, align, orig_src); ! 2114: #endif ! 2115: ! 2116: else if (constp && bytes <= 2*MAX_MOVE_BYTES) ! 2117: emit_insn (gen_movstrsi_internal (gen_rtx (MEM, BLKmode, dest_reg), ! 2118: gen_rtx (MEM, BLKmode, src_reg), ! 2119: bytes_rtx, align_rtx)); ! 2120: ! 2121: else if (constp && align >= UNITS_PER_WORD && optimize) ! 2122: block_move_loop (dest_reg, src_reg, bytes, align, orig_src); ! 2123: ! 2124: else if (constp && optimize) ! 2125: { ! 2126: /* If the alignment is not word aligned, generate a test at ! 2127: runtime, to see whether things wound up aligned, and we ! 2128: can use the faster lw/sw instead ulw/usw. */ ! 2129: ! 2130: rtx temp = gen_reg_rtx (Pmode); ! 2131: rtx aligned_label = gen_label_rtx (); ! 2132: rtx join_label = gen_label_rtx (); ! 2133: int leftover = bytes % MAX_MOVE_BYTES; ! 2134: ! 2135: bytes -= leftover; ! 2136: ! 2137: emit_insn (gen_iorsi3 (temp, src_reg, dest_reg)); ! 2138: emit_insn (gen_andsi3 (temp, temp, GEN_INT (UNITS_PER_WORD-1))); ! 2139: emit_insn (gen_cmpsi (temp, const0_rtx)); ! 2140: emit_jump_insn (gen_beq (aligned_label)); ! 2141: ! 2142: /* Unaligned loop. */ ! 2143: block_move_loop (dest_reg, src_reg, bytes, 1, orig_src); ! 2144: emit_jump_insn (gen_jump (join_label)); ! 2145: emit_barrier (); ! 2146: ! 2147: /* Aligned loop. */ ! 2148: emit_label (aligned_label); ! 2149: block_move_loop (dest_reg, src_reg, bytes, UNITS_PER_WORD, orig_src); ! 2150: emit_label (join_label); ! 2151: ! 2152: /* Bytes at the end of the loop. */ ! 2153: if (leftover) ! 2154: { ! 2155: #if 0 ! 2156: if (leftover <= 3*align) ! 2157: block_move_sequence (dest_reg, src_reg, leftover, align, orig_src); ! 2158: ! 2159: else ! 2160: #endif ! 2161: emit_insn (gen_movstrsi_internal (gen_rtx (MEM, BLKmode, dest_reg), ! 2162: gen_rtx (MEM, BLKmode, src_reg), ! 2163: GEN_INT (leftover), ! 2164: GEN_INT (align))); ! 2165: } ! 2166: } ! 2167: ! 2168: else ! 2169: block_move_call (dest_reg, src_reg, bytes_rtx); ! 2170: } ! 2171: ! 2172: ! 2173: /* Emit load/stores for a small constant block_move. ! 2174: ! 2175: operands[0] is the memory address of the destination. ! 2176: operands[1] is the memory address of the source. ! 2177: operands[2] is the number of bytes to move. ! 2178: operands[3] is the alignment. ! 2179: operands[4] is a temp register. ! 2180: operands[5] is a temp register. ! 2181: ... ! 2182: operands[3+num_regs] is the last temp register. ! 2183: ! 2184: The block move type can be one of the following: ! 2185: BLOCK_MOVE_NORMAL Do all of the block move. ! 2186: BLOCK_MOVE_NOT_LAST Do all but the last store. ! 2187: BLOCK_MOVE_LAST Do just the last store. */ ! 2188: ! 2189: char * ! 2190: output_block_move (insn, operands, num_regs, move_type) ! 2191: rtx insn; ! 2192: rtx operands[]; ! 2193: int num_regs; ! 2194: enum block_move_type move_type; ! 2195: { ! 2196: rtx dest_reg = XEXP (operands[0], 0); ! 2197: rtx src_reg = XEXP (operands[1], 0); ! 2198: int bytes = INTVAL (operands[2]); ! 2199: int align = INTVAL (operands[3]); ! 2200: int num = 0; ! 2201: int offset = 0; ! 2202: int use_lwl_lwr = FALSE; ! 2203: int last_operand = num_regs+4; ! 2204: int safe_regs = 4; ! 2205: int i; ! 2206: rtx xoperands[10]; ! 2207: ! 2208: struct { ! 2209: char *load; /* load insn without nop */ ! 2210: char *load_nop; /* load insn with trailing nop */ ! 2211: char *store; /* store insn */ ! 2212: char *final; /* if last_store used: NULL or swr */ ! 2213: char *last_store; /* last store instruction */ ! 2214: int offset; /* current offset */ ! 2215: enum machine_mode mode; /* mode to use on (MEM) */ ! 2216: } load_store[4]; ! 2217: ! 2218: /* Detect a bug in GCC, where it can give us a register ! 2219: the same as one of the addressing registers and reduce ! 2220: the number of registers available. */ ! 2221: for (i = 4; ! 2222: i < last_operand && safe_regs < (sizeof(xoperands) / sizeof(xoperands[0])); ! 2223: i++) ! 2224: { ! 2225: if (!reg_mentioned_p (operands[i], operands[0]) ! 2226: && !reg_mentioned_p (operands[i], operands[1])) ! 2227: ! 2228: xoperands[safe_regs++] = operands[i]; ! 2229: } ! 2230: ! 2231: if (safe_regs < last_operand) ! 2232: { ! 2233: xoperands[0] = operands[0]; ! 2234: xoperands[1] = operands[1]; ! 2235: xoperands[2] = operands[2]; ! 2236: xoperands[3] = operands[3]; ! 2237: return output_block_move (insn, xoperands, safe_regs-4, move_type); ! 2238: } ! 2239: ! 2240: /* If we are given global or static addresses, and we would be ! 2241: emitting a few instructions, try to save time by using a ! 2242: temporary register for the pointer. */ ! 2243: if (num_regs > 2 && (bytes > 2*align || move_type != BLOCK_MOVE_NORMAL)) ! 2244: { ! 2245: if (CONSTANT_P (src_reg)) ! 2246: { ! 2247: if (TARGET_STATS) ! 2248: mips_count_memory_refs (operands[1], 1); ! 2249: ! 2250: src_reg = operands[ 3 + num_regs-- ]; ! 2251: if (move_type != BLOCK_MOVE_LAST) ! 2252: { ! 2253: xoperands[1] = operands[1]; ! 2254: xoperands[0] = src_reg; ! 2255: output_asm_insn ("la\t%0,%1", xoperands); ! 2256: } ! 2257: } ! 2258: ! 2259: if (CONSTANT_P (dest_reg)) ! 2260: { ! 2261: if (TARGET_STATS) ! 2262: mips_count_memory_refs (operands[0], 1); ! 2263: ! 2264: dest_reg = operands[ 3 + num_regs-- ]; ! 2265: if (move_type != BLOCK_MOVE_LAST) ! 2266: { ! 2267: xoperands[1] = operands[0]; ! 2268: xoperands[0] = dest_reg; ! 2269: output_asm_insn ("la\t%0,%1", xoperands); ! 2270: } ! 2271: } ! 2272: } ! 2273: ! 2274: if (num_regs > (sizeof (load_store) / sizeof (load_store[0]))) ! 2275: num_regs = (sizeof (load_store) / sizeof (load_store[0])); ! 2276: ! 2277: else if (num_regs < 1) ! 2278: abort_with_insn (insn, "Cannot do block move, not enough scratch registers"); ! 2279: ! 2280: if (TARGET_GAS && move_type != BLOCK_MOVE_LAST && set_noreorder++ == 0) ! 2281: output_asm_insn (".set\tnoreorder", operands); ! 2282: ! 2283: while (bytes > 0) ! 2284: { ! 2285: load_store[num].offset = offset; ! 2286: ! 2287: if (bytes >= UNITS_PER_WORD && align >= UNITS_PER_WORD) ! 2288: { ! 2289: load_store[num].load = "lw\t%0,%1"; ! 2290: load_store[num].load_nop = "lw\t%0,%1%#"; ! 2291: load_store[num].store = "sw\t%0,%1"; ! 2292: load_store[num].last_store = "sw\t%0,%1"; ! 2293: load_store[num].final = (char *)0; ! 2294: load_store[num].mode = SImode; ! 2295: offset += UNITS_PER_WORD; ! 2296: bytes -= UNITS_PER_WORD; ! 2297: } ! 2298: ! 2299: else if (bytes >= UNITS_PER_WORD) ! 2300: { ! 2301: #if BYTES_BIG_ENDIAN ! 2302: load_store[num].load = "lwl\t%0,%1\n\tlwr\t%0,%2"; ! 2303: load_store[num].load_nop = "lwl\t%0,%1\n\tlwr\t%0,%2%#"; ! 2304: load_store[num].store = "swl\t%0,%1\n\tswr\t%0,%2"; ! 2305: load_store[num].last_store = "swr\t%0,%2"; ! 2306: load_store[num].final = "swl\t%0,%1"; ! 2307: #else ! 2308: load_store[num].load = "lwl\t%0,%2\n\tlwr\t%0,%1"; ! 2309: load_store[num].load_nop = "lwl\t%0,%2\n\tlwr\t%0,%1%#"; ! 2310: load_store[num].store = "swl\t%0,%2\n\tswr\t%0,%1"; ! 2311: load_store[num].last_store = "swr\t%0,%1"; ! 2312: load_store[num].final = "swl\t%0,%2"; ! 2313: #endif ! 2314: load_store[num].mode = SImode; ! 2315: offset += UNITS_PER_WORD; ! 2316: bytes -= UNITS_PER_WORD; ! 2317: use_lwl_lwr = TRUE; ! 2318: } ! 2319: ! 2320: else if (bytes >= UNITS_PER_SHORT && align >= UNITS_PER_SHORT) ! 2321: { ! 2322: load_store[num].load = "lh\t%0,%1"; ! 2323: load_store[num].load_nop = "lh\t%0,%1%#"; ! 2324: load_store[num].store = "sh\t%0,%1"; ! 2325: load_store[num].last_store = "sh\t%0,%1"; ! 2326: load_store[num].final = (char *)0; ! 2327: load_store[num].offset = offset; ! 2328: load_store[num].mode = HImode; ! 2329: offset += UNITS_PER_SHORT; ! 2330: bytes -= UNITS_PER_SHORT; ! 2331: } ! 2332: ! 2333: else ! 2334: { ! 2335: load_store[num].load = "lb\t%0,%1"; ! 2336: load_store[num].load_nop = "lb\t%0,%1%#"; ! 2337: load_store[num].store = "sb\t%0,%1"; ! 2338: load_store[num].last_store = "sb\t%0,%1"; ! 2339: load_store[num].final = (char *)0; ! 2340: load_store[num].mode = QImode; ! 2341: offset++; ! 2342: bytes--; ! 2343: } ! 2344: ! 2345: if (TARGET_STATS && move_type != BLOCK_MOVE_LAST) ! 2346: { ! 2347: dslots_load_total++; ! 2348: dslots_load_filled++; ! 2349: ! 2350: if (CONSTANT_P (src_reg)) ! 2351: mips_count_memory_refs (src_reg, 1); ! 2352: ! 2353: if (CONSTANT_P (dest_reg)) ! 2354: mips_count_memory_refs (dest_reg, 1); ! 2355: } ! 2356: ! 2357: /* Emit load/stores now if we have run out of registers or are ! 2358: at the end of the move. */ ! 2359: ! 2360: if (++num == num_regs || bytes == 0) ! 2361: { ! 2362: /* If only load/store, we need a NOP after the load. */ ! 2363: if (num == 1) ! 2364: { ! 2365: load_store[0].load = load_store[0].load_nop; ! 2366: if (TARGET_STATS && move_type != BLOCK_MOVE_LAST) ! 2367: dslots_load_filled--; ! 2368: } ! 2369: ! 2370: if (move_type != BLOCK_MOVE_LAST) ! 2371: { ! 2372: for (i = 0; i < num; i++) ! 2373: { ! 2374: int offset; ! 2375: ! 2376: if (!operands[i+4]) ! 2377: abort (); ! 2378: ! 2379: if (GET_MODE (operands[i+4]) != load_store[i].mode) ! 2380: operands[i+4] = gen_rtx (REG, load_store[i].mode, REGNO (operands[i+4])); ! 2381: ! 2382: offset = load_store[i].offset; ! 2383: xoperands[0] = operands[i+4]; ! 2384: xoperands[1] = gen_rtx (MEM, load_store[i].mode, ! 2385: plus_constant (src_reg, offset)); ! 2386: ! 2387: if (use_lwl_lwr) ! 2388: xoperands[2] = gen_rtx (MEM, load_store[i].mode, ! 2389: plus_constant (src_reg, UNITS_PER_WORD-1+offset)); ! 2390: ! 2391: output_asm_insn (load_store[i].load, xoperands); ! 2392: } ! 2393: } ! 2394: ! 2395: for (i = 0; i < num; i++) ! 2396: { ! 2397: int last_p = (i == num-1 && bytes == 0); ! 2398: int offset = load_store[i].offset; ! 2399: ! 2400: xoperands[0] = operands[i+4]; ! 2401: xoperands[1] = gen_rtx (MEM, load_store[i].mode, ! 2402: plus_constant (dest_reg, offset)); ! 2403: ! 2404: ! 2405: if (use_lwl_lwr) ! 2406: xoperands[2] = gen_rtx (MEM, load_store[i].mode, ! 2407: plus_constant (dest_reg, UNITS_PER_WORD-1+offset)); ! 2408: ! 2409: if (move_type == BLOCK_MOVE_NORMAL) ! 2410: output_asm_insn (load_store[i].store, xoperands); ! 2411: ! 2412: else if (move_type == BLOCK_MOVE_NOT_LAST) ! 2413: { ! 2414: if (!last_p) ! 2415: output_asm_insn (load_store[i].store, xoperands); ! 2416: ! 2417: else if (load_store[i].final != (char *)0) ! 2418: output_asm_insn (load_store[i].final, xoperands); ! 2419: } ! 2420: ! 2421: else if (last_p) ! 2422: output_asm_insn (load_store[i].last_store, xoperands); ! 2423: } ! 2424: ! 2425: num = 0; /* reset load_store */ ! 2426: use_lwl_lwr = FALSE; /* reset whether or not we used lwl/lwr */ ! 2427: } ! 2428: } ! 2429: ! 2430: if (TARGET_GAS && move_type != BLOCK_MOVE_LAST && --set_noreorder == 0) ! 2431: output_asm_insn (".set\treorder", operands); ! 2432: ! 2433: return ""; ! 2434: } ! 2435: ! 2436: ! 2437: /* Argument support functions. */ ! 2438: ! 2439: /* Initialize CUMULATIVE_ARGS for a function. */ ! 2440: ! 2441: void ! 2442: init_cumulative_args (cum, fntype, libname) ! 2443: CUMULATIVE_ARGS *cum; /* argument info to initialize */ ! 2444: tree fntype; /* tree ptr for function decl */ ! 2445: rtx libname; /* SYMBOL_REF of library name or 0 */ ! 2446: { ! 2447: static CUMULATIVE_ARGS zero_cum; ! 2448: tree param, next_param; ! 2449: ! 2450: if (TARGET_DEBUG_E_MODE) ! 2451: { ! 2452: fprintf (stderr, "\ninit_cumulative_args, fntype = 0x%.8lx", (long)fntype); ! 2453: if (!fntype) ! 2454: fputc ('\n', stderr); ! 2455: ! 2456: else ! 2457: { ! 2458: tree ret_type = TREE_TYPE (fntype); ! 2459: fprintf (stderr, ", fntype code = %s, ret code = %s\n", ! 2460: tree_code_name[ (int)TREE_CODE (fntype) ], ! 2461: tree_code_name[ (int)TREE_CODE (ret_type) ]); ! 2462: } ! 2463: } ! 2464: ! 2465: *cum = zero_cum; ! 2466: ! 2467: /* Determine if this function has variable arguments. This is ! 2468: indicated by the last argument being 'void_type_mode' if there ! 2469: are no variable arguments. The standard MIPS calling sequence ! 2470: passes all arguments in the general purpose registers in this ! 2471: case. */ ! 2472: ! 2473: for (param = (fntype) ? TYPE_ARG_TYPES (fntype) : 0; ! 2474: param != (tree)0; ! 2475: param = next_param) ! 2476: { ! 2477: next_param = TREE_CHAIN (param); ! 2478: if (next_param == (tree)0 && TREE_VALUE (param) != void_type_node) ! 2479: cum->gp_reg_found = 1; ! 2480: } ! 2481: } ! 2482: ! 2483: /* Advance the argument to the next argument position. */ ! 2484: ! 2485: void ! 2486: function_arg_advance (cum, mode, type, named) ! 2487: CUMULATIVE_ARGS *cum; /* current arg information */ ! 2488: enum machine_mode mode; /* current arg mode */ ! 2489: tree type; /* type of the argument or 0 if lib support */ ! 2490: int named; /* whether or not the argument was named */ ! 2491: { ! 2492: if (TARGET_DEBUG_E_MODE) ! 2493: fprintf (stderr, ! 2494: "function_adv( {gp reg found = %d, arg # = %2d, words = %2d}, %4s, 0x%.8x, %d )\n\n", ! 2495: cum->gp_reg_found, cum->arg_number, cum->arg_words, GET_MODE_NAME (mode), ! 2496: type, named); ! 2497: ! 2498: cum->arg_number++; ! 2499: switch (mode) ! 2500: { ! 2501: default: ! 2502: error ("Illegal mode given to function_arg_advance"); ! 2503: break; ! 2504: ! 2505: case VOIDmode: ! 2506: break; ! 2507: ! 2508: case BLKmode: ! 2509: cum->gp_reg_found = 1; ! 2510: cum->arg_words += (int_size_in_bytes (type) + 3) / 4; ! 2511: break; ! 2512: ! 2513: case SFmode: ! 2514: cum->arg_words++; ! 2515: break; ! 2516: ! 2517: case DFmode: ! 2518: cum->arg_words += 2; ! 2519: break; ! 2520: ! 2521: case DImode: ! 2522: cum->gp_reg_found = 1; ! 2523: cum->arg_words += 2; ! 2524: break; ! 2525: ! 2526: case QImode: ! 2527: case HImode: ! 2528: case SImode: ! 2529: cum->gp_reg_found = 1; ! 2530: cum->arg_words++; ! 2531: break; ! 2532: } ! 2533: } ! 2534: ! 2535: /* Return a RTL expression containing the register for the given mode, ! 2536: or 0 if the argument is too be passed on the stack. */ ! 2537: ! 2538: struct rtx_def * ! 2539: function_arg (cum, mode, type, named) ! 2540: CUMULATIVE_ARGS *cum; /* current arg information */ ! 2541: enum machine_mode mode; /* current arg mode */ ! 2542: tree type; /* type of the argument or 0 if lib support */ ! 2543: int named; /* != 0 for normal args, == 0 for ... args */ ! 2544: { ! 2545: rtx ret; ! 2546: int regbase = -1; ! 2547: int bias = 0; ! 2548: int struct_p = ((type != (tree)0) ! 2549: && (TREE_CODE (type) == RECORD_TYPE ! 2550: || TREE_CODE (type) == UNION_TYPE)); ! 2551: ! 2552: if (TARGET_DEBUG_E_MODE) ! 2553: fprintf (stderr, ! 2554: "function_arg( {gp reg found = %d, arg # = %2d, words = %2d}, %4s, 0x%.8x, %d ) = ", ! 2555: cum->gp_reg_found, cum->arg_number, cum->arg_words, GET_MODE_NAME (mode), ! 2556: type, named); ! 2557: ! 2558: switch (mode) ! 2559: { ! 2560: default: ! 2561: error ("Illegal mode given to function_arg"); ! 2562: break; ! 2563: ! 2564: case SFmode: ! 2565: if (cum->gp_reg_found || cum->arg_number >= 2) ! 2566: regbase = GP_ARG_FIRST; ! 2567: else { ! 2568: regbase = (TARGET_SOFT_FLOAT) ? GP_ARG_FIRST : FP_ARG_FIRST; ! 2569: if (cum->arg_words == 1) /* first arg was float */ ! 2570: bias = 1; /* use correct reg */ ! 2571: } ! 2572: ! 2573: break; ! 2574: ! 2575: case DFmode: ! 2576: cum->arg_words += (cum->arg_words & 1); ! 2577: regbase = (cum->gp_reg_found || TARGET_SOFT_FLOAT) ! 2578: ? GP_ARG_FIRST ! 2579: : FP_ARG_FIRST; ! 2580: break; ! 2581: ! 2582: case BLKmode: ! 2583: if (type != (tree)0 && TYPE_ALIGN (type) > BITS_PER_WORD) ! 2584: cum->arg_words += (cum->arg_words & 1); ! 2585: ! 2586: regbase = GP_ARG_FIRST; ! 2587: break; ! 2588: ! 2589: case VOIDmode: ! 2590: case QImode: ! 2591: case HImode: ! 2592: case SImode: ! 2593: regbase = GP_ARG_FIRST; ! 2594: break; ! 2595: ! 2596: case DImode: ! 2597: cum->arg_words += (cum->arg_words & 1); ! 2598: regbase = GP_ARG_FIRST; ! 2599: } ! 2600: ! 2601: if (cum->arg_words >= MAX_ARGS_IN_REGISTERS) ! 2602: { ! 2603: if (TARGET_DEBUG_E_MODE) ! 2604: fprintf (stderr, "<stack>%s\n", struct_p ? ", [struct]" : ""); ! 2605: ! 2606: ret = (rtx)0; ! 2607: } ! 2608: else ! 2609: { ! 2610: if (regbase == -1) ! 2611: abort (); ! 2612: ! 2613: ret = gen_rtx (REG, mode, regbase + cum->arg_words + bias); ! 2614: ! 2615: if (TARGET_DEBUG_E_MODE) ! 2616: fprintf (stderr, "%s%s\n", reg_names[regbase + cum->arg_words + bias], ! 2617: struct_p ? ", [struct]" : ""); ! 2618: ! 2619: /* The following is a hack in order to pass 1 byte structures ! 2620: the same way that the MIPS compiler does (namely by passing ! 2621: the structure in the high byte or half word of the register). ! 2622: This also makes varargs work. If we have such a structure, ! 2623: we save the adjustment RTL, and the call define expands will ! 2624: emit them. For the VOIDmode argument (argument after the ! 2625: last real argument, pass back a parallel vector holding each ! 2626: of the adjustments. */ ! 2627: ! 2628: if (struct_p && (mode == QImode || mode == HImode)) ! 2629: { ! 2630: rtx amount = GEN_INT (BITS_PER_WORD - GET_MODE_BITSIZE (mode)); ! 2631: rtx reg = gen_rtx (REG, SImode, regbase + cum->arg_words + bias); ! 2632: cum->adjust[ cum->num_adjusts++ ] = gen_ashlsi3 (reg, reg, amount); ! 2633: } ! 2634: } ! 2635: ! 2636: if (mode == VOIDmode && cum->num_adjusts > 0) ! 2637: ret = gen_rtx (PARALLEL, VOIDmode, gen_rtvec_v (cum->num_adjusts, cum->adjust)); ! 2638: ! 2639: return ret; ! 2640: } ! 2641: ! 2642: ! 2643: int ! 2644: function_arg_partial_nregs (cum, mode, type, named) ! 2645: CUMULATIVE_ARGS *cum; /* current arg information */ ! 2646: enum machine_mode mode; /* current arg mode */ ! 2647: tree type; /* type of the argument or 0 if lib support */ ! 2648: int named; /* != 0 for normal args, == 0 for ... args */ ! 2649: { ! 2650: if (mode == BLKmode && cum->arg_words < MAX_ARGS_IN_REGISTERS) ! 2651: { ! 2652: int words = (int_size_in_bytes (type) + 3) / 4; ! 2653: ! 2654: if (words + cum->arg_words <= MAX_ARGS_IN_REGISTERS) ! 2655: return 0; /* structure fits in registers */ ! 2656: ! 2657: if (TARGET_DEBUG_E_MODE) ! 2658: fprintf (stderr, "function_arg_partial_nregs = %d\n", ! 2659: MAX_ARGS_IN_REGISTERS - cum->arg_words); ! 2660: ! 2661: return MAX_ARGS_IN_REGISTERS - cum->arg_words; ! 2662: } ! 2663: ! 2664: else if (mode == DImode && cum->arg_words == MAX_ARGS_IN_REGISTERS-1) ! 2665: { ! 2666: if (TARGET_DEBUG_E_MODE) ! 2667: fprintf (stderr, "function_arg_partial_nregs = 1\n"); ! 2668: ! 2669: return 1; ! 2670: } ! 2671: ! 2672: return 0; ! 2673: } ! 2674: ! 2675: ! 2676: /* Print the options used in the assembly file. */ ! 2677: ! 2678: static struct {char *name; int value;} target_switches [] ! 2679: = TARGET_SWITCHES; ! 2680: ! 2681: void ! 2682: print_options (out) ! 2683: FILE *out; ! 2684: { ! 2685: int line_len; ! 2686: int len; ! 2687: int j; ! 2688: char **p; ! 2689: int mask = TARGET_DEFAULT; ! 2690: ! 2691: /* Allow assembly language comparisons with -mdebug eliminating the ! 2692: compiler version number and switch lists. */ ! 2693: ! 2694: if (TARGET_DEBUG_MODE) ! 2695: return; ! 2696: ! 2697: fprintf (out, "\n # %s %s", language_string, version_string); ! 2698: #ifdef TARGET_VERSION_INTERNAL ! 2699: TARGET_VERSION_INTERNAL (out); ! 2700: #endif ! 2701: #ifdef __GNUC__ ! 2702: fprintf (out, " compiled by GNU C\n\n"); ! 2703: #else ! 2704: fprintf (out, " compiled by CC\n\n"); ! 2705: #endif ! 2706: ! 2707: fprintf (out, " # Cc1 defaults:"); ! 2708: line_len = 32767; ! 2709: for (j = 0; j < sizeof target_switches / sizeof target_switches[0]; j++) ! 2710: { ! 2711: if (target_switches[j].name[0] != '\0' ! 2712: && target_switches[j].value > 0 ! 2713: && (target_switches[j].value & mask) == target_switches[j].value) ! 2714: { ! 2715: mask &= ~ target_switches[j].value; ! 2716: len = strlen (target_switches[j].name) + 1; ! 2717: if (len + line_len > 79) ! 2718: { ! 2719: line_len = 2; ! 2720: fputs ("\n #", out); ! 2721: } ! 2722: fprintf (out, " -m%s", target_switches[j].name); ! 2723: line_len += len; ! 2724: } ! 2725: } ! 2726: ! 2727: fprintf (out, "\n\n # Cc1 arguments (-G value = %d, Cpu = %s, ISA = %d):", ! 2728: mips_section_threshold, mips_cpu_string, mips_isa); ! 2729: ! 2730: line_len = 32767; ! 2731: for (p = &save_argv[1]; *p != (char *)0; p++) ! 2732: { ! 2733: char *arg = *p; ! 2734: if (*arg == '-') ! 2735: { ! 2736: len = strlen (arg) + 1; ! 2737: if (len + line_len > 79) ! 2738: { ! 2739: line_len = 2; ! 2740: fputs ("\n #", out); ! 2741: } ! 2742: fprintf (out, " %s", *p); ! 2743: line_len += len; ! 2744: } ! 2745: } ! 2746: ! 2747: fputs ("\n\n", out); ! 2748: } ! 2749: ! 2750: ! 2751: /* Abort after printing out a specific insn. */ ! 2752: ! 2753: void ! 2754: abort_with_insn (insn, reason) ! 2755: rtx insn; ! 2756: char *reason; ! 2757: { ! 2758: error (reason); ! 2759: debug_rtx (insn); ! 2760: abort (); ! 2761: } ! 2762: ! 2763: /* Write a message to stderr (for use in macros expanded in files that do not ! 2764: include stdio.h). */ ! 2765: ! 2766: void ! 2767: trace (s, s1, s2) ! 2768: char *s, *s1, *s2; ! 2769: { ! 2770: fprintf (stderr, s, s1, s2); ! 2771: } ! 2772: ! 2773: ! 2774: #ifdef SIGINFO ! 2775: ! 2776: static void ! 2777: siginfo (signo) ! 2778: int signo; ! 2779: { ! 2780: fprintf (stderr, "compiling '%s' in '%s'\n", ! 2781: (current_function_name != (char *)0) ? current_function_name : "<toplevel>", ! 2782: (current_function_file != (char *)0) ? current_function_file : "<no file>"); ! 2783: fflush (stderr); ! 2784: } ! 2785: #endif /* SIGINFO */ ! 2786: ! 2787: ! 2788: /* Set up the threshold for data to go into the small data area, instead ! 2789: of the normal data area, and detect any conflicts in the switches. */ ! 2790: ! 2791: void ! 2792: override_options () ! 2793: { ! 2794: register int i, start; ! 2795: register int regno; ! 2796: register enum machine_mode mode; ! 2797: ! 2798: mips_section_threshold = (g_switch_set) ? g_switch_value : MIPS_DEFAULT_GVALUE; ! 2799: ! 2800: /* Identify the processor type */ ! 2801: if (mips_cpu_string == (char *)0 ! 2802: || !strcmp (mips_cpu_string, "default") ! 2803: || !strcmp (mips_cpu_string, "DEFAULT")) ! 2804: { ! 2805: mips_cpu_string = "default"; ! 2806: mips_cpu = PROCESSOR_DEFAULT; ! 2807: } ! 2808: ! 2809: else ! 2810: { ! 2811: char *p = mips_cpu_string; ! 2812: ! 2813: if (*p == 'r' || *p == 'R') ! 2814: p++; ! 2815: ! 2816: /* Since there is no difference between a R2000 and R3000 in ! 2817: terms of the scheduler, we collapse them into just an R3000. */ ! 2818: ! 2819: mips_cpu = PROCESSOR_DEFAULT; ! 2820: switch (*p) ! 2821: { ! 2822: case '2': ! 2823: if (!strcmp (p, "2000") || !strcmp (p, "2k") || !strcmp (p, "2K")) ! 2824: mips_cpu = PROCESSOR_R3000; ! 2825: break; ! 2826: ! 2827: case '3': ! 2828: if (!strcmp (p, "3000") || !strcmp (p, "3k") || !strcmp (p, "3K")) ! 2829: mips_cpu = PROCESSOR_R3000; ! 2830: break; ! 2831: ! 2832: case '4': ! 2833: if (!strcmp (p, "4000") || !strcmp (p, "4k") || !strcmp (p, "4K")) ! 2834: mips_cpu = PROCESSOR_R4000; ! 2835: break; ! 2836: ! 2837: case '6': ! 2838: if (!strcmp (p, "6000") || !strcmp (p, "6k") || !strcmp (p, "6K")) ! 2839: mips_cpu = PROCESSOR_R6000; ! 2840: break; ! 2841: } ! 2842: ! 2843: if (mips_cpu == PROCESSOR_DEFAULT) ! 2844: { ! 2845: error ("bad value (%s) for -mcpu= switch", mips_cpu_string); ! 2846: mips_cpu_string = "default"; ! 2847: } ! 2848: } ! 2849: ! 2850: /* Now get the architectural level. */ ! 2851: if (mips_isa_string == (char *)0) ! 2852: mips_isa = 1; ! 2853: ! 2854: else if (isdigit (*mips_isa_string)) ! 2855: mips_isa = atoi (mips_isa_string); ! 2856: ! 2857: else ! 2858: { ! 2859: error ("bad value (%s) for -mips switch", mips_isa_string); ! 2860: mips_isa = 1; ! 2861: } ! 2862: ! 2863: if (mips_isa < 0 || mips_isa > 3) ! 2864: error ("-mips%d not supported", mips_isa); ! 2865: ! 2866: else if (mips_isa > 1 ! 2867: && (mips_cpu == PROCESSOR_DEFAULT || mips_cpu == PROCESSOR_R3000)) ! 2868: error ("-mcpu=%s does not support -mips%d", mips_cpu_string, mips_isa); ! 2869: ! 2870: else if (mips_cpu == PROCESSOR_R6000 && mips_isa > 2) ! 2871: error ("-mcpu=%s does not support -mips%d", mips_cpu_string, mips_isa); ! 2872: ! 2873: /* make sure sizes of ints/longs/etc. are ok */ ! 2874: if (mips_isa < 3) ! 2875: { ! 2876: if (TARGET_INT64) ! 2877: fatal ("Only the r4000 can support 64 bit ints"); ! 2878: ! 2879: else if (TARGET_LONG64) ! 2880: fatal ("Only the r4000 can support 64 bit longs"); ! 2881: ! 2882: else if (TARGET_LLONG128) ! 2883: fatal ("Only the r4000 can support 128 bit long longs"); ! 2884: ! 2885: else if (TARGET_FLOAT64) ! 2886: fatal ("Only the r4000 can support 64 bit fp registers"); ! 2887: } ! 2888: else if (TARGET_INT64 || TARGET_LONG64 || TARGET_LLONG128 || TARGET_FLOAT64) ! 2889: warning ("r4000 64/128 bit types not yet supported"); ! 2890: ! 2891: /* Tell halfpic.c that we have half-pic code if we do. */ ! 2892: if (TARGET_HALF_PIC) ! 2893: HALF_PIC_INIT (); ! 2894: ! 2895: /* -mrnames says to use the MIPS software convention for register ! 2896: names instead of the hardware names (ie, a0 instead of $4). ! 2897: We do this by switching the names in mips_reg_names, which the ! 2898: reg_names points into via the REGISTER_NAMES macro. */ ! 2899: ! 2900: if (TARGET_NAME_REGS) ! 2901: { ! 2902: if (TARGET_GAS) ! 2903: { ! 2904: target_flags &= ~ MASK_NAME_REGS; ! 2905: error ("Gas does not support the MIPS software register name convention."); ! 2906: } ! 2907: else ! 2908: bcopy ((char *) mips_sw_reg_names, (char *) mips_reg_names, sizeof (mips_reg_names)); ! 2909: } ! 2910: ! 2911: /* If this is OSF/1, set up a SIGINFO handler so we can see what function ! 2912: is currently being compiled. */ ! 2913: #ifdef SIGINFO ! 2914: if (getenv ("GCC_SIGINFO") != (char *)0) ! 2915: { ! 2916: struct sigaction action; ! 2917: action.sa_handler = siginfo; ! 2918: action.sa_mask = 0; ! 2919: action.sa_flags = SA_RESTART; ! 2920: sigaction (SIGINFO, &action, (struct sigaction *)0); ! 2921: } ! 2922: #endif ! 2923: ! 2924: #if defined(_IOLBF) ! 2925: #if defined(ultrix) || defined(__ultrix) || defined(__OSF1__) || defined(__osf__) || defined(osf) ! 2926: /* If -mstats and -quiet, make stderr line buffered. */ ! 2927: if (quiet_flag && TARGET_STATS) ! 2928: setvbuf (stderr, (char *)0, _IOLBF, BUFSIZ); ! 2929: #endif ! 2930: #endif ! 2931: ! 2932: /* Set up the classification arrays now. */ ! 2933: mips_rtx_classify[(int)PLUS] = CLASS_ADD_OP; ! 2934: mips_rtx_classify[(int)MINUS] = CLASS_ADD_OP; ! 2935: mips_rtx_classify[(int)DIV] = CLASS_DIVMOD_OP; ! 2936: mips_rtx_classify[(int)MOD] = CLASS_DIVMOD_OP; ! 2937: mips_rtx_classify[(int)UDIV] = CLASS_DIVMOD_OP | CLASS_UNSIGNED_OP; ! 2938: mips_rtx_classify[(int)UMOD] = CLASS_DIVMOD_OP | CLASS_UNSIGNED_OP; ! 2939: mips_rtx_classify[(int)EQ] = CLASS_CMP_OP | CLASS_EQUALITY_OP | CLASS_FCMP_OP; ! 2940: mips_rtx_classify[(int)NE] = CLASS_CMP_OP | CLASS_EQUALITY_OP | CLASS_FCMP_OP; ! 2941: mips_rtx_classify[(int)GT] = CLASS_CMP_OP | CLASS_FCMP_OP; ! 2942: mips_rtx_classify[(int)GE] = CLASS_CMP_OP | CLASS_FCMP_OP; ! 2943: mips_rtx_classify[(int)LT] = CLASS_CMP_OP | CLASS_FCMP_OP; ! 2944: mips_rtx_classify[(int)LE] = CLASS_CMP_OP | CLASS_FCMP_OP; ! 2945: mips_rtx_classify[(int)GTU] = CLASS_CMP_OP | CLASS_UNSIGNED_OP; ! 2946: mips_rtx_classify[(int)GEU] = CLASS_CMP_OP | CLASS_UNSIGNED_OP; ! 2947: mips_rtx_classify[(int)LTU] = CLASS_CMP_OP | CLASS_UNSIGNED_OP; ! 2948: mips_rtx_classify[(int)LEU] = CLASS_CMP_OP | CLASS_UNSIGNED_OP; ! 2949: ! 2950: mips_print_operand_punct['?'] = TRUE; ! 2951: mips_print_operand_punct['#'] = TRUE; ! 2952: mips_print_operand_punct['&'] = TRUE; ! 2953: mips_print_operand_punct['!'] = TRUE; ! 2954: mips_print_operand_punct['*'] = TRUE; ! 2955: mips_print_operand_punct['@'] = TRUE; ! 2956: mips_print_operand_punct['.'] = TRUE; ! 2957: mips_print_operand_punct['('] = TRUE; ! 2958: mips_print_operand_punct[')'] = TRUE; ! 2959: mips_print_operand_punct['['] = TRUE; ! 2960: mips_print_operand_punct[']'] = TRUE; ! 2961: mips_print_operand_punct['<'] = TRUE; ! 2962: mips_print_operand_punct['>'] = TRUE; ! 2963: mips_print_operand_punct['{'] = TRUE; ! 2964: mips_print_operand_punct['}'] = TRUE; ! 2965: ! 2966: mips_char_to_class['d'] = GR_REGS; ! 2967: mips_char_to_class['f'] = ((TARGET_HARD_FLOAT) ? FP_REGS : NO_REGS); ! 2968: mips_char_to_class['h'] = HI_REG; ! 2969: mips_char_to_class['l'] = LO_REG; ! 2970: mips_char_to_class['x'] = MD_REGS; ! 2971: mips_char_to_class['y'] = GR_REGS; ! 2972: mips_char_to_class['z'] = ST_REGS; ! 2973: ! 2974: /* Set up array to map GCC register number to debug register number. ! 2975: Ignore the special purpose register numbers. */ ! 2976: ! 2977: for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) ! 2978: mips_dbx_regno[i] = -1; ! 2979: ! 2980: start = GP_DBX_FIRST - GP_REG_FIRST; ! 2981: for (i = GP_REG_FIRST; i <= GP_REG_LAST; i++) ! 2982: mips_dbx_regno[i] = i + start; ! 2983: ! 2984: start = FP_DBX_FIRST - FP_REG_FIRST; ! 2985: for (i = FP_REG_FIRST; i <= FP_REG_LAST; i++) ! 2986: mips_dbx_regno[i] = i + start; ! 2987: ! 2988: /* Set up array giving whether a given register can hold a given mode. ! 2989: At present, restrict ints from being in FP registers, because reload ! 2990: is a little enthusiastic about storing extra values in FP registers, ! 2991: and this is not good for things like OS kernels. Also, due to the ! 2992: mandatory delay, it is as fast to load from cached memory as to move ! 2993: from the FP register. */ ! 2994: ! 2995: for (mode = VOIDmode; ! 2996: mode != MAX_MACHINE_MODE; ! 2997: mode = (enum machine_mode)((int)mode + 1)) ! 2998: { ! 2999: register int size = GET_MODE_SIZE (mode); ! 3000: register enum mode_class class = GET_MODE_CLASS (mode); ! 3001: ! 3002: for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) ! 3003: { ! 3004: register int temp; ! 3005: ! 3006: if (mode == CC_FPmode || mode == CC_REV_FPmode) ! 3007: temp = (regno == FPSW_REGNUM); ! 3008: ! 3009: else if (GP_REG_P (regno)) ! 3010: temp = ((regno & 1) == 0 || (size <= UNITS_PER_WORD)); ! 3011: ! 3012: else if (FP_REG_P (regno)) ! 3013: temp = ((TARGET_FLOAT64 || ((regno & 1) == 0)) ! 3014: && (class == MODE_FLOAT ! 3015: || class == MODE_COMPLEX_FLOAT ! 3016: || (TARGET_DEBUG_H_MODE && class == MODE_INT))); ! 3017: ! 3018: else if (MD_REG_P (regno)) ! 3019: temp = (mode == SImode || (regno == MD_REG_FIRST && mode == DImode)); ! 3020: ! 3021: else ! 3022: temp = FALSE; ! 3023: ! 3024: mips_hard_regno_mode_ok[(int)mode][regno] = temp; ! 3025: } ! 3026: } ! 3027: } ! 3028: ! 3029: ! 3030: /* ! 3031: * The MIPS debug format wants all automatic variables and arguments ! 3032: * to be in terms of the virtual frame pointer (stack pointer before ! 3033: * any adjustment in the function), while the MIPS 3.0 linker wants ! 3034: * the frame pointer to be the stack pointer after the initial ! 3035: * adjustment. So, we do the adjustment here. The arg pointer (which ! 3036: * is eliminated) points to the virtual frame pointer, while the frame ! 3037: * pointer (which may be eliminated) points to the stack pointer after ! 3038: * the initial adjustments. ! 3039: */ ! 3040: ! 3041: int ! 3042: mips_debugger_offset (addr, offset) ! 3043: rtx addr; ! 3044: int offset; ! 3045: { ! 3046: rtx offset2 = const0_rtx; ! 3047: rtx reg = eliminate_constant_term (addr, &offset2); ! 3048: ! 3049: if (!offset) ! 3050: offset = INTVAL (offset2); ! 3051: ! 3052: if (reg == stack_pointer_rtx || reg == frame_pointer_rtx) ! 3053: { ! 3054: int frame_size = (!current_frame_info.initialized) ! 3055: ? compute_frame_size (get_frame_size ()) ! 3056: : current_frame_info.total_size; ! 3057: ! 3058: offset = offset - frame_size; ! 3059: } ! 3060: /* sdbout_parms does not want this to crash for unrecognized cases. */ ! 3061: #if 0 ! 3062: else if (reg != arg_pointer_rtx) ! 3063: abort_with_insn (addr, "mips_debugger_offset called with non stack/frame/arg pointer."); ! 3064: #endif ! 3065: ! 3066: return offset; ! 3067: } ! 3068: ! 3069: ! 3070: /* A C compound statement to output to stdio stream STREAM the ! 3071: assembler syntax for an instruction operand X. X is an RTL ! 3072: expression. ! 3073: ! 3074: CODE is a value that can be used to specify one of several ways ! 3075: of printing the operand. It is used when identical operands ! 3076: must be printed differently depending on the context. CODE ! 3077: comes from the `%' specification that was used to request ! 3078: printing of the operand. If the specification was just `%DIGIT' ! 3079: then CODE is 0; if the specification was `%LTR DIGIT' then CODE ! 3080: is the ASCII code for LTR. ! 3081: ! 3082: If X is a register, this macro should print the register's name. ! 3083: The names can be found in an array `reg_names' whose type is ! 3084: `char *[]'. `reg_names' is initialized from `REGISTER_NAMES'. ! 3085: ! 3086: When the machine description has a specification `%PUNCT' (a `%' ! 3087: followed by a punctuation character), this macro is called with ! 3088: a null pointer for X and the punctuation character for CODE. ! 3089: ! 3090: The MIPS specific codes are: ! 3091: ! 3092: 'X' X is CONST_INT, prints 32 bits in hexadecimal format = "0x%08x", ! 3093: 'x' X is CONST_INT, prints 16 bits in hexadecimal format = "0x%04x", ! 3094: 'd' output integer constant in decimal, ! 3095: 'z' if the operand is 0, use $0 instead of normal operand. ! 3096: 'D' print second register of double-word register operand. ! 3097: 'L' print low-order register of double-word register operand. ! 3098: 'M' print high-order register of double-word register operand. ! 3099: 'C' print part of opcode for a branch condition. ! 3100: 'N' print part of opcode for a branch condition, inverted. ! 3101: '(' Turn on .set noreorder ! 3102: ')' Turn on .set reorder ! 3103: '[' Turn on .set noat ! 3104: ']' Turn on .set at ! 3105: '<' Turn on .set nomacro ! 3106: '>' Turn on .set macro ! 3107: '{' Turn on .set volatile (not GAS) ! 3108: '}' Turn on .set novolatile (not GAS) ! 3109: '&' Turn on .set noreorder if filling delay slots ! 3110: '*' Turn on both .set noreorder and .set nomacro if filling delay slots ! 3111: '!' Turn on .set nomacro if filling delay slots ! 3112: '#' Print nop if in a .set noreorder section. ! 3113: '?' Print 'l' if we are to use a branch likely instead of normal branch. ! 3114: '@' Print the name of the assembler temporary register (at or $1). ! 3115: '.' Print the name of the register with a hard-wired zero (zero or $0). */ ! 3116: ! 3117: void ! 3118: print_operand (file, op, letter) ! 3119: FILE *file; /* file to write to */ ! 3120: rtx op; /* operand to print */ ! 3121: int letter; /* %<letter> or 0 */ ! 3122: { ! 3123: register enum rtx_code code; ! 3124: ! 3125: if (PRINT_OPERAND_PUNCT_VALID_P (letter)) ! 3126: { ! 3127: switch (letter) ! 3128: { ! 3129: default: ! 3130: error ("PRINT_OPERAND: Unknown punctuation '%c'", letter); ! 3131: break; ! 3132: ! 3133: case '?': ! 3134: if (mips_branch_likely) ! 3135: putc ('l', file); ! 3136: break; ! 3137: ! 3138: case '@': ! 3139: fputs (reg_names [GP_REG_FIRST + 1], file); ! 3140: break; ! 3141: ! 3142: case '.': ! 3143: fputs (reg_names [GP_REG_FIRST + 0], file); ! 3144: break; ! 3145: ! 3146: case '&': ! 3147: if (final_sequence != 0 && set_noreorder++ == 0) ! 3148: fputs (".set\tnoreorder\n\t", file); ! 3149: break; ! 3150: ! 3151: case '*': ! 3152: if (final_sequence != 0) ! 3153: { ! 3154: if (set_noreorder++ == 0) ! 3155: fputs (".set\tnoreorder\n\t", file); ! 3156: ! 3157: if (set_nomacro++ == 0) ! 3158: fputs (".set\tnomacro\n\t", file); ! 3159: } ! 3160: break; ! 3161: ! 3162: case '!': ! 3163: if (final_sequence != 0 && set_nomacro++ == 0) ! 3164: fputs ("\n\t.set\tnomacro", file); ! 3165: break; ! 3166: ! 3167: case '#': ! 3168: if (set_noreorder != 0) ! 3169: fputs ("\n\tnop", file); ! 3170: ! 3171: else if (TARGET_GAS || TARGET_STATS) ! 3172: fputs ("\n\t#nop", file); ! 3173: ! 3174: break; ! 3175: ! 3176: case '(': ! 3177: if (set_noreorder++ == 0) ! 3178: fputs (".set\tnoreorder\n\t", file); ! 3179: break; ! 3180: ! 3181: case ')': ! 3182: if (set_noreorder == 0) ! 3183: error ("internal error: %%) found without a %%( in assembler pattern"); ! 3184: ! 3185: else if (--set_noreorder == 0) ! 3186: fputs ("\n\t.set\treorder", file); ! 3187: ! 3188: break; ! 3189: ! 3190: case '[': ! 3191: if (set_noat++ == 0) ! 3192: fputs (".set\tnoat\n\t", file); ! 3193: break; ! 3194: ! 3195: case ']': ! 3196: if (set_noat == 0) ! 3197: error ("internal error: %%] found without a %%[ in assembler pattern"); ! 3198: ! 3199: else if (--set_noat == 0) ! 3200: fputs ("\n\t.set\tat", file); ! 3201: ! 3202: break; ! 3203: ! 3204: case '<': ! 3205: if (set_nomacro++ == 0) ! 3206: fputs (".set\tnomacro\n\t", file); ! 3207: break; ! 3208: ! 3209: case '>': ! 3210: if (set_nomacro == 0) ! 3211: error ("internal error: %%> found without a %%< in assembler pattern"); ! 3212: ! 3213: else if (--set_nomacro == 0) ! 3214: fputs ("\n\t.set\tmacro", file); ! 3215: ! 3216: break; ! 3217: ! 3218: case '{': ! 3219: if (set_volatile++ == 0) ! 3220: fprintf (file, "%s.set\tvolatile\n\t", (TARGET_MIPS_AS) ? "" : "#"); ! 3221: break; ! 3222: ! 3223: case '}': ! 3224: if (set_volatile == 0) ! 3225: error ("internal error: %%} found without a %%{ in assembler pattern"); ! 3226: ! 3227: else if (--set_volatile == 0) ! 3228: fprintf (file, "\n\t%s.set\tnovolatile", (TARGET_MIPS_AS) ? "" : "#"); ! 3229: ! 3230: break; ! 3231: } ! 3232: return; ! 3233: } ! 3234: ! 3235: if (! op) ! 3236: { ! 3237: error ("PRINT_OPERAND null pointer"); ! 3238: return; ! 3239: } ! 3240: ! 3241: code = GET_CODE (op); ! 3242: if (letter == 'C') ! 3243: switch (code) ! 3244: { ! 3245: case EQ: fputs ("eq", file); break; ! 3246: case NE: fputs ("ne", file); break; ! 3247: case GT: fputs ("gt", file); break; ! 3248: case GE: fputs ("ge", file); break; ! 3249: case LT: fputs ("lt", file); break; ! 3250: case LE: fputs ("le", file); break; ! 3251: case GTU: fputs ("gtu", file); break; ! 3252: case GEU: fputs ("geu", file); break; ! 3253: case LTU: fputs ("ltu", file); break; ! 3254: case LEU: fputs ("leu", file); break; ! 3255: ! 3256: default: ! 3257: abort_with_insn (op, "PRINT_OPERAND, illegal insn for %%C"); ! 3258: } ! 3259: ! 3260: else if (letter == 'N') ! 3261: switch (code) ! 3262: { ! 3263: case EQ: fputs ("ne", file); break; ! 3264: case NE: fputs ("eq", file); break; ! 3265: case GT: fputs ("le", file); break; ! 3266: case GE: fputs ("lt", file); break; ! 3267: case LT: fputs ("ge", file); break; ! 3268: case LE: fputs ("gt", file); break; ! 3269: case GTU: fputs ("leu", file); break; ! 3270: case GEU: fputs ("ltu", file); break; ! 3271: case LTU: fputs ("geu", file); break; ! 3272: case LEU: fputs ("gtu", file); break; ! 3273: ! 3274: default: ! 3275: abort_with_insn (op, "PRINT_OPERAND, illegal insn for %%N"); ! 3276: } ! 3277: ! 3278: else if (code == REG) ! 3279: { ! 3280: register int regnum = REGNO (op); ! 3281: ! 3282: if (letter == 'M') ! 3283: regnum += MOST_SIGNIFICANT_WORD; ! 3284: ! 3285: else if (letter == 'L') ! 3286: regnum += LEAST_SIGNIFICANT_WORD; ! 3287: ! 3288: else if (letter == 'D') ! 3289: regnum++; ! 3290: ! 3291: fprintf (file, "%s", reg_names[regnum]); ! 3292: } ! 3293: ! 3294: else if (code == MEM) ! 3295: output_address (XEXP (op, 0)); ! 3296: ! 3297: else if (code == CONST_DOUBLE) ! 3298: { ! 3299: #if HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT ! 3300: union { double d; int i[2]; } u; ! 3301: u.i[0] = CONST_DOUBLE_LOW (op); ! 3302: u.i[1] = CONST_DOUBLE_HIGH (op); ! 3303: if (GET_MODE (op) == SFmode) ! 3304: { ! 3305: float f; ! 3306: f = u.d; ! 3307: u.d = f; ! 3308: } ! 3309: fprintf (file, "%.20e", u.d); ! 3310: #else ! 3311: fatal ("CONST_DOUBLE found in cross compilation"); ! 3312: #endif ! 3313: } ! 3314: ! 3315: else if ((letter == 'x') && (GET_CODE(op) == CONST_INT)) ! 3316: fprintf (file, "0x%04x", 0xffff & (INTVAL(op))); ! 3317: ! 3318: else if ((letter == 'X') && (GET_CODE(op) == CONST_INT)) ! 3319: fprintf (file, "0x%08x", INTVAL(op)); ! 3320: ! 3321: else if ((letter == 'd') && (GET_CODE(op) == CONST_INT)) ! 3322: fprintf (file, "%d", (INTVAL(op))); ! 3323: ! 3324: else if (letter == 'z' ! 3325: && (GET_CODE (op) == CONST_INT) ! 3326: && INTVAL (op) == 0) ! 3327: fputs (reg_names[GP_REG_FIRST], file); ! 3328: ! 3329: else if (letter == 'd' || letter == 'x' || letter == 'X') ! 3330: fatal ("PRINT_OPERAND: letter %c was found & insn was not CONST_INT", letter); ! 3331: ! 3332: else ! 3333: output_addr_const (file, op); ! 3334: } ! 3335: ! 3336: ! 3337: /* A C compound statement to output to stdio stream STREAM the ! 3338: assembler syntax for an instruction operand that is a memory ! 3339: reference whose address is ADDR. ADDR is an RTL expression. ! 3340: ! 3341: On some machines, the syntax for a symbolic address depends on ! 3342: the section that the address refers to. On these machines, ! 3343: define the macro `ENCODE_SECTION_INFO' to store the information ! 3344: into the `symbol_ref', and then check for it here. */ ! 3345: ! 3346: void ! 3347: print_operand_address (file, addr) ! 3348: FILE *file; ! 3349: rtx addr; ! 3350: { ! 3351: if (!addr) ! 3352: error ("PRINT_OPERAND_ADDRESS, null pointer"); ! 3353: ! 3354: else ! 3355: switch (GET_CODE (addr)) ! 3356: { ! 3357: default: ! 3358: abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, illegal insn #1"); ! 3359: break; ! 3360: ! 3361: case REG: ! 3362: if (REGNO (addr) == ARG_POINTER_REGNUM) ! 3363: abort_with_insn (addr, "Arg pointer not eliminated."); ! 3364: ! 3365: fprintf (file, "0(%s)", reg_names [REGNO (addr)]); ! 3366: break; ! 3367: ! 3368: case PLUS: ! 3369: { ! 3370: register rtx reg = (rtx)0; ! 3371: register rtx offset = (rtx)0; ! 3372: register rtx arg0 = XEXP (addr, 0); ! 3373: register rtx arg1 = XEXP (addr, 1); ! 3374: ! 3375: if (GET_CODE (arg0) == REG) ! 3376: { ! 3377: reg = arg0; ! 3378: offset = arg1; ! 3379: if (GET_CODE (offset) == REG) ! 3380: abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, 2 regs"); ! 3381: } ! 3382: else if (GET_CODE (arg1) == REG) ! 3383: { ! 3384: reg = arg1; ! 3385: offset = arg0; ! 3386: } ! 3387: else if (CONSTANT_P (arg0) && CONSTANT_P (arg1)) ! 3388: { ! 3389: output_addr_const (file, addr); ! 3390: break; ! 3391: } ! 3392: else ! 3393: abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, no regs"); ! 3394: ! 3395: if (!CONSTANT_P (offset)) ! 3396: abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, illegal insn #2"); ! 3397: ! 3398: if (REGNO (reg) == ARG_POINTER_REGNUM) ! 3399: abort_with_insn (addr, "Arg pointer not eliminated."); ! 3400: ! 3401: output_addr_const (file, offset); ! 3402: fprintf (file, "(%s)", reg_names [REGNO (reg)]); ! 3403: } ! 3404: break; ! 3405: ! 3406: case LABEL_REF: ! 3407: case SYMBOL_REF: ! 3408: case CONST_INT: ! 3409: case CONST: ! 3410: output_addr_const (file, addr); ! 3411: break; ! 3412: } ! 3413: } ! 3414: ! 3415: ! 3416: /* If optimizing for the global pointer, keep track of all of ! 3417: the externs, so that at the end of the file, we can emit ! 3418: the appropriate .extern declaration for them, before writing ! 3419: out the text section. We assume that all names passed to ! 3420: us are in the permanent obstack, so that they will be valid ! 3421: at the end of the compilation. ! 3422: ! 3423: If we have -G 0, or the extern size is unknown, don't bother ! 3424: emitting the .externs. */ ! 3425: ! 3426: int ! 3427: mips_output_external (file, decl, name) ! 3428: FILE *file; ! 3429: tree decl; ! 3430: char *name; ! 3431: { ! 3432: register struct extern_list *p; ! 3433: int len; ! 3434: ! 3435: if (TARGET_GP_OPT ! 3436: && mips_section_threshold != 0 ! 3437: && ((TREE_CODE (decl)) != FUNCTION_DECL) ! 3438: && ((len = int_size_in_bytes (TREE_TYPE (decl))) > 0)) ! 3439: { ! 3440: p = (struct extern_list *)permalloc ((long) sizeof (struct extern_list)); ! 3441: p->next = extern_head; ! 3442: p->name = name; ! 3443: p->size = len; ! 3444: extern_head = p; ! 3445: } ! 3446: return 0; ! 3447: } ! 3448: ! 3449: ! 3450: /* Compute a string to use as a temporary file name. */ ! 3451: ! 3452: static FILE * ! 3453: make_temp_file () ! 3454: { ! 3455: FILE *stream; ! 3456: char *base = getenv ("TMPDIR"); ! 3457: int len; ! 3458: ! 3459: if (base == (char *)0) ! 3460: { ! 3461: #ifdef P_tmpdir ! 3462: if (access (P_tmpdir, R_OK | W_OK) == 0) ! 3463: base = P_tmpdir; ! 3464: else ! 3465: #endif ! 3466: if (access ("/usr/tmp", R_OK | W_OK) == 0) ! 3467: base = "/usr/tmp/"; ! 3468: else ! 3469: base = "/tmp/"; ! 3470: } ! 3471: ! 3472: len = strlen (base); ! 3473: temp_filename = (char *) alloca (len + sizeof("/ccXXXXXX")); ! 3474: strcpy (temp_filename, base); ! 3475: if (len > 0 && temp_filename[len-1] != '/') ! 3476: temp_filename[len++] = '/'; ! 3477: ! 3478: strcpy (temp_filename + len, "ccXXXXXX"); ! 3479: mktemp (temp_filename); ! 3480: ! 3481: stream = fopen (temp_filename, "w+"); ! 3482: if (!stream) ! 3483: pfatal_with_name (temp_filename); ! 3484: ! 3485: unlink (temp_filename); ! 3486: return stream; ! 3487: } ! 3488: ! 3489: ! 3490: /* Emit a new filename to a stream. If this is MIPS ECOFF, watch out ! 3491: for .file's that start within a function. If we are smuggling stabs, try to ! 3492: put out a MIPS ECOFF file and a stab. */ ! 3493: ! 3494: void ! 3495: mips_output_filename (stream, name) ! 3496: FILE *stream; ! 3497: char *name; ! 3498: { ! 3499: static int first_time = TRUE; ! 3500: char ltext_label_name[100]; ! 3501: ! 3502: if (first_time) ! 3503: { ! 3504: first_time = FALSE; ! 3505: SET_FILE_NUMBER (); ! 3506: current_function_file = name; ! 3507: fprintf (stream, "\t.file\t%d \"%s\"\n", num_source_filenames, name); ! 3508: if (!TARGET_GAS && write_symbols == DBX_DEBUG) ! 3509: fprintf (stream, "\t#@stabs\n"); ! 3510: } ! 3511: ! 3512: else if (!TARGET_GAS && write_symbols == DBX_DEBUG) ! 3513: { ! 3514: ASM_GENERATE_INTERNAL_LABEL (ltext_label_name, "Ltext", 0); ! 3515: fprintf (stream, "%s \"%s\",%d,0,0,%s\n", ASM_STABS_OP, ! 3516: name, N_SOL, <ext_label_name[1]); ! 3517: } ! 3518: ! 3519: else if (name != current_function_file ! 3520: && strcmp (name, current_function_file) != 0) ! 3521: { ! 3522: if (inside_function && !TARGET_GAS) ! 3523: { ! 3524: if (!file_in_function_warning) ! 3525: { ! 3526: file_in_function_warning = TRUE; ! 3527: ignore_line_number = TRUE; ! 3528: warning ("MIPS ECOFF format does not allow changing filenames within functions with #line"); ! 3529: } ! 3530: ! 3531: fprintf (stream, "\t#.file\t%d \"%s\"\n", num_source_filenames, name); ! 3532: } ! 3533: ! 3534: else ! 3535: { ! 3536: SET_FILE_NUMBER (); ! 3537: current_function_file = name; ! 3538: fprintf (stream, "\t.file\t%d \"%s\"\n", num_source_filenames, name); ! 3539: } ! 3540: } ! 3541: } ! 3542: ! 3543: ! 3544: /* Emit a linenumber. For encapsulated stabs, we need to put out a stab ! 3545: as well as a .loc, since it is possible that MIPS ECOFF might not be ! 3546: able to represent the location for inlines that come from a different ! 3547: file. */ ! 3548: ! 3549: void ! 3550: mips_output_lineno (stream, line) ! 3551: FILE *stream; ! 3552: int line; ! 3553: { ! 3554: if (!TARGET_GAS && write_symbols == DBX_DEBUG) ! 3555: { ! 3556: ++sym_lineno; ! 3557: fprintf (stream, "$LM%d:\n\t%s %d,0,%d,$LM%d\n", ! 3558: sym_lineno, ASM_STABN_OP, N_SLINE, line, sym_lineno); ! 3559: } ! 3560: ! 3561: else ! 3562: { ! 3563: fprintf (stream, "\n\t%s.loc\t%d %d\n", ! 3564: (ignore_line_number) ? "#" : "", ! 3565: num_source_filenames, line); ! 3566: ! 3567: LABEL_AFTER_LOC (stream); ! 3568: } ! 3569: } ! 3570: ! 3571: ! 3572: /* If defined, a C statement to be executed just prior to the ! 3573: output of assembler code for INSN, to modify the extracted ! 3574: operands so they will be output differently. ! 3575: ! 3576: Here the argument OPVEC is the vector containing the operands ! 3577: extracted from INSN, and NOPERANDS is the number of elements of ! 3578: the vector which contain meaningful data for this insn. The ! 3579: contents of this vector are what will be used to convert the ! 3580: insn template into assembler code, so you can change the ! 3581: assembler output by changing the contents of the vector. ! 3582: ! 3583: We use it to check if the current insn needs a nop in front of it ! 3584: because of load delays, and also to update the delay slot ! 3585: statistics. */ ! 3586: ! 3587: void ! 3588: final_prescan_insn (insn, opvec, noperands) ! 3589: rtx insn; ! 3590: rtx opvec[]; ! 3591: int noperands; ! 3592: { ! 3593: if (dslots_number_nops > 0) ! 3594: { ! 3595: rtx pattern = PATTERN (insn); ! 3596: int length = get_attr_length (insn); ! 3597: ! 3598: /* Do we need to emit a NOP? */ ! 3599: if (length == 0 ! 3600: || (mips_load_reg != (rtx)0 && reg_mentioned_p (mips_load_reg, pattern)) ! 3601: || (mips_load_reg2 != (rtx)0 && reg_mentioned_p (mips_load_reg2, pattern)) ! 3602: || (mips_load_reg3 != (rtx)0 && reg_mentioned_p (mips_load_reg3, pattern)) ! 3603: || (mips_load_reg4 != (rtx)0 && reg_mentioned_p (mips_load_reg4, pattern))) ! 3604: fputs ((set_noreorder) ? "\tnop\n" : "\t#nop\n", asm_out_file); ! 3605: ! 3606: else ! 3607: dslots_load_filled++; ! 3608: ! 3609: while (--dslots_number_nops > 0) ! 3610: fputs ((set_noreorder) ? "\tnop\n" : "\t#nop\n", asm_out_file); ! 3611: ! 3612: mips_load_reg = (rtx)0; ! 3613: mips_load_reg2 = (rtx)0; ! 3614: mips_load_reg3 = (rtx)0; ! 3615: mips_load_reg4 = (rtx)0; ! 3616: ! 3617: if (set_noreorder && --set_noreorder == 0) ! 3618: fputs ("\t.set\treorder\n", asm_out_file); ! 3619: } ! 3620: ! 3621: if (TARGET_STATS) ! 3622: { ! 3623: enum rtx_code code = GET_CODE (insn); ! 3624: if (code == JUMP_INSN || code == CALL_INSN) ! 3625: dslots_jump_total++; ! 3626: } ! 3627: } ! 3628: ! 3629: ! 3630: /* Output at beginning of assembler file. ! 3631: If we are optimizing to use the global pointer, create a temporary ! 3632: file to hold all of the text stuff, and write it out to the end. ! 3633: This is needed because the MIPS assembler is evidently one pass, ! 3634: and if it hasn't seen the relevant .comm/.lcomm/.extern/.sdata ! 3635: declaration when the code is processed, it generates a two ! 3636: instruction sequence. */ ! 3637: ! 3638: void ! 3639: mips_asm_file_start (stream) ! 3640: FILE *stream; ! 3641: { ! 3642: ASM_OUTPUT_SOURCE_FILENAME (stream, main_input_filename); ! 3643: ! 3644: /* Versions of the MIPS assembler before 2.20 generate errors ! 3645: if a branch inside of a .set noreorder section jumps to a ! 3646: label outside of the .set noreorder section. Revision 2.20 ! 3647: just set nobopt silently rather than fixing the bug. */ ! 3648: ! 3649: if (TARGET_MIPS_AS && optimize && flag_delayed_branch) ! 3650: fprintf (stream, "\t.set\tnobopt\n"); ! 3651: ! 3652: /* Generate the pseudo ops that the Pyramid based System V.4 wants. */ ! 3653: if (TARGET_ABICALLS) ! 3654: fprintf (stream, "\t.abicalls\n"); ! 3655: ! 3656: if (TARGET_GP_OPT) ! 3657: { ! 3658: asm_out_data_file = stream; ! 3659: asm_out_text_file = make_temp_file (); ! 3660: } ! 3661: else ! 3662: asm_out_data_file = asm_out_text_file = stream; ! 3663: ! 3664: if (TARGET_NAME_REGS) ! 3665: fprintf (asm_out_file, "#include <regdef.h>\n"); ! 3666: ! 3667: print_options (stream); ! 3668: } ! 3669: ! 3670: ! 3671: /* If we are optimizing the global pointer, emit the text section now ! 3672: and any small externs which did not have .comm, etc that are ! 3673: needed. Also, give a warning if the data area is more than 32K and ! 3674: -pic because 3 instructions are needed to reference the data ! 3675: pointers. */ ! 3676: ! 3677: void ! 3678: mips_asm_file_end (file) ! 3679: FILE *file; ! 3680: { ! 3681: char buffer[8192]; ! 3682: tree name_tree; ! 3683: struct extern_list *p; ! 3684: int len; ! 3685: ! 3686: if (HALF_PIC_P ()) ! 3687: HALF_PIC_FINISH (file); ! 3688: ! 3689: if (TARGET_GP_OPT) ! 3690: { ! 3691: if (extern_head) ! 3692: fputs ("\n", file); ! 3693: ! 3694: for (p = extern_head; p != 0; p = p->next) ! 3695: { ! 3696: name_tree = get_identifier (p->name); ! 3697: ! 3698: /* Positively ensure only one .extern for any given symbol. */ ! 3699: if (! TREE_ASM_WRITTEN (name_tree)) ! 3700: { ! 3701: TREE_ASM_WRITTEN (name_tree) = 1; ! 3702: fputs ("\t.extern\t", file); ! 3703: assemble_name (file, p->name); ! 3704: fprintf (file, ", %d\n", p->size); ! 3705: } ! 3706: } ! 3707: ! 3708: fprintf (file, "\n\t.text\n"); ! 3709: rewind (asm_out_text_file); ! 3710: if (ferror (asm_out_text_file)) ! 3711: fatal_io_error (temp_filename); ! 3712: ! 3713: while ((len = fread (buffer, 1, sizeof (buffer), asm_out_text_file)) > 0) ! 3714: if (fwrite (buffer, 1, len, file) != len) ! 3715: pfatal_with_name (asm_file_name); ! 3716: ! 3717: if (len < 0) ! 3718: pfatal_with_name (temp_filename); ! 3719: ! 3720: if (fclose (asm_out_text_file) != 0) ! 3721: pfatal_with_name (temp_filename); ! 3722: } ! 3723: } ! 3724: ! 3725: ! 3726: /* Emit either a label, .comm, or .lcomm directive, and mark ! 3727: that the symbol is used, so that we don't emit an .extern ! 3728: for it in mips_asm_file_end. */ ! 3729: ! 3730: void ! 3731: mips_declare_object (stream, name, init_string, final_string, size) ! 3732: FILE *stream; ! 3733: char *name; ! 3734: char *init_string; ! 3735: char *final_string; ! 3736: int size; ! 3737: { ! 3738: fputs (init_string, stream); /* "", "\t.comm\t", or "\t.lcomm\t" */ ! 3739: assemble_name (stream, name); ! 3740: fprintf (stream, final_string, size); /* ":\n", ",%u\n", ",%u\n" */ ! 3741: ! 3742: if (TARGET_GP_OPT && mips_section_threshold != 0) ! 3743: { ! 3744: tree name_tree = get_identifier (name); ! 3745: TREE_ASM_WRITTEN (name_tree) = 1; ! 3746: } ! 3747: } ! 3748: ! 3749: ! 3750: /* Output a double precision value to the assembler. If both the ! 3751: host and target are IEEE, emit the values in hex. */ ! 3752: ! 3753: void ! 3754: mips_output_double (stream, value) ! 3755: FILE *stream; ! 3756: REAL_VALUE_TYPE value; ! 3757: { ! 3758: #ifdef REAL_VALUE_TO_TARGET_DOUBLE ! 3759: long value_long[2]; ! 3760: REAL_VALUE_TO_TARGET_DOUBLE (value, value_long); ! 3761: ! 3762: fprintf (stream, "\t.word\t0x%08lx\t\t# %.20g\n\t.word\t0x%08lx\n", ! 3763: value_long[0], value, value_long[1]); ! 3764: #else ! 3765: fprintf (stream, "\t.double\t%.20g\n", value); ! 3766: #endif ! 3767: } ! 3768: ! 3769: ! 3770: /* Output a single precision value to the assembler. If both the ! 3771: host and target are IEEE, emit the values in hex. */ ! 3772: ! 3773: void ! 3774: mips_output_float (stream, value) ! 3775: FILE *stream; ! 3776: REAL_VALUE_TYPE value; ! 3777: { ! 3778: #ifdef REAL_VALUE_TO_TARGET_SINGLE ! 3779: long value_long; ! 3780: REAL_VALUE_TO_TARGET_SINGLE (value, value_long); ! 3781: ! 3782: fprintf (stream, "\t.word\t0x%08lx\t\t# %.12g (float)\n", value_long, value); ! 3783: #else ! 3784: fprintf (stream, "\t.float\t%.12g\n", value); ! 3785: #endif ! 3786: } ! 3787: ! 3788: ! 3789: /* Return TRUE if any register used in the epilogue is used. This to insure ! 3790: any insn put into the epilogue delay slots is safe. */ ! 3791: ! 3792: int ! 3793: epilogue_reg_mentioned_p (insn) ! 3794: rtx insn; ! 3795: { ! 3796: register char *fmt; ! 3797: register int i; ! 3798: register enum rtx_code code; ! 3799: register int regno; ! 3800: ! 3801: if (insn == (rtx)0) ! 3802: return 0; ! 3803: ! 3804: if (GET_CODE (insn) == LABEL_REF) ! 3805: return 0; ! 3806: ! 3807: code = GET_CODE (insn); ! 3808: switch (code) ! 3809: { ! 3810: case REG: ! 3811: regno = REGNO (insn); ! 3812: if (regno == STACK_POINTER_REGNUM) ! 3813: return 1; ! 3814: ! 3815: if (regno == FRAME_POINTER_REGNUM && frame_pointer_needed) ! 3816: return 1; ! 3817: ! 3818: if (!call_used_regs[regno]) ! 3819: return 1; ! 3820: ! 3821: if (regno != MIPS_TEMP1_REGNUM && regno != MIPS_TEMP2_REGNUM) ! 3822: return 0; ! 3823: ! 3824: if (!current_frame_info.initialized) ! 3825: compute_frame_size (get_frame_size ()); ! 3826: ! 3827: return (current_frame_info.total_size >= 32768); ! 3828: ! 3829: case SCRATCH: ! 3830: case CC0: ! 3831: case PC: ! 3832: case CONST_INT: ! 3833: case CONST_DOUBLE: ! 3834: return 0; ! 3835: } ! 3836: ! 3837: fmt = GET_RTX_FORMAT (code); ! 3838: for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) ! 3839: { ! 3840: if (fmt[i] == 'E') ! 3841: { ! 3842: register int j; ! 3843: for (j = XVECLEN (insn, i) - 1; j >= 0; j--) ! 3844: if (epilogue_reg_mentioned_p (XVECEXP (insn, i, j))) ! 3845: return 1; ! 3846: } ! 3847: else if (fmt[i] == 'e' && epilogue_reg_mentioned_p (XEXP (insn, i))) ! 3848: return 1; ! 3849: } ! 3850: ! 3851: return 0; ! 3852: } ! 3853: ! 3854: ! 3855: /* Return the bytes needed to compute the frame pointer from the current ! 3856: stack pointer. ! 3857: ! 3858: Mips stack frames look like: ! 3859: ! 3860: Before call After call ! 3861: +-----------------------+ +-----------------------+ ! 3862: high | | | | ! 3863: mem. | | | | ! 3864: | caller's temps. | | caller's temps. | ! 3865: | | | | ! 3866: +-----------------------+ +-----------------------+ ! 3867: | | | | ! 3868: | arguments on stack. | | arguments on stack. | ! 3869: | | | | ! 3870: +-----------------------+ +-----------------------+ ! 3871: | 4 words to save | | 4 words to save | ! 3872: | arguments passed | | arguments passed | ! 3873: | in registers, even | | in registers, even | ! 3874: SP->| if not passed. | FP->| if not passed. | ! 3875: +-----------------------+ +-----------------------+ ! 3876: | | ! 3877: | GP save for V.4 abi | ! 3878: | | ! 3879: +-----------------------+ ! 3880: | | ! 3881: | fp register save | ! 3882: | | ! 3883: +-----------------------+ ! 3884: | | ! 3885: | gp register save | ! 3886: | | ! 3887: +-----------------------+ ! 3888: | | ! 3889: | local variables | ! 3890: | | ! 3891: +-----------------------+ ! 3892: | | ! 3893: | alloca allocations | ! 3894: | | ! 3895: +-----------------------+ ! 3896: | | ! 3897: | arguments on stack | ! 3898: | | ! 3899: +-----------------------+ ! 3900: | 4 words to save | ! 3901: | arguments passed | ! 3902: | in registers, even | ! 3903: low SP->| if not passed. | ! 3904: memory +-----------------------+ ! 3905: ! 3906: */ ! 3907: ! 3908: long ! 3909: compute_frame_size (size) ! 3910: int size; /* # of var. bytes allocated */ ! 3911: { ! 3912: int regno; ! 3913: long total_size; /* # bytes that the entire frame takes up */ ! 3914: long var_size; /* # bytes that variables take up */ ! 3915: long args_size; /* # bytes that outgoing arguments take up */ ! 3916: long extra_size; /* # extra bytes */ ! 3917: long gp_reg_rounded; /* # bytes needed to store gp after rounding */ ! 3918: long gp_reg_size; /* # bytes needed to store gp regs */ ! 3919: long fp_reg_size; /* # bytes needed to store fp regs */ ! 3920: long mask; /* mask of saved gp registers */ ! 3921: long fmask; /* mask of saved fp registers */ ! 3922: int fp_inc; /* 1 or 2 depending on the size of fp regs */ ! 3923: long fp_bits; /* bitmask to use for each fp register */ ! 3924: ! 3925: gp_reg_size = 0; ! 3926: fp_reg_size = 0; ! 3927: mask = 0; ! 3928: fmask = 0; ! 3929: extra_size = MIPS_STACK_ALIGN (((TARGET_ABICALLS) ? UNITS_PER_WORD : 0)); ! 3930: var_size = MIPS_STACK_ALIGN (size); ! 3931: args_size = MIPS_STACK_ALIGN (current_function_outgoing_args_size); ! 3932: ! 3933: /* The MIPS 3.0 linker does not like functions that dynamically ! 3934: allocate the stack and have 0 for STACK_DYNAMIC_OFFSET, since it ! 3935: looks like we are trying to create a second frame pointer to the ! 3936: function, so allocate some stack space to make it happy. */ ! 3937: ! 3938: if (args_size == 0 && current_function_calls_alloca) ! 3939: args_size = 4*UNITS_PER_WORD; ! 3940: ! 3941: total_size = var_size + args_size + extra_size; ! 3942: ! 3943: /* Calculate space needed for gp registers. */ ! 3944: for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++) ! 3945: { ! 3946: if (MUST_SAVE_REGISTER (regno)) ! 3947: { ! 3948: gp_reg_size += UNITS_PER_WORD; ! 3949: mask |= 1L << (regno - GP_REG_FIRST); ! 3950: } ! 3951: } ! 3952: ! 3953: /* Calculate space needed for fp registers. */ ! 3954: if (TARGET_FLOAT64) ! 3955: { ! 3956: fp_inc = 1; ! 3957: fp_bits = 1; ! 3958: } ! 3959: else ! 3960: { ! 3961: fp_inc = 2; ! 3962: fp_bits = 3; ! 3963: } ! 3964: ! 3965: for (regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno += fp_inc) ! 3966: { ! 3967: if (regs_ever_live[regno] && !call_used_regs[regno]) ! 3968: { ! 3969: fp_reg_size += 2*UNITS_PER_WORD; ! 3970: fmask |= fp_bits << (regno - FP_REG_FIRST); ! 3971: } ! 3972: } ! 3973: ! 3974: gp_reg_rounded = MIPS_STACK_ALIGN (gp_reg_size); ! 3975: total_size += gp_reg_rounded + fp_reg_size; ! 3976: ! 3977: if (total_size == extra_size) ! 3978: total_size = extra_size = 0; ! 3979: ! 3980: /* Save other computed information. */ ! 3981: current_frame_info.total_size = total_size; ! 3982: current_frame_info.var_size = var_size; ! 3983: current_frame_info.args_size = args_size; ! 3984: current_frame_info.extra_size = extra_size; ! 3985: current_frame_info.gp_reg_size = gp_reg_size; ! 3986: current_frame_info.fp_reg_size = fp_reg_size; ! 3987: current_frame_info.mask = mask; ! 3988: current_frame_info.fmask = fmask; ! 3989: current_frame_info.initialized = reload_completed; ! 3990: current_frame_info.num_gp = gp_reg_size / UNITS_PER_WORD; ! 3991: current_frame_info.num_fp = fp_reg_size / (2*UNITS_PER_WORD); ! 3992: ! 3993: if (mask) ! 3994: { ! 3995: unsigned long offset = args_size + var_size + gp_reg_size - UNITS_PER_WORD; ! 3996: current_frame_info.gp_sp_offset = offset; ! 3997: current_frame_info.gp_save_offset = offset - total_size; ! 3998: } ! 3999: else ! 4000: { ! 4001: current_frame_info.gp_sp_offset = 0; ! 4002: current_frame_info.gp_save_offset = 0; ! 4003: } ! 4004: ! 4005: ! 4006: if (fmask) ! 4007: { ! 4008: unsigned long offset = args_size + var_size + gp_reg_rounded + fp_reg_size - 2*UNITS_PER_WORD; ! 4009: current_frame_info.fp_sp_offset = offset; ! 4010: current_frame_info.fp_save_offset = offset - total_size + UNITS_PER_WORD; ! 4011: } ! 4012: else ! 4013: { ! 4014: current_frame_info.fp_sp_offset = 0; ! 4015: current_frame_info.fp_save_offset = 0; ! 4016: } ! 4017: ! 4018: /* Ok, we're done. */ ! 4019: return total_size; ! 4020: } ! 4021: ! 4022: ! 4023: /* Common code to emit the insns (or to write the instructions to a file) ! 4024: to save/restore registers. ! 4025: ! 4026: Other parts of the code assume that MIPS_TEMP1_REGNUM (aka large_reg) ! 4027: is not modified within save_restore_insns. */ ! 4028: ! 4029: #define BITSET_P(value,bit) (((value) & (1L << (bit))) != 0) ! 4030: ! 4031: static void ! 4032: save_restore_insns (store_p, large_reg, large_offset, file) ! 4033: int store_p; /* true if this is prologue */ ! 4034: rtx large_reg; /* register holding large offset constant or NULL */ ! 4035: long large_offset; /* large constant offset value */ ! 4036: FILE *file; /* file to write instructions to instead of making RTL */ ! 4037: { ! 4038: long mask = current_frame_info.mask; ! 4039: long fmask = current_frame_info.fmask; ! 4040: int regno; ! 4041: rtx base_reg_rtx; ! 4042: long base_offset; ! 4043: long gp_offset; ! 4044: long fp_offset; ! 4045: long end_offset; ! 4046: ! 4047: if (frame_pointer_needed && !BITSET_P (mask, FRAME_POINTER_REGNUM - GP_REG_FIRST)) ! 4048: abort (); ! 4049: ! 4050: if (mask == 0 && fmask == 0) ! 4051: return; ! 4052: ! 4053: /* Save registers starting from high to low. The debuggers prefer ! 4054: at least the return register be stored at func+4, and also it ! 4055: allows us not to need a nop in the epilog if at least one ! 4056: register is reloaded in addition to return address. */ ! 4057: ! 4058: /* Save GP registers if needed. */ ! 4059: if (mask) ! 4060: { ! 4061: /* Pick which pointer to use as a base register. For small ! 4062: frames, just use the stack pointer. Otherwise, use a ! 4063: temporary register. Save 2 cycles if the save area is near ! 4064: the end of a large frame, by reusing the constant created in ! 4065: the prologue/epilogue to adjust the stack frame. */ ! 4066: ! 4067: gp_offset = current_frame_info.gp_sp_offset; ! 4068: end_offset = gp_offset - (current_frame_info.gp_reg_size - UNITS_PER_WORD); ! 4069: ! 4070: if (gp_offset < 0 || end_offset < 0) ! 4071: fatal ("gp_offset (%ld) or end_offset (%ld) is less than zero.", ! 4072: gp_offset, end_offset); ! 4073: ! 4074: else if (gp_offset < 32768) ! 4075: { ! 4076: base_reg_rtx = stack_pointer_rtx; ! 4077: base_offset = 0; ! 4078: } ! 4079: ! 4080: else if (large_reg != (rtx)0 ! 4081: && (((unsigned long)(large_offset - gp_offset)) < 32768) ! 4082: && (((unsigned long)(large_offset - end_offset)) < 32768)) ! 4083: { ! 4084: base_reg_rtx = gen_rtx (REG, Pmode, MIPS_TEMP2_REGNUM); ! 4085: base_offset = large_offset; ! 4086: if (file == (FILE *)0) ! 4087: emit_insn (gen_addsi3 (base_reg_rtx, large_reg, stack_pointer_rtx)); ! 4088: else ! 4089: fprintf (file, "\taddu\t%s,%s,%s\n", ! 4090: reg_names[MIPS_TEMP2_REGNUM], ! 4091: reg_names[REGNO (large_reg)], ! 4092: reg_names[STACK_POINTER_REGNUM]); ! 4093: } ! 4094: ! 4095: else ! 4096: { ! 4097: base_reg_rtx = gen_rtx (REG, Pmode, MIPS_TEMP2_REGNUM); ! 4098: base_offset = gp_offset; ! 4099: if (file == (FILE *)0) ! 4100: { ! 4101: emit_move_insn (base_reg_rtx, GEN_INT (gp_offset)); ! 4102: emit_insn (gen_addsi3 (base_reg_rtx, base_reg_rtx, stack_pointer_rtx)); ! 4103: } ! 4104: else ! 4105: fprintf (file, "\tli\t%s,0x%.08lx\t# %ld\n\taddu\t%s,%s,%s\n", ! 4106: reg_names[MIPS_TEMP2_REGNUM], ! 4107: (long)base_offset, ! 4108: (long)base_offset, ! 4109: reg_names[MIPS_TEMP2_REGNUM], ! 4110: reg_names[MIPS_TEMP2_REGNUM], ! 4111: reg_names[STACK_POINTER_REGNUM]); ! 4112: } ! 4113: ! 4114: for (regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--) ! 4115: { ! 4116: if (BITSET_P (mask, regno - GP_REG_FIRST)) ! 4117: { ! 4118: if (file == (FILE *)0) ! 4119: { ! 4120: rtx reg_rtx = gen_rtx (REG, Pmode, regno); ! 4121: rtx mem_rtx = gen_rtx (MEM, Pmode, ! 4122: gen_rtx (PLUS, Pmode, base_reg_rtx, ! 4123: GEN_INT (gp_offset - base_offset))); ! 4124: ! 4125: if (store_p) ! 4126: emit_move_insn (mem_rtx, reg_rtx); ! 4127: else ! 4128: emit_move_insn (reg_rtx, mem_rtx); ! 4129: } ! 4130: else ! 4131: fprintf (file, "\t%s\t%s,%ld(%s)\n", ! 4132: (store_p) ? "sw" : "lw", ! 4133: reg_names[regno], ! 4134: gp_offset - base_offset, ! 4135: reg_names[REGNO(base_reg_rtx)]); ! 4136: ! 4137: gp_offset -= UNITS_PER_WORD; ! 4138: } ! 4139: } ! 4140: } ! 4141: else ! 4142: { ! 4143: base_reg_rtx = (rtx)0; /* Make sure these are initialzed */ ! 4144: base_offset = 0; ! 4145: } ! 4146: ! 4147: /* Save floating point registers if needed. */ ! 4148: if (fmask) ! 4149: { ! 4150: int fp_inc = (TARGET_FLOAT64) ? 1 : 2; ! 4151: ! 4152: /* Pick which pointer to use as a base register. */ ! 4153: fp_offset = current_frame_info.fp_sp_offset; ! 4154: end_offset = fp_offset - (current_frame_info.fp_reg_size - 2*UNITS_PER_WORD); ! 4155: ! 4156: if (fp_offset < 0 || end_offset < 0) ! 4157: fatal ("fp_offset (%ld) or end_offset (%ld) is less than zero.", ! 4158: fp_offset, end_offset); ! 4159: ! 4160: else if (fp_offset < 32768) ! 4161: { ! 4162: base_reg_rtx = stack_pointer_rtx; ! 4163: base_offset = 0; ! 4164: } ! 4165: ! 4166: else if (base_reg_rtx != (rtx)0 ! 4167: && (((unsigned long)(base_offset - fp_offset)) < 32768) ! 4168: && (((unsigned long)(base_offset - end_offset)) < 32768)) ! 4169: { ! 4170: ; /* already set up for gp registers above */ ! 4171: } ! 4172: ! 4173: else if (large_reg != (rtx)0 ! 4174: && (((unsigned long)(large_offset - fp_offset)) < 32768) ! 4175: && (((unsigned long)(large_offset - end_offset)) < 32768)) ! 4176: { ! 4177: base_reg_rtx = gen_rtx (REG, Pmode, MIPS_TEMP2_REGNUM); ! 4178: base_offset = large_offset; ! 4179: if (file == (FILE *)0) ! 4180: emit_insn (gen_addsi3 (base_reg_rtx, large_reg, stack_pointer_rtx)); ! 4181: else ! 4182: fprintf (file, "\taddu\t%s,%s,%s\n", ! 4183: reg_names[MIPS_TEMP2_REGNUM], ! 4184: reg_names[REGNO (large_reg)], ! 4185: reg_names[STACK_POINTER_REGNUM]); ! 4186: } ! 4187: ! 4188: else ! 4189: { ! 4190: base_reg_rtx = gen_rtx (REG, Pmode, MIPS_TEMP2_REGNUM); ! 4191: base_offset = fp_offset; ! 4192: if (file == (FILE *)0) ! 4193: { ! 4194: emit_move_insn (base_reg_rtx, GEN_INT (fp_offset)); ! 4195: emit_insn (gen_addsi3 (base_reg_rtx, base_reg_rtx, stack_pointer_rtx)); ! 4196: } ! 4197: else ! 4198: fprintf (file, "\tli\t%s,0x%.08lx\t# %ld\n\taddu\t%s,%s,%s\n", ! 4199: reg_names[MIPS_TEMP2_REGNUM], ! 4200: (long)base_offset, ! 4201: (long)base_offset, ! 4202: reg_names[MIPS_TEMP2_REGNUM], ! 4203: reg_names[MIPS_TEMP2_REGNUM], ! 4204: reg_names[STACK_POINTER_REGNUM]); ! 4205: } ! 4206: ! 4207: for (regno = FP_REG_LAST-1; regno >= FP_REG_FIRST; regno -= fp_inc) ! 4208: { ! 4209: if (BITSET_P (fmask, regno - FP_REG_FIRST)) ! 4210: { ! 4211: if (file == (FILE *)0) ! 4212: { ! 4213: rtx reg_rtx = gen_rtx (REG, DFmode, regno); ! 4214: rtx mem_rtx = gen_rtx (MEM, DFmode, ! 4215: gen_rtx (PLUS, Pmode, base_reg_rtx, ! 4216: GEN_INT (fp_offset - base_offset))); ! 4217: ! 4218: if (store_p) ! 4219: emit_move_insn (mem_rtx, reg_rtx); ! 4220: else ! 4221: emit_move_insn (reg_rtx, mem_rtx); ! 4222: } ! 4223: else ! 4224: fprintf (file, "\t%s\t%s,%ld(%s)\n", ! 4225: (store_p) ? "s.d" : "l.d", ! 4226: reg_names[regno], ! 4227: fp_offset - base_offset, ! 4228: reg_names[REGNO(base_reg_rtx)]); ! 4229: ! 4230: ! 4231: fp_offset -= 2*UNITS_PER_WORD; ! 4232: } ! 4233: } ! 4234: } ! 4235: } ! 4236: ! 4237: ! 4238: /* Set up the stack and frame (if desired) for the function. */ ! 4239: ! 4240: void ! 4241: function_prologue (file, size) ! 4242: FILE *file; ! 4243: int size; ! 4244: { ! 4245: long tsize = current_frame_info.total_size; ! 4246: ! 4247: ASM_OUTPUT_SOURCE_FILENAME (file, DECL_SOURCE_FILE (current_function_decl)); ! 4248: ! 4249: if (debug_info_level != DINFO_LEVEL_TERSE) ! 4250: ASM_OUTPUT_SOURCE_LINE (file, DECL_SOURCE_LINE (current_function_decl)); ! 4251: ! 4252: inside_function = 1; ! 4253: fputs ("\t.ent\t", file); ! 4254: assemble_name (file, current_function_name); ! 4255: fputs ("\n", file); ! 4256: ! 4257: assemble_name (file, current_function_name); ! 4258: fputs (":\n", file); ! 4259: ! 4260: if (TARGET_ABICALLS) ! 4261: fprintf (file, ! 4262: "\t.set\tnoreorder\n\t.cpload\t%s\n\t.set\treorder\n", ! 4263: reg_names[ GP_REG_FIRST + 25 ]); ! 4264: ! 4265: tsize = current_frame_info.total_size; ! 4266: if (tsize > 0 && TARGET_ABICALLS) ! 4267: fprintf (file, "\t.cprestore %d\n", tsize + STARTING_FRAME_OFFSET); ! 4268: ! 4269: fprintf (file, "\t.frame\t%s,%d,%s\t\t# vars= %d, regs= %d/%d, args = %d, extra= %d\n", ! 4270: reg_names[ (frame_pointer_needed) ? FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM ], ! 4271: tsize, ! 4272: reg_names[31 + GP_REG_FIRST], ! 4273: current_frame_info.var_size, ! 4274: current_frame_info.num_gp, ! 4275: current_frame_info.num_fp, ! 4276: current_function_outgoing_args_size, ! 4277: current_frame_info.extra_size); ! 4278: ! 4279: fprintf (file, "\t.mask\t0x%08lx,%d\n\t.fmask\t0x%08lx,%d\n", ! 4280: current_frame_info.mask, ! 4281: current_frame_info.gp_save_offset, ! 4282: current_frame_info.fmask, ! 4283: current_frame_info.fp_save_offset); ! 4284: } ! 4285: ! 4286: ! 4287: /* Expand the prologue into a bunch of separate insns. */ ! 4288: ! 4289: void ! 4290: mips_expand_prologue () ! 4291: { ! 4292: int regno; ! 4293: long tsize; ! 4294: rtx tmp_rtx = (rtx)0; ! 4295: char *arg_name = (char *)0; ! 4296: tree fndecl = current_function_decl; ! 4297: tree fntype = TREE_TYPE (fndecl); ! 4298: tree fnargs = (TREE_CODE (fntype) != METHOD_TYPE) ! 4299: ? DECL_ARGUMENTS (fndecl) ! 4300: : 0; ! 4301: rtx next_arg_reg; ! 4302: int i; ! 4303: tree next_arg; ! 4304: tree cur_arg; ! 4305: CUMULATIVE_ARGS args_so_far; ! 4306: ! 4307: /* Determine the last argument, and get its name. */ ! 4308: ! 4309: INIT_CUMULATIVE_ARGS (args_so_far, fntype, (rtx)0); ! 4310: regno = GP_ARG_FIRST; ! 4311: ! 4312: for (cur_arg = fnargs; cur_arg != (tree)0; cur_arg = next_arg) ! 4313: { ! 4314: tree type = DECL_ARG_TYPE (cur_arg); ! 4315: enum machine_mode passed_mode = TYPE_MODE (type); ! 4316: rtx entry_parm = FUNCTION_ARG (args_so_far, ! 4317: passed_mode, ! 4318: DECL_ARG_TYPE (cur_arg), ! 4319: 1); ! 4320: ! 4321: if (entry_parm) ! 4322: { ! 4323: int words; ! 4324: ! 4325: /* passed in a register, so will get homed automatically */ ! 4326: if (GET_MODE (entry_parm) == BLKmode) ! 4327: words = (int_size_in_bytes (type) + 3) / 4; ! 4328: else ! 4329: words = (GET_MODE_SIZE (GET_MODE (entry_parm)) + 3) / 4; ! 4330: ! 4331: regno = REGNO (entry_parm) + words - 1; ! 4332: } ! 4333: else ! 4334: { ! 4335: regno = GP_ARG_LAST+1; ! 4336: break; ! 4337: } ! 4338: ! 4339: FUNCTION_ARG_ADVANCE (args_so_far, ! 4340: passed_mode, ! 4341: DECL_ARG_TYPE (cur_arg), ! 4342: 1); ! 4343: ! 4344: next_arg = TREE_CHAIN (cur_arg); ! 4345: if (next_arg == (tree)0) ! 4346: { ! 4347: if (DECL_NAME (cur_arg)) ! 4348: arg_name = IDENTIFIER_POINTER (DECL_NAME (cur_arg)); ! 4349: ! 4350: break; ! 4351: } ! 4352: } ! 4353: ! 4354: /* In order to pass small structures by value in registers ! 4355: compatibly with the MIPS compiler, we need to shift the value ! 4356: into the high part of the register. Function_arg has encoded a ! 4357: PARALLEL rtx, holding a vector of adjustments to be made as the ! 4358: next_arg_reg variable, so we split up the insns, and emit them ! 4359: separately. */ ! 4360: ! 4361: next_arg_reg = FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1); ! 4362: if (next_arg_reg != (rtx)0 && GET_CODE (next_arg_reg) == PARALLEL) ! 4363: { ! 4364: rtvec adjust = XVEC (next_arg_reg, 0); ! 4365: int num = GET_NUM_ELEM (adjust); ! 4366: ! 4367: for (i = 0; i < num; i++) ! 4368: { ! 4369: rtx pattern = RTVEC_ELT (adjust, i); ! 4370: if (GET_CODE (pattern) != SET ! 4371: || GET_CODE (SET_SRC (pattern)) != ASHIFT) ! 4372: abort_with_insn (pattern, "Insn is not a shift"); ! 4373: ! 4374: PUT_CODE (SET_SRC (pattern), ASHIFTRT); ! 4375: emit_insn (pattern); ! 4376: } ! 4377: } ! 4378: ! 4379: /* If this function is a varargs function, store any registers that ! 4380: would normally hold arguments ($4 - $7) on the stack. */ ! 4381: if ((TYPE_ARG_TYPES (fntype) != 0 ! 4382: && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) != void_type_node)) ! 4383: || (arg_name != (char *)0 ! 4384: && ((arg_name[0] == '_' && strcmp (arg_name, "__builtin_va_alist") == 0) ! 4385: || (arg_name[0] == 'v' && strcmp (arg_name, "va_alist") == 0)))) ! 4386: { ! 4387: for (; regno <= GP_ARG_LAST; regno++) ! 4388: { ! 4389: rtx ptr = stack_pointer_rtx; ! 4390: if (regno != GP_ARG_FIRST) ! 4391: ptr = gen_rtx (PLUS, Pmode, ptr, ! 4392: GEN_INT ((regno - GP_ARG_FIRST) * UNITS_PER_WORD)); ! 4393: ! 4394: emit_move_insn (gen_rtx (MEM, Pmode, ptr), gen_rtx (REG, Pmode, regno)); ! 4395: } ! 4396: } ! 4397: ! 4398: tsize = compute_frame_size (get_frame_size ()); ! 4399: if (tsize > 0) ! 4400: { ! 4401: rtx tsize_rtx = GEN_INT (tsize); ! 4402: ! 4403: if (tsize > 32767) ! 4404: { ! 4405: tmp_rtx = gen_rtx (REG, SImode, MIPS_TEMP1_REGNUM); ! 4406: emit_move_insn (tmp_rtx, tsize_rtx); ! 4407: tsize_rtx = tmp_rtx; ! 4408: } ! 4409: ! 4410: emit_insn (gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx, tsize_rtx)); ! 4411: ! 4412: save_restore_insns (TRUE, tmp_rtx, tsize, (FILE *)0); ! 4413: ! 4414: if (frame_pointer_needed) ! 4415: emit_insn (gen_movsi (frame_pointer_rtx, stack_pointer_rtx)); ! 4416: } ! 4417: ! 4418: /* If we are profiling, make sure no instructions are scheduled before ! 4419: the call to mcount. */ ! 4420: ! 4421: if (profile_flag || profile_block_flag) ! 4422: emit_insn (gen_blockage ()); ! 4423: } ! 4424: ! 4425: ! 4426: /* Do any necessary cleanup after a function to restore stack, frame, and regs. */ ! 4427: ! 4428: #define RA_MASK ((long) 0x80000000) /* 1 << 31 */ ! 4429: ! 4430: void ! 4431: function_epilogue (file, size) ! 4432: FILE *file; ! 4433: int size; ! 4434: { ! 4435: long tsize; ! 4436: char *sp_str = reg_names[STACK_POINTER_REGNUM]; ! 4437: char *t1_str = reg_names[MIPS_TEMP1_REGNUM]; ! 4438: rtx epilogue_delay = current_function_epilogue_delay_list; ! 4439: int noreorder = !TARGET_MIPS_AS || (epilogue_delay != 0); ! 4440: int noepilogue = FALSE; ! 4441: int load_nop = FALSE; ! 4442: int load_only_r31; ! 4443: rtx tmp_rtx = (rtx)0; ! 4444: rtx restore_rtx; ! 4445: int i; ! 4446: ! 4447: /* The epilogue does not depend on any registers, but the stack ! 4448: registers, so we assume that if we have 1 pending nop, it can be ! 4449: ignored, and 2 it must be filled (2 nops occur for integer ! 4450: multiply and divide). */ ! 4451: ! 4452: if (dslots_number_nops > 0) ! 4453: { ! 4454: if (dslots_number_nops == 1) ! 4455: { ! 4456: dslots_number_nops = 0; ! 4457: dslots_load_filled++; ! 4458: } ! 4459: else ! 4460: { ! 4461: while (--dslots_number_nops > 0) ! 4462: fputs ((set_noreorder) ? "\tnop\n" : "\t#nop\n", asm_out_file); ! 4463: } ! 4464: ! 4465: if (set_noreorder > 0 && --set_noreorder == 0) ! 4466: fputs ("\t.set\treorder\n", file); ! 4467: } ! 4468: ! 4469: if (set_noat != 0) ! 4470: { ! 4471: set_noat = 0; ! 4472: fputs ("\t.set\tat\n", file); ! 4473: error ("internal gcc error: .set noat left on in epilogue"); ! 4474: } ! 4475: ! 4476: if (set_nomacro != 0) ! 4477: { ! 4478: set_nomacro = 0; ! 4479: fputs ("\t.set\tmacro\n", file); ! 4480: error ("internal gcc error: .set nomacro left on in epilogue"); ! 4481: } ! 4482: ! 4483: if (set_noreorder != 0) ! 4484: { ! 4485: set_noreorder = 0; ! 4486: fputs ("\t.set\treorder\n", file); ! 4487: error ("internal gcc error: .set noreorder left on in epilogue"); ! 4488: } ! 4489: ! 4490: if (set_volatile != 0) ! 4491: { ! 4492: set_volatile = 0; ! 4493: fprintf (file, "\t#.set\tnovolatile\n", (TARGET_MIPS_AS) ? "" : "#"); ! 4494: error ("internal gcc error: .set volatile left on in epilogue"); ! 4495: } ! 4496: ! 4497: size = MIPS_STACK_ALIGN (size); ! 4498: tsize = (!current_frame_info.initialized) ! 4499: ? compute_frame_size (size) ! 4500: : current_frame_info.total_size; ! 4501: ! 4502: if (tsize == 0 && epilogue_delay == 0) ! 4503: { ! 4504: rtx insn = get_last_insn (); ! 4505: ! 4506: /* If the last insn was a BARRIER, we don't have to write any code ! 4507: because a jump (aka return) was put there. */ ! 4508: if (GET_CODE (insn) == NOTE) ! 4509: insn = prev_nonnote_insn (insn); ! 4510: if (insn && GET_CODE (insn) == BARRIER) ! 4511: noepilogue = TRUE; ! 4512: ! 4513: noreorder = FALSE; ! 4514: } ! 4515: ! 4516: if (!noepilogue) ! 4517: { ! 4518: /* In the reload sequence, we don't need to fill the load delay ! 4519: slots for most of the loads, also see if we can fill the final ! 4520: delay slot if not otherwise filled by the reload sequence. */ ! 4521: ! 4522: if (noreorder) ! 4523: fprintf (file, "\t.set\tnoreorder\n"); ! 4524: ! 4525: if (tsize > 32767) ! 4526: { ! 4527: fprintf (file, "\tli\t%s,0x%.08lx\t# %ld\n", t1_str, (long)tsize, (long)tsize); ! 4528: tmp_rtx = gen_rtx (REG, Pmode, MIPS_TEMP1_REGNUM); ! 4529: } ! 4530: ! 4531: if (frame_pointer_needed) ! 4532: fprintf (file, "\tmove\t%s,%s\t\t\t# sp not trusted here\n", ! 4533: sp_str, reg_names[FRAME_POINTER_REGNUM]); ! 4534: ! 4535: save_restore_insns (FALSE, tmp_rtx, tsize, file); ! 4536: ! 4537: load_only_r31 = (current_frame_info.mask == RA_MASK ! 4538: && current_frame_info.fmask == 0); ! 4539: ! 4540: if (noreorder) ! 4541: { ! 4542: /* If the only register saved is the return address, we need a ! 4543: nop, unless we have an instruction to put into it. Otherwise ! 4544: we don't since reloading multiple registers doesn't reference ! 4545: the register being loaded. */ ! 4546: ! 4547: if (load_only_r31) ! 4548: { ! 4549: if (epilogue_delay) ! 4550: final_scan_insn (XEXP (epilogue_delay, 0), ! 4551: file, ! 4552: 1, /* optimize */ ! 4553: -2, /* prescan */ ! 4554: 1); /* nopeepholes */ ! 4555: else ! 4556: { ! 4557: fprintf (file, "\tnop\n"); ! 4558: load_nop = TRUE; ! 4559: } ! 4560: } ! 4561: ! 4562: fprintf (file, "\tj\t%s\n", reg_names[GP_REG_FIRST + 31]); ! 4563: ! 4564: if (tsize > 32767) ! 4565: fprintf (file, "\taddu\t%s,%s,%s\n", sp_str, sp_str, t1_str); ! 4566: ! 4567: else if (tsize > 0) ! 4568: fprintf (file, "\taddu\t%s,%s,%d\n", sp_str, sp_str, tsize); ! 4569: ! 4570: else if (!load_only_r31 && epilogue_delay != 0) ! 4571: final_scan_insn (XEXP (epilogue_delay, 0), ! 4572: file, ! 4573: 1, /* optimize */ ! 4574: -2, /* prescan */ ! 4575: 1); /* nopeepholes */ ! 4576: ! 4577: fprintf (file, "\t.set\treorder\n"); ! 4578: } ! 4579: ! 4580: else ! 4581: { ! 4582: if (tsize > 32767) ! 4583: fprintf (file, "\taddu\t%s,%s,%s\n", sp_str, sp_str, t1_str); ! 4584: ! 4585: else if (tsize > 0) ! 4586: fprintf (file, "\taddu\t%s,%s,%d\n", sp_str, sp_str, tsize); ! 4587: ! 4588: fprintf (file, "\tj\t%s\n", reg_names[GP_REG_FIRST + 31]); ! 4589: } ! 4590: } ! 4591: ! 4592: fputs ("\t.end\t", file); ! 4593: assemble_name (file, current_function_name); ! 4594: fputs ("\n", file); ! 4595: ! 4596: if (TARGET_STATS) ! 4597: { ! 4598: int num_gp_regs = current_frame_info.gp_reg_size / 4; ! 4599: int num_fp_regs = current_frame_info.fp_reg_size / 8; ! 4600: int num_regs = num_gp_regs + num_fp_regs; ! 4601: char *name = current_function_name; ! 4602: ! 4603: if (name[0] == '*') ! 4604: name++; ! 4605: ! 4606: dslots_load_total += num_regs; ! 4607: ! 4608: if (!noepilogue) ! 4609: dslots_jump_total++; ! 4610: ! 4611: if (noreorder) ! 4612: { ! 4613: dslots_load_filled += num_regs; ! 4614: ! 4615: /* If the only register saved is the return register, we ! 4616: can't fill this register's delay slot. */ ! 4617: ! 4618: if (load_only_r31 && epilogue_delay == 0) ! 4619: dslots_load_filled--; ! 4620: ! 4621: if (tsize > 0 || (!load_only_r31 && epilogue_delay != 0)) ! 4622: dslots_jump_filled++; ! 4623: } ! 4624: ! 4625: fprintf (stderr, ! 4626: "%-20s fp=%c leaf=%c alloca=%c setjmp=%c stack=%4ld arg=%3ld reg=%2d/%d delay=%3d/%3dL %3d/%3dJ refs=%3d/%3d/%3d", ! 4627: name, ! 4628: (frame_pointer_needed) ? 'y' : 'n', ! 4629: ((current_frame_info.mask & RA_MASK) != 0) ? 'n' : 'y', ! 4630: (current_function_calls_alloca) ? 'y' : 'n', ! 4631: (current_function_calls_setjmp) ? 'y' : 'n', ! 4632: (long)current_frame_info.total_size, ! 4633: (long)current_function_outgoing_args_size, ! 4634: num_gp_regs, num_fp_regs, ! 4635: dslots_load_total, dslots_load_filled, ! 4636: dslots_jump_total, dslots_jump_filled, ! 4637: num_refs[0], num_refs[1], num_refs[2]); ! 4638: ! 4639: if (HALF_PIC_NUMBER_PTRS > prev_half_pic_ptrs) ! 4640: { ! 4641: fprintf (stderr, " half-pic=%3d", HALF_PIC_NUMBER_PTRS - prev_half_pic_ptrs); ! 4642: prev_half_pic_ptrs = HALF_PIC_NUMBER_PTRS; ! 4643: } ! 4644: ! 4645: if (HALF_PIC_NUMBER_REFS > prev_half_pic_refs) ! 4646: { ! 4647: fprintf (stderr, " pic-ref=%3d", HALF_PIC_NUMBER_REFS - prev_half_pic_refs); ! 4648: prev_half_pic_refs = HALF_PIC_NUMBER_REFS; ! 4649: } ! 4650: ! 4651: fputc ('\n', stderr); ! 4652: } ! 4653: ! 4654: /* Reset state info for each function. */ ! 4655: inside_function = FALSE; ! 4656: ignore_line_number = FALSE; ! 4657: dslots_load_total = 0; ! 4658: dslots_jump_total = 0; ! 4659: dslots_load_filled = 0; ! 4660: dslots_jump_filled = 0; ! 4661: num_refs[0] = 0; ! 4662: num_refs[1] = 0; ! 4663: num_refs[2] = 0; ! 4664: mips_load_reg = (rtx)0; ! 4665: mips_load_reg2 = (rtx)0; ! 4666: current_frame_info = zero_frame_info; ! 4667: ! 4668: /* Restore the output file if optimizing the GP (optimizing the GP causes ! 4669: the text to be diverted to a tempfile, so that data decls come before ! 4670: references to the data). */ ! 4671: ! 4672: if (TARGET_GP_OPT) ! 4673: asm_out_file = asm_out_data_file; ! 4674: } ! 4675: ! 4676: ! 4677: /* Expand the epilogue into a bunch of separate insns. */ ! 4678: ! 4679: void ! 4680: mips_expand_epilogue () ! 4681: { ! 4682: long tsize = current_frame_info.total_size; ! 4683: rtx tsize_rtx = GEN_INT (tsize); ! 4684: rtx tmp_rtx = (rtx)0; ! 4685: ! 4686: if (tsize > 32767) ! 4687: { ! 4688: tmp_rtx = gen_rtx (REG, SImode, MIPS_TEMP1_REGNUM); ! 4689: emit_move_insn (tmp_rtx, tsize_rtx); ! 4690: tsize_rtx = tmp_rtx; ! 4691: } ! 4692: ! 4693: if (tsize > 0) ! 4694: { ! 4695: if (frame_pointer_needed) ! 4696: emit_insn (gen_movsi (stack_pointer_rtx, frame_pointer_rtx)); ! 4697: ! 4698: save_restore_insns (FALSE, tmp_rtx, tsize, (FILE *)0); ! 4699: ! 4700: emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, tsize_rtx)); ! 4701: } ! 4702: ! 4703: emit_jump_insn (gen_return_internal (gen_rtx (REG, Pmode, GP_REG_FIRST+31))); ! 4704: } ! 4705: ! 4706: ! 4707: /* Define the number of delay slots needed for the function epilogue. ! 4708: ! 4709: On the mips, we need a slot if either no stack has been allocated, ! 4710: or the only register saved is the return register. */ ! 4711: ! 4712: int ! 4713: mips_epilogue_delay_slots () ! 4714: { ! 4715: if (!current_frame_info.initialized) ! 4716: (void) compute_frame_size (get_frame_size ()); ! 4717: ! 4718: if (current_frame_info.total_size == 0) ! 4719: return 1; ! 4720: ! 4721: if (current_frame_info.mask == RA_MASK && current_frame_info.fmask == 0) ! 4722: return 1; ! 4723: ! 4724: return 0; ! 4725: } ! 4726: ! 4727: ! 4728: /* Return true if this function is known to have a null epilogue. ! 4729: This allows the optimizer to omit jumps to jumps if no stack ! 4730: was created. */ ! 4731: ! 4732: int ! 4733: simple_epilogue_p () ! 4734: { ! 4735: if (!reload_completed) ! 4736: return 0; ! 4737: ! 4738: if (current_frame_info.initialized) ! 4739: return current_frame_info.total_size == 0; ! 4740: ! 4741: return (compute_frame_size (get_frame_size ())) == 0; ! 4742: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.