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