|
|
1.1 ! root 1: /* Subroutines for insn-output.c for Motorola 88000. ! 2: Copyright (C) 1988, 1989, 1990, 1991 Free Software Foundation, Inc. ! 3: Contributed by Michael Tiemann ([email protected]) ! 4: Enhanced by Michael Meissner ([email protected]) ! 5: Currently supported by Tom Wood ([email protected]) ! 6: ! 7: This file is part of GNU CC. ! 8: ! 9: GNU CC is free software; you can redistribute it and/or modify ! 10: it under the terms of the GNU General Public License as published by ! 11: the Free Software Foundation; either version 2, or (at your option) ! 12: any later version. ! 13: ! 14: GNU CC is distributed in the hope that it will be useful, ! 15: but WITHOUT ANY WARRANTY; without even the implied warranty of ! 16: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ! 17: GNU General Public License for more details. ! 18: ! 19: You should have received a copy of the GNU General Public License ! 20: along with GNU CC; see the file COPYING. If not, write to ! 21: the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ ! 22: ! 23: #include <stdio.h> ! 24: #include <sys/types.h> ! 25: #include <time.h> ! 26: #include <ctype.h> ! 27: ! 28: #include "config.h" ! 29: #include "rtl.h" ! 30: #include "regs.h" ! 31: #include "hard-reg-set.h" ! 32: #include "real.h" ! 33: #include "insn-config.h" ! 34: #include "conditions.h" ! 35: #include "insn-flags.h" ! 36: #include "output.h" ! 37: #include "insn-attr.h" ! 38: #include "tree.h" ! 39: #include "c-tree.h" ! 40: #include "expr.h" ! 41: #include "hard-reg-set.h" ! 42: #include "flags.h" ! 43: ! 44: extern char *version_string; ! 45: extern time_t time (); ! 46: extern char *ctime (); ! 47: extern int flag_traditional; ! 48: extern FILE *asm_out_file; ! 49: ! 50: static char out_sccs_id[] = "@(#)m88k.c 1.96.5.2 06 Feb 1992 10:02:20"; ! 51: static char tm_sccs_id [] = TM_SCCS_ID; ! 52: ! 53: char *m88k_pound_sign = ""; /* Either # for SVR4 or empty for SVR3 */ ! 54: char *m88k_short_data; ! 55: ! 56: int m88k_gp_threshold; ! 57: int m88k_prologue_done = 0; /* Ln directives can now be emitted */ ! 58: int m88k_function_number = 0; /* Counter unique to each function */ ! 59: int m88k_fp_offset = 0; /* offset of frame pointer if used */ ! 60: int m88k_stack_size = 0; /* size of allocated stack (including frame) */ ! 61: int m88k_case_index; ! 62: ! 63: rtx m88k_compare_reg; /* cmp output pseudo register */ ! 64: rtx m88k_compare_op0; /* cmpsi operand 0 */ ! 65: rtx m88k_compare_op1; /* cmpsi operand 1 */ ! 66: ! 67: /* Determine what instructions are needed to manufacture the integer VALUE ! 68: in the given MODE. */ ! 69: ! 70: enum m88k_instruction ! 71: classify_integer (mode, value) ! 72: enum machine_mode mode; ! 73: register int value; ! 74: { ! 75: register int mask; ! 76: ! 77: if (value == 0) ! 78: return m88k_zero; ! 79: else if (SMALL_INTVAL (value)) ! 80: return m88k_or; ! 81: else if (SMALL_INTVAL (-value)) ! 82: return m88k_subu; ! 83: else if (mode == HImode) ! 84: return m88k_or_lo16; ! 85: else if (mode == QImode) ! 86: return m88k_or_lo8; ! 87: else if ((value & 0xffff) == 0) ! 88: return m88k_oru_hi16; ! 89: else if (integer_ok_for_set (value)) ! 90: return m88k_set; ! 91: else ! 92: return m88k_oru_or; ! 93: } ! 94: ! 95: int ! 96: integer_ok_for_set (value) ! 97: register unsigned value; ! 98: { ! 99: /* All the "one" bits must be contiguous. If so, MASK + 1 will be ! 100: a power of two or zero. */ ! 101: register unsigned mask = (value | (value - 1)); ! 102: return (value && POWER_OF_2_or_0 (mask + 1)); ! 103: } ! 104: ! 105: char * ! 106: output_load_const_int (mode, operands) ! 107: enum machine_mode mode; ! 108: rtx *operands; ! 109: { ! 110: static char *patterns[] = ! 111: { "or %0,%#r0,0", ! 112: "or %0,%#r0,%1", ! 113: "subu %0,%#r0,%n1", ! 114: "or %0,%#r0,%h1", ! 115: "or %0,%#r0,%q1", ! 116: "set %0,%#r0,%s1", ! 117: "or.u %0,%#r0,%X1", ! 118: "or.u %0,%#r0,%X1\n\tor %0,%0,%x1", ! 119: }; ! 120: ! 121: if (! REG_P (operands[0]) ! 122: || GET_CODE (operands[1]) != CONST_INT) ! 123: abort (); ! 124: return patterns[classify_integer (mode, INTVAL (operands[1]))]; ! 125: } ! 126: ! 127: /* These next two routines assume that floating point numbers are represented ! 128: in a manner which is consistent between host and target machines. */ ! 129: ! 130: char * ! 131: output_load_const_float (operands) ! 132: rtx *operands; ! 133: { ! 134: /* These can return 0 under some circumstances when cross-compiling. */ ! 135: operands[0] = operand_subword (operands[0], 0, 0, SFmode); ! 136: operands[1] = operand_subword (operands[1], 0, 0, SFmode); ! 137: ! 138: return output_load_const_int (SImode, operands); ! 139: } ! 140: ! 141: char * ! 142: output_load_const_double (operands) ! 143: rtx *operands; ! 144: { ! 145: rtx latehalf[2]; ! 146: ! 147: /* These can return zero on some cross-compilers, but there's nothing ! 148: we can do about it. */ ! 149: latehalf[0] = operand_subword (operands[0], 1, 0, DFmode); ! 150: latehalf[1] = operand_subword (operands[1], 1, 0, DFmode); ! 151: ! 152: operands[0] = operand_subword (operands[0], 0, 0, DFmode); ! 153: operands[1] = operand_subword (operands[1], 0, 0, DFmode); ! 154: ! 155: output_asm_insn (output_load_const_int (SImode, operands), operands); ! 156: ! 157: operands[0] = latehalf[0]; ! 158: operands[1] = latehalf[1]; ! 159: ! 160: return output_load_const_int (SImode, operands); ! 161: } ! 162: ! 163: char * ! 164: output_load_const_dimode (operands) ! 165: rtx *operands; ! 166: { ! 167: rtx latehalf[2]; ! 168: ! 169: latehalf[0] = operand_subword (operands[0], 1, 0, DImode); ! 170: latehalf[1] = operand_subword (operands[1], 1, 0, DImode); ! 171: ! 172: operands[0] = operand_subword (operands[0], 0, 0, DImode); ! 173: operands[1] = operand_subword (operands[1], 0, 0, DImode); ! 174: ! 175: output_asm_insn (output_load_const_int (SImode, operands), operands); ! 176: ! 177: operands[0] = latehalf[0]; ! 178: operands[1] = latehalf[1]; ! 179: ! 180: return output_load_const_int (SImode, operands); ! 181: } ! 182: ! 183: /* Emit insns to move operands[1] into operands[0]. ! 184: ! 185: Return 1 if we have written out everything that needs to be done to ! 186: do the move. Otherwise, return 0 and the caller will emit the move ! 187: normally. */ ! 188: ! 189: int ! 190: emit_move_sequence (operands, mode) ! 191: rtx *operands; ! 192: enum machine_mode mode; ! 193: { ! 194: register rtx operand0 = operands[0]; ! 195: register rtx operand1 = operands[1]; ! 196: ! 197: /* Handle most common case first: storing into a register. */ ! 198: if (register_operand (operand0, mode)) ! 199: { ! 200: if (register_operand (operand1, mode) ! 201: || (GET_CODE (operand1) == CONST_INT && SMALL_INT (operand1)) ! 202: || GET_CODE (operand1) == HIGH ! 203: /* Only `general_operands' can come here, so MEM is ok. */ ! 204: || GET_CODE (operand1) == MEM) ! 205: { ! 206: /* Run this case quickly. */ ! 207: emit_insn (gen_rtx (SET, VOIDmode, operand0, operand1)); ! 208: return 1; ! 209: } ! 210: } ! 211: else if (GET_CODE (operand0) == MEM) ! 212: { ! 213: if (register_operand (operand1, mode) || operand1 == const0_rtx) ! 214: { ! 215: /* Run this case quickly. */ ! 216: emit_insn (gen_rtx (SET, VOIDmode, operand0, operand1)); ! 217: return 1; ! 218: } ! 219: if (! reload_in_progress) ! 220: { ! 221: operands[0] = validize_mem (operand0); ! 222: operands[1] = operand1 = force_reg (mode, operand1); ! 223: } ! 224: } ! 225: ! 226: /* Simplify the source if we need to. */ ! 227: if (GET_CODE (operand1) != HIGH && immediate_operand (operand1, mode)) ! 228: { ! 229: if (GET_CODE (operand1) != CONST_INT ! 230: && GET_CODE (operand1) != CONST_DOUBLE) ! 231: { ! 232: rtx temp = reload_in_progress ? operand0 : gen_reg_rtx (Pmode); ! 233: operands[1] = legitimize_address (flag_pic ! 234: && symbolic_address_p (operand1), ! 235: operand1, temp); ! 236: if (mode != SImode) ! 237: operands[1] = gen_rtx (SUBREG, mode, operands[1], 0); ! 238: } ! 239: } ! 240: ! 241: /* Now have insn-emit do whatever it normally does. */ ! 242: return 0; ! 243: } ! 244: ! 245: /* Return a legitimate reference for ORIG (either an address or a MEM) using ! 246: the register REG. If PIC and the address is already position-independent, ! 247: use ORIG. */ ! 248: ! 249: struct rtx_def * ! 250: legitimize_address (pic, orig, reg) ! 251: int pic; ! 252: rtx orig; ! 253: rtx reg; ! 254: { ! 255: rtx addr = (GET_CODE (orig) == MEM ? XEXP (orig, 0) : orig); ! 256: rtx new = orig; ! 257: rtx temp; ! 258: ! 259: if (pic) ! 260: { ! 261: if (GET_CODE (addr) == SYMBOL_REF ! 262: || GET_CODE (addr) == LABEL_REF) ! 263: { ! 264: if (reg == 0) abort (); ! 265: ! 266: if (flag_pic == 2) ! 267: { ! 268: emit_insn (gen_rtx (SET, VOIDmode, ! 269: reg, gen_rtx (HIGH, SImode, addr))); ! 270: emit_insn (gen_rtx (SET, VOIDmode, ! 271: reg, gen_rtx (LO_SUM, SImode, reg, addr))); ! 272: addr = reg; ! 273: } ! 274: new = gen_rtx (MEM, Pmode, ! 275: gen_rtx (PLUS, SImode, ! 276: pic_offset_table_rtx, addr)); ! 277: current_function_uses_pic_offset_table = 1; ! 278: RTX_UNCHANGING_P (new) = 1; ! 279: { ! 280: rtx insn = emit_move_insn (reg, new); ! 281: /* Put a REG_EQUAL note on this insn, so that it can be optimized ! 282: by loop. */ ! 283: REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_EQUAL, orig, ! 284: REG_NOTES (insn)); ! 285: } ! 286: new = reg; ! 287: } ! 288: else if (GET_CODE (addr) == CONST) ! 289: { ! 290: rtx base, offset; ! 291: ! 292: if (GET_CODE (XEXP (addr, 0)) == PLUS ! 293: && XEXP (XEXP (addr, 0), 0) == pic_offset_table_rtx) ! 294: return orig; ! 295: ! 296: if (reg == 0) ! 297: abort (); ! 298: ! 299: if (GET_CODE (XEXP (addr, 0)) != PLUS) abort (); ! 300: ! 301: base = legitimize_address (1, XEXP (XEXP (addr, 0), 0), reg); ! 302: addr = legitimize_address (1, XEXP (XEXP (addr, 0), 1), ! 303: base == reg ? 0 : reg); ! 304: ! 305: if (GET_CODE (addr) == CONST_INT) ! 306: new = plus_constant_for_output (base, INTVAL (addr)); ! 307: else ! 308: new = gen_rtx (PLUS, SImode, base, addr); ! 309: /* Should we set special REG_NOTEs here? */ ! 310: } ! 311: } ! 312: else if (! SHORT_ADDRESS_P (addr, temp)) ! 313: { ! 314: emit_insn (gen_rtx (SET, VOIDmode, ! 315: reg, gen_rtx (HIGH, SImode, addr))); ! 316: new = gen_rtx (LO_SUM, SImode, reg, addr); ! 317: } ! 318: ! 319: if (new != orig ! 320: && GET_CODE (orig) == MEM) ! 321: { ! 322: new = gen_rtx (MEM, GET_MODE (orig), new); ! 323: RTX_UNCHANGING_P (new) = RTX_UNCHANGING_P (orig); ! 324: MEM_VOLATILE_P (new) = MEM_VOLATILE_P (orig); ! 325: MEM_IN_STRUCT_P (new) = MEM_IN_STRUCT_P (orig); ! 326: } ! 327: return new; ! 328: } ! 329: ! 330: /* Support functions for code to emit a block move. There are four methods ! 331: used to perform the block move: ! 332: + call memcpy ! 333: + call the looping library function, e.g. __movstrSI64n8 ! 334: + call a non-looping library function, e.g. __movstrHI15x11 ! 335: + produce an inline sequence of ld/st instructions ! 336: ! 337: The parameters below describe the library functions produced by ! 338: movstr-m88k.sh. */ ! 339: ! 340: #define MOVSTR_LOOP 64 /* __movstrSI64n68 .. __movstrSI64n8 */ ! 341: #define MOVSTR_QI 16 /* __movstrQI16x16 .. __movstrQI16x2 */ ! 342: #define MOVSTR_HI 48 /* __movstrHI48x48 .. __movstrHI48x4 */ ! 343: #define MOVSTR_SI 96 /* __movstrSI96x96 .. __movstrSI96x8 */ ! 344: #define MOVSTR_ODD_SI 48 /* __movstrSI47x47 .. __movstrSI47x11, ! 345: __movstrSI46x46 .. __movstrSI46x10, ! 346: __movstrSI45x45 .. __movstrSI45x9 */ ! 347: #define MOVSTR_ODD_HI 16 /* __movstrHI15x15 .. __movstrHI15x5 */ ! 348: ! 349: /* Break even points where memcpy will do just as well. */ ! 350: #define MOVSTR_QI_LIMIT 13 ! 351: #define MOVSTR_HI_LIMIT 38 ! 352: #define MOVSTR_SI_LIMIT MOVSTR_SI ! 353: ! 354: static enum machine_mode mode_from_bytes[] = ! 355: {VOIDmode, QImode, HImode, VOIDmode, SImode}; ! 356: static int max_from_bytes[] = {0, MOVSTR_QI, MOVSTR_HI, 0, MOVSTR_SI}; ! 357: static int all_from_bytes[] = {0, MOVSTR_QI, MOVSTR_ODD_HI, 0, MOVSTR_ODD_SI}; ! 358: static int best_from_bytes[] = ! 359: {0, MOVSTR_QI_LIMIT, MOVSTR_HI_LIMIT, 0, MOVSTR_SI_LIMIT}; ! 360: ! 361: static void block_move_loop (); ! 362: static void block_move_no_loop (); ! 363: static void block_move_sequence (); ! 364: ! 365: /* Emit code to perform a block move. Choose the best method. ! 366: ! 367: OPERANDS[0] is the destination. ! 368: OPERANDS[1] is the source. ! 369: OPERANDS[2] is the size. ! 370: OPERANDS[3] is the alignment safe to use. */ ! 371: ! 372: void ! 373: expand_block_move (dest_mem, src_mem, operands) ! 374: rtx dest_mem; ! 375: rtx src_mem; ! 376: rtx *operands; ! 377: { ! 378: int align = INTVAL (operands[3]); ! 379: int constp = (GET_CODE (operands[2]) == CONST_INT); ! 380: int bytes = (constp ? INTVAL (operands[2]) : 0); ! 381: ! 382: if (constp && bytes <= 0) ! 383: return; ! 384: ! 385: /* Determine machine mode to do move with. */ ! 386: if (align > 4) ! 387: align = 4; ! 388: else if (align <= 0 || align == 3) ! 389: abort (); /* block move invalid alignment. */ ! 390: ! 391: if (constp && bytes <= 3 * align) ! 392: block_move_sequence (operands[0], dest_mem, operands[1], src_mem, ! 393: bytes, align, 0); ! 394: ! 395: else if (constp && bytes <= best_from_bytes[align]) ! 396: block_move_no_loop (operands[0], dest_mem, operands[1], src_mem, ! 397: bytes, align); ! 398: ! 399: else if (constp && align == 4) ! 400: block_move_loop (operands[0], dest_mem, operands[1], src_mem, ! 401: bytes, align); ! 402: ! 403: else ! 404: { ! 405: #ifdef TARGET_MEM_FUNCTIONS ! 406: emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "memcpy"), 0, ! 407: VOIDmode, 3, ! 408: operands[0], Pmode, ! 409: operands[1], Pmode, ! 410: operands[2], SImode); ! 411: #else ! 412: emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "bcopy"), 0, ! 413: VOIDmode, 3, ! 414: operands[1], Pmode, ! 415: operands[0], Pmode, ! 416: operands[2], SImode); ! 417: #endif ! 418: } ! 419: } ! 420: ! 421: /* Emit code to perform a block move by calling a looping movstr library ! 422: function. SIZE and ALIGN are known constants. DEST and SRC are ! 423: registers. */ ! 424: ! 425: static void ! 426: block_move_loop (dest, dest_mem, src, src_mem, size, align) ! 427: rtx dest, dest_mem; ! 428: rtx src, src_mem; ! 429: int size; ! 430: int align; ! 431: { ! 432: enum machine_mode mode; ! 433: int count; ! 434: int units; ! 435: int remainder; ! 436: rtx offset_rtx; ! 437: rtx value_rtx; ! 438: char entry[30]; ! 439: tree entry_name; ! 440: ! 441: /* Determine machine mode to do move with. */ ! 442: if (align != 4) ! 443: abort (); ! 444: ! 445: /* Determine the structure of the loop. */ ! 446: count = size / MOVSTR_LOOP; ! 447: units = (size - count * MOVSTR_LOOP) / align; ! 448: ! 449: if (units < 2) ! 450: { ! 451: count--; ! 452: units += MOVSTR_LOOP / align; ! 453: } ! 454: ! 455: if (count <= 0) ! 456: { ! 457: block_move_no_loop (dest, dest_mem, src, src_mem, size, align); ! 458: return; ! 459: } ! 460: ! 461: remainder = size - count * MOVSTR_LOOP - units * align; ! 462: ! 463: mode = mode_from_bytes[align]; ! 464: sprintf (entry, "__movstr%s%dn%d", ! 465: GET_MODE_NAME (mode), MOVSTR_LOOP, units * align); ! 466: entry_name = get_identifier (entry); ! 467: ! 468: offset_rtx = gen_rtx (CONST_INT, VOIDmode, ! 469: MOVSTR_LOOP + (1 - units) * align); ! 470: ! 471: value_rtx = gen_rtx (MEM, mode, ! 472: gen_rtx (PLUS, Pmode, ! 473: gen_rtx (REG, Pmode, 3), ! 474: offset_rtx)); ! 475: RTX_UNCHANGING_P (value_rtx) = RTX_UNCHANGING_P (src_mem); ! 476: MEM_VOLATILE_P (value_rtx) = MEM_VOLATILE_P (src_mem); ! 477: MEM_IN_STRUCT_P (value_rtx) = MEM_IN_STRUCT_P (src_mem); ! 478: ! 479: emit_insn (gen_call_block_move_loop ! 480: (gen_rtx (SYMBOL_REF, Pmode, IDENTIFIER_POINTER (entry_name)), ! 481: dest, src, offset_rtx, value_rtx, ! 482: gen_rtx (REG, GET_MODE (value_rtx), ((units & 1) ? 4 : 5)), ! 483: gen_rtx (CONST_INT, VOIDmode, count))); ! 484: ! 485: if (remainder) ! 486: block_move_sequence (gen_rtx (REG, Pmode, 2), dest_mem, ! 487: gen_rtx (REG, Pmode, 3), src_mem, ! 488: remainder, align, MOVSTR_LOOP + align); ! 489: } ! 490: ! 491: /* Emit code to perform a block move by calling a non-looping library ! 492: function. SIZE and ALIGN are known constants. DEST and SRC are ! 493: registers. OFFSET is the known starting point for the output pattern. */ ! 494: ! 495: static void ! 496: block_move_no_loop (dest, dest_mem, src, src_mem, size, align) ! 497: rtx dest, dest_mem; ! 498: rtx src, src_mem; ! 499: int size; ! 500: int align; ! 501: { ! 502: enum machine_mode mode = mode_from_bytes[align]; ! 503: int units = size / align; ! 504: int remainder = size - units * align; ! 505: int most; ! 506: int evenp; ! 507: rtx offset_rtx; ! 508: rtx value_rtx; ! 509: char entry[30]; ! 510: tree entry_name; ! 511: ! 512: if (remainder && size <= all_from_bytes[align]) ! 513: { ! 514: most = all_from_bytes[align] - (align - remainder); ! 515: remainder = 0; ! 516: } ! 517: else ! 518: { ! 519: most = max_from_bytes[align]; ! 520: } ! 521: ! 522: sprintf (entry, "__movstr%s%dx%d", ! 523: GET_MODE_NAME (mode), most, size - remainder); ! 524: entry_name = get_identifier (entry); ! 525: ! 526: offset_rtx = gen_rtx (CONST_INT, VOIDmode, most - (size - remainder)); ! 527: ! 528: value_rtx = gen_rtx (MEM, mode, ! 529: gen_rtx (PLUS, Pmode, ! 530: gen_rtx (REG, Pmode, 3), ! 531: offset_rtx)); ! 532: RTX_UNCHANGING_P (value_rtx) = RTX_UNCHANGING_P (src_mem); ! 533: MEM_VOLATILE_P (value_rtx) = MEM_VOLATILE_P (src_mem); ! 534: MEM_IN_STRUCT_P (value_rtx) = MEM_IN_STRUCT_P (src_mem); ! 535: ! 536: evenp = ((most - (size - remainder)) / align) & 1; ! 537: ! 538: emit_insn (gen_call_block_move ! 539: (gen_rtx (SYMBOL_REF, Pmode, IDENTIFIER_POINTER (entry_name)), ! 540: dest, src, offset_rtx, value_rtx, ! 541: gen_rtx (REG, GET_MODE (value_rtx), (evenp ? 4 : 5)))); ! 542: ! 543: if (remainder) ! 544: block_move_sequence (gen_rtx (REG, Pmode, 2), dest_mem, ! 545: gen_rtx (REG, Pmode, 3), src_mem, ! 546: remainder, align, most); ! 547: } ! 548: ! 549: /* Emit code to perform a block move with an offset sequence of ld/st ! 550: instructions (..., ld 0, st 1, ld 1, st 0, ...). SIZE and ALIGN are ! 551: known constants. DEST and SRC are registers. OFFSET is the known ! 552: starting point for the output pattern. */ ! 553: ! 554: static void ! 555: block_move_sequence (dest, dest_mem, src, src_mem, size, align, offset) ! 556: rtx dest, dest_mem; ! 557: rtx src, src_mem; ! 558: int size; ! 559: int align; ! 560: int offset; ! 561: { ! 562: rtx temp[2]; ! 563: enum machine_mode mode[2]; ! 564: int amount[2]; ! 565: int active[2]; ! 566: int phase = 0; ! 567: int next; ! 568: int offset_ld = offset; ! 569: int offset_st = offset; ! 570: ! 571: active[0] = active[1] = FALSE; ! 572: ! 573: /* Establish parameters for the first load and for the second load if ! 574: it is known to be the same mode as the first. */ ! 575: amount[0] = amount[1] = align; ! 576: mode[0] = mode_from_bytes[align]; ! 577: temp[0] = gen_reg_rtx (mode[0]); ! 578: if (size >= 2 * align) ! 579: { ! 580: mode[1] = mode[0]; ! 581: temp[1] = gen_reg_rtx (mode[1]); ! 582: } ! 583: ! 584: do ! 585: { ! 586: rtx srcp, dstp; ! 587: next = phase; ! 588: phase = !phase; ! 589: ! 590: if (size > 0) ! 591: { ! 592: /* Change modes as the sequence tails off. */ ! 593: if (size < amount[next]) ! 594: { ! 595: amount[next] = (size >= 2 ? 2 : 1); ! 596: mode[next] = mode_from_bytes[amount[next]]; ! 597: temp[next] = gen_reg_rtx (mode[next]); ! 598: } ! 599: size -= amount[next]; ! 600: srcp = gen_rtx (MEM, mode[next], ! 601: gen_rtx (PLUS, Pmode, src, ! 602: gen_rtx (CONST_INT, SImode, offset_ld))); ! 603: RTX_UNCHANGING_P (srcp) = RTX_UNCHANGING_P (src_mem); ! 604: MEM_VOLATILE_P (srcp) = MEM_VOLATILE_P (src_mem); ! 605: MEM_IN_STRUCT_P (srcp) = MEM_IN_STRUCT_P (src_mem); ! 606: emit_move_insn (temp[next], srcp); ! 607: offset_ld += amount[next]; ! 608: active[next] = TRUE; ! 609: } ! 610: ! 611: if (active[phase]) ! 612: { ! 613: active[phase] = FALSE; ! 614: dstp = gen_rtx (MEM, mode[phase], ! 615: gen_rtx (PLUS, Pmode, dest, ! 616: gen_rtx (CONST_INT, SImode, offset_st))); ! 617: RTX_UNCHANGING_P (dstp) = RTX_UNCHANGING_P (dest_mem); ! 618: MEM_VOLATILE_P (dstp) = MEM_VOLATILE_P (dest_mem); ! 619: MEM_IN_STRUCT_P (dstp) = MEM_IN_STRUCT_P (dest_mem); ! 620: emit_move_insn (dstp, temp[phase]); ! 621: offset_st += amount[phase]; ! 622: } ! 623: } ! 624: while (active[next]); ! 625: } ! 626: ! 627: /* Emit the code to do an AND operation. */ ! 628: ! 629: char * ! 630: output_and (operands) ! 631: rtx operands[]; ! 632: { ! 633: unsigned int value; ! 634: ! 635: if (REG_P (operands[2])) ! 636: return "and %0,%1,%2"; ! 637: ! 638: value = INTVAL (operands[2]); ! 639: if (SMALL_INTVAL (value)) ! 640: return "mask %0,%1,%2"; ! 641: else if ((value & 0xffff0000) == 0xffff0000) ! 642: return "and %0,%1,%x2"; ! 643: else if ((value & 0xffff) == 0xffff) ! 644: return "and.u %0,%1,%X2"; ! 645: else if ((value & 0xffff) == 0) ! 646: return "mask.u %0,%1,%X2"; ! 647: else if (integer_ok_for_set (~value)) ! 648: return "clr %0,%1,%S2"; ! 649: else ! 650: return "and.u %0,%1,%X2\n\tand %0,%0,%x2"; ! 651: } ! 652: ! 653: /* Emit the code to do an inclusive OR operation. */ ! 654: ! 655: char * ! 656: output_ior (operands) ! 657: rtx operands[]; ! 658: { ! 659: unsigned int value; ! 660: ! 661: if (REG_P (operands[2])) ! 662: return "or %0,%1,%2"; ! 663: ! 664: value = INTVAL (operands[2]); ! 665: if (SMALL_INTVAL (value)) ! 666: return "or %0,%1,%2"; ! 667: else if ((value & 0xffff) == 0) ! 668: return "or.u %0,%1,%X2"; ! 669: else if (integer_ok_for_set (value)) ! 670: return "set %0,%1,%s2"; ! 671: else ! 672: return "or.u %0,%1,%X2\n\tor %0,%0,%x2"; ! 673: } ! 674: ! 675: /* Emit the instructions for doing an XOR. */ ! 676: ! 677: char * ! 678: output_xor (operands) ! 679: rtx operands[]; ! 680: { ! 681: unsigned int value; ! 682: ! 683: if (REG_P (operands[2])) ! 684: return "xor %0,%1,%2"; ! 685: ! 686: value = INTVAL (operands[2]); ! 687: if (SMALL_INTVAL (value)) ! 688: return "xor %0,%1,%2"; ! 689: else if ((value & 0xffff) == 0) ! 690: return "xor.u %0,%1,%X2"; ! 691: else ! 692: return "xor.u %0,%1,%X2\n\txor %0,%0,%x2"; ! 693: } ! 694: ! 695: /* Output a call. Normally this is just bsr or jsr, but this also deals with ! 696: accomplishing a branch after the call by incrementing r1. This requires ! 697: that various assembler bugs be accomodated. The 4.30 DG/UX assembler ! 698: requires that forward references not occur when computing the difference of ! 699: two labels. The [version?] Motorola assembler computes a word difference. ! 700: No doubt there's more to come! ! 701: ! 702: It would seem the same idea could be used to tail call, but in this case, ! 703: the epilogue will be non-null. */ ! 704: ! 705: static rtx sb_name = 0; ! 706: static rtx sb_high = 0; ! 707: static rtx sb_low = 0; ! 708: ! 709: char * ! 710: output_call (operands, addr) ! 711: rtx operands[]; ! 712: rtx addr; ! 713: { ! 714: operands[0] = addr; ! 715: if (final_sequence) ! 716: { ! 717: rtx jump; ! 718: ! 719: /* This can be generalized, but there is currently no need. */ ! 720: if (XVECLEN (final_sequence, 0) != 2) ! 721: abort (); ! 722: ! 723: jump = XVECEXP (final_sequence, 0, 1); ! 724: if (GET_CODE (jump) == JUMP_INSN) ! 725: { ! 726: rtx low, high; ! 727: char *last; ! 728: rtx dest = XEXP (SET_SRC (PATTERN (jump)), 0); ! 729: int delta = 4 * (insn_addresses[INSN_UID (dest)] ! 730: - insn_addresses[INSN_UID (jump)]); ! 731: #if (MONITOR_GCC & 0x2) /* How often do long branches happen? */ ! 732: if ((unsigned) (delta + 0x8000) >= 0x10000) ! 733: warning ("Internal gcc monitor: short-branch(%x)", delta); ! 734: #endif ! 735: ! 736: /* Delete the jump. */ ! 737: PUT_CODE (jump, NOTE); ! 738: NOTE_LINE_NUMBER (jump) = NOTE_INSN_DELETED; ! 739: NOTE_SOURCE_FILE (jump) = 0; ! 740: ! 741: /* If we loose, we must use the non-delay form. This is unlikely ! 742: to ever happen. If it becomes a problem, claim that a call ! 743: has two delay slots and only the second can be filled with ! 744: a jump. */ ! 745: #ifdef AS_BUG_IMMEDIATE_LABEL /* The assembler restricts immediate values. */ ! 746: if (! ADD_INTVAL (delta * 2)) ! 747: #else ! 748: if (! ADD_INTVAL (delta)) ! 749: #endif ! 750: { ! 751: operands[1] = dest; ! 752: return (REG_P (addr) ! 753: ? "jsr %0\n\tbr %l1" ! 754: : (flag_pic ! 755: ? "bsr %0#plt\n\tbr %l1" ! 756: : "bsr %0\n\tbr %l1")); ! 757: } ! 758: ! 759: /* Output the short branch form. */ ! 760: output_asm_insn ((REG_P (addr) ! 761: ? "jsr.n %0" ! 762: : (flag_pic ? "bsr.n %0#plt" : "bsr.n %0")), ! 763: operands); ! 764: ! 765: operands[0] = gen_label_rtx (); ! 766: operands[1] = gen_label_rtx (); ! 767: if (delta < 0) ! 768: { ! 769: low = dest; ! 770: high = operands[1]; ! 771: last = "subu %#r1,%#r1,%l0\n%l1:"; ! 772: } ! 773: else ! 774: { ! 775: low = operands[1]; ! 776: high = dest; ! 777: last = "addu %#r1,%#r1,%l0\n%l1:"; ! 778: } ! 779: ! 780: /* Record the values to be computed later as "def name,high-low". */ ! 781: sb_name = gen_rtx (EXPR_LIST, VOIDmode, operands[0], sb_name); ! 782: sb_high = gen_rtx (EXPR_LIST, VOIDmode, high, sb_high); ! 783: sb_low = gen_rtx (EXPR_LIST, VOIDmode, low, sb_low); ! 784: ! 785: return last; ! 786: } ! 787: } ! 788: return (REG_P (addr) ! 789: ? "jsr%. %0" ! 790: : (flag_pic ? "bsr%. %0#plt" : "bsr%. %0")); ! 791: } ! 792: ! 793: static void ! 794: output_short_branch_defs (stream) ! 795: FILE *stream; ! 796: { ! 797: char name[256], high[256], low[256]; ! 798: ! 799: for (; sb_name && sb_high && sb_low; ! 800: sb_name = XEXP (sb_name, 1), ! 801: sb_high = XEXP (sb_high, 1), ! 802: sb_low = XEXP (sb_low, 1)) ! 803: { ! 804: ASM_GENERATE_INTERNAL_LABEL ! 805: (name, "L", CODE_LABEL_NUMBER (XEXP (sb_name, 0))); ! 806: ASM_GENERATE_INTERNAL_LABEL ! 807: (high, "L", CODE_LABEL_NUMBER (XEXP (sb_high, 0))); ! 808: ASM_GENERATE_INTERNAL_LABEL ! 809: (low, "L", CODE_LABEL_NUMBER (XEXP (sb_low, 0))); ! 810: /* This will change as the assembler requirements become known. */ ! 811: fprintf (stream, "%s\t %s,%s-%s\n", ! 812: DEF_ASM_OP, &name[1], &high[1], &low[1]); ! 813: } ! 814: if (sb_name || sb_high || sb_low) ! 815: abort (); ! 816: } ! 817: ! 818: /* Report errors on floating point, if we are given NaN's, or such. Leave ! 819: the number as is, though, since we output the number in hex, and the ! 820: assemble won't choak on it. */ ! 821: ! 822: void ! 823: check_float_value (mode, value) ! 824: enum machine_mode mode; ! 825: REAL_VALUE_TYPE value; ! 826: { ! 827: union { ! 828: REAL_VALUE_TYPE d; ! 829: struct { ! 830: unsigned sign : 1; ! 831: unsigned exponent : 11; ! 832: unsigned mantissa1 : 20; ! 833: unsigned mantissa2; ! 834: } s; ! 835: } u; ! 836: ! 837: if (mode == DFmode) ! 838: { ! 839: u.d = value; ! 840: if (u.s.mantissa1 != 0 || u.s.mantissa2 != 0) ! 841: { ! 842: if (u.s.exponent == 0x7ff) /* Not a Number */ ! 843: warning ("floating point number is not valid for IEEE double precision"); ! 844: else if (u.s.exponent == 0) ! 845: warning ("denormalized double precision floating point number"); ! 846: } ! 847: } ! 848: else if (mode == SFmode) ! 849: { ! 850: u.d = REAL_VALUE_TRUNCATE (mode, value); ! 851: if (u.s.mantissa1 != 0 || u.s.mantissa2 != 0) ! 852: { ! 853: if (u.s.exponent == 0x7ff) /* Not a Number */ ! 854: warning ("floating point number is not valid for IEEE double precision"); ! 855: else if (u.s.exponent == 0) ! 856: warning ("denormalized single precision floating point number"); ! 857: } ! 858: else if (u.s.exponent == 0x7ff) /* Infinity */ ! 859: warning ("floating point number exceeds range of `float'"); ! 860: } ! 861: } ! 862: ! 863: /* Return true if the operand is a power of two and is a floating ! 864: point type (to optimize division by power of two into multiplication). */ ! 865: ! 866: int ! 867: real_power_of_2_operand (op, mode) ! 868: rtx op; ! 869: enum machine_mode mode; ! 870: { ! 871: union { ! 872: REAL_VALUE_TYPE d; ! 873: int i[sizeof (REAL_VALUE_TYPE) / sizeof (int)]; ! 874: struct { /* IEEE double precision format */ ! 875: unsigned sign : 1; ! 876: unsigned exponent : 11; ! 877: unsigned mantissa1 : 20; ! 878: unsigned mantissa2; ! 879: } s; ! 880: struct { /* IEEE double format to quick check */ ! 881: unsigned sign : 1; /* if it fits in a float */ ! 882: unsigned exponent1 : 4; ! 883: unsigned exponent2 : 7; ! 884: unsigned mantissa1 : 20; ! 885: unsigned mantissa2; ! 886: } s2; ! 887: } u; ! 888: ! 889: if (GET_MODE (op) != DFmode && GET_MODE (op) != SFmode) ! 890: return 0; ! 891: ! 892: if (GET_CODE (op) != CONST_DOUBLE) ! 893: return 0; ! 894: ! 895: u.i[0] = CONST_DOUBLE_LOW (op); ! 896: u.i[1] = CONST_DOUBLE_HIGH (op); ! 897: ! 898: if (u.s.mantissa1 != 0 || u.s.mantissa2 != 0 /* not a power of two */ ! 899: || u.s.exponent == 0 /* constant 0.0 */ ! 900: || u.s.exponent == 0x7ff /* NAN */ ! 901: || (u.s2.exponent1 != 0x8 && u.s2.exponent1 != 0x7)) ! 902: return 0; /* const won't fit in float */ ! 903: ! 904: return 1; ! 905: } ! 906: ! 907: /* Make OP legitimate for mode MODE. Currently this only deals with DFmode ! 908: operands, putting them in registers and making CONST_DOUBLE values ! 909: SFmode where possible. */ ! 910: ! 911: struct rtx_def * ! 912: legitimize_operand (op, mode) ! 913: rtx op; ! 914: enum machine_mode mode; ! 915: { ! 916: rtx temp; ! 917: union { ! 918: union real_extract r; ! 919: struct { /* IEEE double precision format */ ! 920: unsigned sign : 1; ! 921: unsigned exponent : 11; ! 922: unsigned mantissa1 : 20; ! 923: unsigned mantissa2; ! 924: } d; ! 925: struct { /* IEEE double format to quick check */ ! 926: unsigned sign : 1; /* if it fits in a float */ ! 927: unsigned exponent1 : 4; ! 928: unsigned exponent2 : 7; ! 929: unsigned mantissa1 : 20; ! 930: unsigned mantissa2; ! 931: } s; ! 932: } u; ! 933: ! 934: if (GET_CODE (op) == REG || mode != DFmode) ! 935: return op; ! 936: ! 937: if (GET_CODE (op) == CONST_DOUBLE) ! 938: { ! 939: bcopy (&CONST_DOUBLE_LOW (op), &u.r, sizeof u); ! 940: if (u.d.exponent != 0x7ff /* NaN */ ! 941: && u.d.mantissa2 == 0 /* Mantissa fits */ ! 942: && (u.s.exponent1 == 0x8 || u.s.exponent1 == 0x7) /* Exponent fits */ ! 943: && (temp = simplify_unary_operation (FLOAT_TRUNCATE, SFmode, ! 944: op, mode)) != 0) ! 945: return gen_rtx (FLOAT_EXTEND, mode, force_reg (SFmode, temp)); ! 946: } ! 947: else if (register_operand (op, mode)) ! 948: return op; ! 949: ! 950: return force_reg (mode, op); ! 951: } ! 952: ! 953: /* Return true if OP is a suitable input for a move insn. */ ! 954: ! 955: int ! 956: move_operand (op, mode) ! 957: rtx op; ! 958: enum machine_mode mode; ! 959: { ! 960: if (register_operand (op, mode)) ! 961: return 1; ! 962: if (GET_CODE (op) == CONST_INT) ! 963: return (classify_integer (mode, INTVAL (op)) < m88k_oru_hi16); ! 964: if (GET_MODE (op) != mode) ! 965: return 0; ! 966: if (GET_CODE (op) == SUBREG) ! 967: op = SUBREG_REG (op); ! 968: if (GET_CODE (op) != MEM) ! 969: return 0; ! 970: ! 971: op = XEXP (op, 0); ! 972: if (GET_CODE (op) == LO_SUM) ! 973: return (REG_P (XEXP (op, 0)) ! 974: && symbolic_address_p (XEXP (op, 1))); ! 975: return memory_address_p (mode, op); ! 976: } ! 977: ! 978: /* Return true if OP is suitable for a call insn. */ ! 979: ! 980: int ! 981: call_address_operand (op, mode) ! 982: rtx op; ! 983: enum machine_mode mode; ! 984: { ! 985: return (REG_P (op) || symbolic_address_p (op)); ! 986: } ! 987: ! 988: /* Returns true if OP is either a symbol reference or a sum of a symbol ! 989: reference and a constant. */ ! 990: ! 991: int ! 992: symbolic_address_p (op) ! 993: register rtx op; ! 994: { ! 995: switch (GET_CODE (op)) ! 996: { ! 997: case SYMBOL_REF: ! 998: case LABEL_REF: ! 999: return 1; ! 1000: ! 1001: case CONST: ! 1002: op = XEXP (op, 0); ! 1003: return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF ! 1004: || GET_CODE (XEXP (op, 0)) == LABEL_REF) ! 1005: && GET_CODE (XEXP (op, 1)) == CONST_INT); ! 1006: ! 1007: default: ! 1008: return 0; ! 1009: } ! 1010: } ! 1011: ! 1012: /* Return true if OP is a register or const0_rtx. */ ! 1013: ! 1014: int ! 1015: reg_or_0_operand (op, mode) ! 1016: rtx op; ! 1017: enum machine_mode mode; ! 1018: { ! 1019: return (op == const0_rtx || register_operand (op, mode)); ! 1020: } ! 1021: ! 1022: /* Nonzero if OP is a valid second operand for an arithmetic insn. */ ! 1023: ! 1024: int ! 1025: arith_operand (op, mode) ! 1026: rtx op; ! 1027: enum machine_mode mode; ! 1028: { ! 1029: return (register_operand (op, mode) ! 1030: || (GET_CODE (op) == CONST_INT && SMALL_INT (op))); ! 1031: } ! 1032: ! 1033: /* Return true if OP is a register or 5 bit integer. */ ! 1034: ! 1035: int ! 1036: arith5_operand (op, mode) ! 1037: rtx op; ! 1038: enum machine_mode mode; ! 1039: { ! 1040: return (register_operand (op, mode) ! 1041: || (GET_CODE (op) == CONST_INT && (unsigned) INTVAL (op) < 32)); ! 1042: } ! 1043: ! 1044: int ! 1045: arith32_operand (op, mode) ! 1046: rtx op; ! 1047: enum machine_mode mode; ! 1048: { ! 1049: return (register_operand (op, mode) || GET_CODE (op) == CONST_INT); ! 1050: } ! 1051: ! 1052: int ! 1053: arith64_operand (op, mode) ! 1054: rtx op; ! 1055: enum machine_mode mode; ! 1056: { ! 1057: return (register_operand (op, mode) ! 1058: || GET_CODE (op) == CONST_INT ! 1059: || (GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == DImode)); ! 1060: } ! 1061: ! 1062: int ! 1063: int5_operand (op, mode) ! 1064: rtx op; ! 1065: enum machine_mode mode; ! 1066: { ! 1067: return (GET_CODE (op) == CONST_INT && (unsigned) INTVAL (op) < 32); ! 1068: } ! 1069: ! 1070: int ! 1071: int32_operand (op, mode) ! 1072: rtx op; ! 1073: enum machine_mode mode; ! 1074: { ! 1075: return (GET_CODE (op) == CONST_INT); ! 1076: } ! 1077: ! 1078: /* Return true if OP is a register or a valid immediate operand for ! 1079: addu or subu. */ ! 1080: ! 1081: int ! 1082: add_operand (op, mode) ! 1083: rtx op; ! 1084: enum machine_mode mode; ! 1085: { ! 1086: return (register_operand (op, mode) ! 1087: || (GET_CODE (op) == CONST_INT && ADD_INT (op))); ! 1088: } ! 1089: ! 1090: /* Nonzero if this is a bitmask filling the bottom bits, for optimizing and + ! 1091: shift left combinations into a single mak instruction. */ ! 1092: ! 1093: int ! 1094: mak_mask_p (value) ! 1095: int value; ! 1096: { ! 1097: return (value && POWER_OF_2_or_0 (value + 1)); ! 1098: } ! 1099: ! 1100: int ! 1101: reg_or_bbx_mask_operand (op, mode) ! 1102: rtx op; ! 1103: enum machine_mode mode; ! 1104: { ! 1105: int value; ! 1106: if (register_operand (op, mode)) ! 1107: return 1; ! 1108: if (GET_CODE (op) != CONST_INT) ! 1109: return 0; ! 1110: ! 1111: value = INTVAL (op); ! 1112: if (POWER_OF_2 (value)) ! 1113: return 1; ! 1114: ! 1115: return 0; ! 1116: } ! 1117: ! 1118: /* Return true if OP is valid to use in the context of a floating ! 1119: point operation. Special case 0.0, since we can use r0. */ ! 1120: ! 1121: int ! 1122: real_or_0_operand (op, mode) ! 1123: rtx op; ! 1124: enum machine_mode mode; ! 1125: { ! 1126: if (mode != SFmode && mode != DFmode) ! 1127: return 0; ! 1128: ! 1129: return (register_operand (op, mode) ! 1130: || (GET_CODE (op) == CONST_DOUBLE ! 1131: && op == CONST0_RTX (mode))); ! 1132: } ! 1133: ! 1134: /* Return true if OP is a relational operator. */ ! 1135: ! 1136: int ! 1137: relop (op, mode) ! 1138: rtx op; ! 1139: enum machine_mode mode; ! 1140: { ! 1141: switch (GET_CODE (op)) ! 1142: { ! 1143: case EQ: ! 1144: case NE: ! 1145: case LT: ! 1146: case LE: ! 1147: case GE: ! 1148: case GT: ! 1149: case LTU: ! 1150: case LEU: ! 1151: case GEU: ! 1152: case GTU: ! 1153: return 1; ! 1154: default: ! 1155: return 0; ! 1156: } ! 1157: } ! 1158: ! 1159: /* Return true if OP is a relational operator, and is not an unsigned ! 1160: relational operator. */ ! 1161: ! 1162: int ! 1163: relop_no_unsigned (op, mode) ! 1164: rtx op; ! 1165: enum machine_mode mode; ! 1166: { ! 1167: switch (GET_CODE (op)) ! 1168: { ! 1169: case EQ: ! 1170: case NE: ! 1171: case LT: ! 1172: case LE: ! 1173: case GE: ! 1174: case GT: ! 1175: /* @@ What is this test doing? Why not use `mode'? */ ! 1176: if (GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT ! 1177: || GET_MODE (op) == DImode ! 1178: || GET_MODE_CLASS (GET_MODE (XEXP (op, 0))) == MODE_FLOAT ! 1179: || GET_MODE (XEXP (op, 0)) == DImode ! 1180: || GET_MODE_CLASS (GET_MODE (XEXP (op, 1))) == MODE_FLOAT ! 1181: || GET_MODE (XEXP (op, 1)) == DImode) ! 1182: return 0; ! 1183: return 1; ! 1184: default: ! 1185: return 0; ! 1186: } ! 1187: } ! 1188: ! 1189: /* Return true if the code of this rtx pattern is EQ or NE. */ ! 1190: ! 1191: int ! 1192: equality_op (op, mode) ! 1193: rtx op; ! 1194: enum machine_mode mode; ! 1195: { ! 1196: return (GET_CODE (op) == EQ || GET_CODE (op) == NE); ! 1197: } ! 1198: ! 1199: /* Return true if the code of this rtx pattern is pc or label_ref. */ ! 1200: ! 1201: int ! 1202: pc_or_label_ref (op, mode) ! 1203: rtx op; ! 1204: enum machine_mode mode; ! 1205: { ! 1206: return (GET_CODE (op) == PC || GET_CODE (op) == LABEL_REF); ! 1207: } ! 1208: ! 1209: /* Output to FILE the start of the assembler file. */ ! 1210: ! 1211: struct option ! 1212: { ! 1213: char *string; ! 1214: int *variable; ! 1215: int on_value; ! 1216: }; ! 1217: ! 1218: static int ! 1219: output_option (file, sep, type, name, indent, pos, max) ! 1220: FILE *file; ! 1221: char *sep; ! 1222: char *type; ! 1223: char *name; ! 1224: char *indent; ! 1225: int pos; ! 1226: int max; ! 1227: { ! 1228: if (strlen (sep) + strlen (type) + strlen (name) + pos > max) ! 1229: { ! 1230: fprintf (file, indent); ! 1231: return fprintf (file, "%s%s", type, name); ! 1232: } ! 1233: return pos + fprintf (file, "%s%s%s", sep, type, name); ! 1234: } ! 1235: ! 1236: static struct { char *name; int value; } m_options[] = TARGET_SWITCHES; ! 1237: ! 1238: static void ! 1239: output_options (file, f_options, f_len, W_options, W_len, ! 1240: pos, max, sep, indent, term) ! 1241: FILE *file; ! 1242: struct option *f_options; ! 1243: struct option *W_options; ! 1244: int f_len, W_len; ! 1245: int pos; ! 1246: int max; ! 1247: char *indent; ! 1248: char *term; ! 1249: { ! 1250: register int j; ! 1251: ! 1252: if (optimize) ! 1253: pos = output_option (file, sep, "-O", "", indent, pos, max); ! 1254: if (write_symbols != NO_DEBUG) ! 1255: pos = output_option (file, sep, "-g", "", indent, pos, max); ! 1256: if (flag_traditional) ! 1257: pos = output_option (file, sep, "-traditional", "", indent, pos, max); ! 1258: if (profile_flag) ! 1259: pos = output_option (file, sep, "-p", "", indent, pos, max); ! 1260: ! 1261: for (j = 0; j < f_len; j++) ! 1262: if (*f_options[j].variable == f_options[j].on_value) ! 1263: pos = output_option (file, sep, "-f", f_options[j].string, ! 1264: indent, pos, max); ! 1265: ! 1266: for (j = 0; j < W_len; j++) ! 1267: if (*W_options[j].variable == W_options[j].on_value) ! 1268: pos = output_option (file, sep, "-W", W_options[j].string, ! 1269: indent, pos, max); ! 1270: ! 1271: for (j = 0; j < sizeof m_options / sizeof m_options[0]; j++) ! 1272: if (m_options[j].name[0] != '\0' ! 1273: && m_options[j].value > 0 ! 1274: && ((m_options[j].value & target_flags) ! 1275: == m_options[j].value)) ! 1276: pos = output_option (file, sep, "-m", m_options[j].name, ! 1277: indent, pos, max); ! 1278: ! 1279: if (m88k_short_data) ! 1280: pos = output_option (file, sep, "-mshort-data-", m88k_short_data, ! 1281: indent, pos, max); ! 1282: ! 1283: fprintf (file, term); ! 1284: } ! 1285: ! 1286: void ! 1287: output_file_start (file, f_options, f_len, W_options, W_len) ! 1288: FILE *file; ! 1289: struct option *f_options; ! 1290: struct option *W_options; ! 1291: int f_len, W_len; ! 1292: { ! 1293: register int pos; ! 1294: ! 1295: ASM_FIRST_LINE (file); ! 1296: output_file_directive (file, main_input_filename); ! 1297: /* Switch to the data section so that the coffsem symbol and the ! 1298: gcc2_compiled. symbol aren't in the text section. */ ! 1299: data_section (); ! 1300: ASM_COFFSEM (file); ! 1301: ! 1302: pos = fprintf (file, "\n; cc1 (%s) arguments:", VERSION_STRING); ! 1303: output_options (file, f_options, f_len, W_options, W_len, ! 1304: pos, 75, " ", "\n; ", "\n\n"); ! 1305: ! 1306: if (TARGET_IDENTIFY_REVISION) ! 1307: { ! 1308: char indent[256]; ! 1309: ! 1310: time_t now = time ((time_t *)0); ! 1311: sprintf (indent, "]\"\n%s\t \"@(#)%s [", IDENT_ASM_OP, main_input_filename); ! 1312: fprintf (file, indent+3); ! 1313: pos = fprintf (file, "gcc %s, %.24s,", VERSION_STRING, ctime (&now)); ! 1314: output_options (file, f_options, f_len, W_options, W_len, ! 1315: pos, 150 - strlen (indent), " ", indent, "]\"\n\n"); ! 1316: } ! 1317: } ! 1318: ! 1319: /* Output an ascii string. */ ! 1320: ! 1321: void ! 1322: output_ascii (file, p, size) ! 1323: FILE *file; ! 1324: unsigned char *p; ! 1325: int size; ! 1326: { ! 1327: int i; ! 1328: ! 1329: register int num = 0; ! 1330: ! 1331: fprintf (file, "%s\t \"", ASCII_DATA_ASM_OP); ! 1332: for (i = 0; i < size; i++) ! 1333: { ! 1334: register int c = p[i]; ! 1335: ! 1336: if (num > 48) ! 1337: { ! 1338: fprintf (file, "\"\n%s\t \"", ASCII_DATA_ASM_OP); ! 1339: num = 0; ! 1340: } ! 1341: ! 1342: if (c == '\"' || c == '\\') ! 1343: { ! 1344: putc ('\\', file); ! 1345: num++; ! 1346: } ! 1347: ! 1348: if (c >= ' ' && c < 0177) ! 1349: { ! 1350: putc (c, file); ! 1351: num++; ! 1352: } ! 1353: else ! 1354: { ! 1355: fprintf (file, "\\%03o", c); ! 1356: num += 4; ! 1357: /* After an octal-escape, if a digit follows, ! 1358: terminate one string constant and start another. ! 1359: The Vax assembler fails to stop reading the escape ! 1360: after three digits, so this is the only way we ! 1361: can get it to parse the data properly. */ ! 1362: if (i < size - 1 && p[i + 1] >= '0' && p[i + 1] <= '9') ! 1363: num = 32767; /* next pass will start a new string */ ! 1364: } ! 1365: } ! 1366: fprintf (file, "\"\n"); ! 1367: } ! 1368: ! 1369: /* Output a label (allows insn-output.c to be compiled without including ! 1370: m88k.c or needing to include stdio.h). */ ! 1371: ! 1372: void ! 1373: output_label (label_number) ! 1374: int label_number; ! 1375: { ! 1376: ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", label_number); ! 1377: } ! 1378: ! 1379: /* Handle a pragma directive. HANDLE_PRAGMA conspires to parse the input ! 1380: following #pragma into tokens based on yylex. */ ! 1381: ! 1382: void ! 1383: m88k_handle_pragma_token (string, token) ! 1384: char *string; ! 1385: tree token; ! 1386: { ! 1387: static enum pragma_state ! 1388: { ! 1389: ps_start, ! 1390: ps_done, ! 1391: ps_bad, ! 1392: ps_weak, ! 1393: ps_name, ! 1394: ps_equals, ! 1395: ps_value ! 1396: } state; ! 1397: static char *name; ! 1398: static char *value; ! 1399: ! 1400: if (HANDLE_PRAGMA_WEAK) ! 1401: { ! 1402: if (string == 0) ! 1403: { ! 1404: if (state == ps_name || state == ps_value) ! 1405: { ! 1406: fprintf (asm_out_file, "%s\t ", WEAK_ASM_OP); ! 1407: ASM_OUTPUT_LABELREF (asm_out_file, name); ! 1408: fputc ('\n', asm_out_file); ! 1409: if (state == ps_value) ! 1410: { ! 1411: fprintf (asm_out_file, "%s\t ", DEF_ASM_OP); ! 1412: ASM_OUTPUT_LABELREF (asm_out_file, name); ! 1413: fputc (',', asm_out_file); ! 1414: ASM_OUTPUT_LABELREF (asm_out_file, value); ! 1415: fputc ('\n', asm_out_file); ! 1416: } ! 1417: } ! 1418: else if (! (state == ps_done || state == ps_start)) ! 1419: warning ("ignoring malformed #pragma weak symbol [=value]"); ! 1420: state = ps_start; ! 1421: } ! 1422: else ! 1423: switch (state) ! 1424: { ! 1425: case ps_start: ! 1426: if (token ! 1427: && TREE_CODE (token) == IDENTIFIER_NODE ! 1428: && !strcmp (IDENTIFIER_POINTER (token), "weak")) ! 1429: state = ps_weak; ! 1430: else ! 1431: state = ps_done; ! 1432: break; ! 1433: ! 1434: case ps_weak: ! 1435: if (token ! 1436: && TREE_CODE (token) == IDENTIFIER_NODE) ! 1437: { ! 1438: name = IDENTIFIER_POINTER (token); ! 1439: state = ps_name; ! 1440: } ! 1441: else ! 1442: state = ps_bad; ! 1443: break; ! 1444: ! 1445: case ps_name: ! 1446: state = (strcmp (string, "=") ? ps_bad : ps_equals); ! 1447: break; ! 1448: ! 1449: case ps_equals: ! 1450: if (token ! 1451: && TREE_CODE (token) == IDENTIFIER_NODE) ! 1452: { ! 1453: value = IDENTIFIER_POINTER (token); ! 1454: state = ps_value; ! 1455: } ! 1456: else ! 1457: state = ps_bad; ! 1458: break; ! 1459: ! 1460: case ps_value: ! 1461: state = ps_bad; ! 1462: case ps_bad: ! 1463: case ps_done: ! 1464: break; ! 1465: ! 1466: default: ! 1467: abort (); ! 1468: } ! 1469: } ! 1470: } ! 1471: ! 1472: /* Generate the assembly code for function entry. ! 1473: ! 1474: The prologue is responsible for setting up the stack frame, ! 1475: initializing the frame pointer register, saving registers that must be ! 1476: saved, and allocating SIZE additional bytes of storage for the ! 1477: local variables. SIZE is an integer. FILE is a stdio ! 1478: stream to which the assembler code should be output. ! 1479: ! 1480: The label for the beginning of the function need not be output by this ! 1481: macro. That has already been done when the macro is run. ! 1482: ! 1483: To determine which registers to save, the macro can refer to the array ! 1484: `regs_ever_live': element R is nonzero if hard register ! 1485: R is used anywhere within the function. This implies the ! 1486: function prologue should save register R, but not if it is one ! 1487: of the call-used registers. ! 1488: ! 1489: On machines where functions may or may not have frame-pointers, the ! 1490: function entry code must vary accordingly; it must set up the frame ! 1491: pointer if one is wanted, and not otherwise. To determine whether a ! 1492: frame pointer is in wanted, the macro can refer to the variable ! 1493: `frame_pointer_needed'. The variable's value will be 1 at run ! 1494: time in a function that needs a frame pointer. ! 1495: ! 1496: On machines where an argument may be passed partly in registers and ! 1497: partly in memory, this macro must examine the variable ! 1498: `current_function_pretend_args_size', and allocate that many bytes ! 1499: of uninitialized space on the stack just underneath the first argument ! 1500: arriving on the stack. (This may not be at the very end of the stack, ! 1501: if the calling sequence has pushed anything else since pushing the stack ! 1502: arguments. But usually, on such machines, nothing else has been pushed ! 1503: yet, because the function prologue itself does all the pushing.) ! 1504: ! 1505: If `ACCUMULATE_OUTGOING_ARGS' is defined, the variable ! 1506: `current_function_outgoing_args_size' contains the size in bytes ! 1507: required for the outgoing arguments. This macro must add that ! 1508: amount of uninitialized space to very bottom of the stack. ! 1509: ! 1510: The stack frame we use looks like this: ! 1511: ! 1512: caller callee ! 1513: |==============================================| ! 1514: | caller's frame | ! 1515: |==============================================| ! 1516: | [caller's outgoing memory arguments] | ! 1517: |==============================================| ! 1518: | caller's outgoing argument area (32 bytes) | ! 1519: sp -> |==============================================| <- ap ! 1520: | [local variable space] | ! 1521: |----------------------------------------------| ! 1522: | [return address (r1)] | ! 1523: |----------------------------------------------| ! 1524: | [previous frame pointer (r30)] | ! 1525: |==============================================| <- fp ! 1526: | [preserved registers (r25..r14)] | ! 1527: |==============================================| ! 1528: | [dynamically allocated space (alloca)] | ! 1529: |==============================================| ! 1530: | [callee's outgoing memory arguments] | ! 1531: |==============================================| ! 1532: | [callee's outgoing argument area (32 bytes)] | ! 1533: |==============================================| <- sp ! 1534: ! 1535: Notes: ! 1536: ! 1537: r1 and r30 must be saved if debugging. ! 1538: ! 1539: fp (if present) is located two words down from the local ! 1540: variable space. ! 1541: */ ! 1542: ! 1543: static void output_reg_adjust (); ! 1544: static void preserve_registers (); ! 1545: static void output_tdesc (); ! 1546: ! 1547: static int nregs; ! 1548: static char save_regs[FIRST_PSEUDO_REGISTER]; ! 1549: static int frame_laid_out; ! 1550: static int frame_size; ! 1551: static int variable_args_p; ! 1552: ! 1553: extern char call_used_regs[]; ! 1554: extern int current_function_pretend_args_size; ! 1555: extern int current_function_outgoing_args_size; ! 1556: extern int frame_pointer_needed; ! 1557: ! 1558: #define FIRST_OCS_PRESERVE_REGISTER 14 ! 1559: #define LAST_OCS_PRESERVE_REGISTER 30 ! 1560: ! 1561: #define STACK_UNIT_BOUNDARY (STACK_BOUNDARY / BITS_PER_UNIT) ! 1562: #define ROUND_CALL_BLOCK_SIZE(BYTES) \ ! 1563: (((BYTES) + (STACK_UNIT_BOUNDARY - 1)) & ~(STACK_UNIT_BOUNDARY - 1)) ! 1564: ! 1565: /* Establish the position of the FP relative to the SP. This is done ! 1566: either during FUNCTION_PROLOGUE or by INITIAL_ELIMINATION_OFFSET. */ ! 1567: ! 1568: void ! 1569: m88k_layout_frame () ! 1570: { ! 1571: int regno, sp_size; ! 1572: ! 1573: frame_laid_out++; ! 1574: ! 1575: bzero ((char *) &save_regs[0], sizeof (save_regs)); ! 1576: sp_size = nregs = 0; ! 1577: frame_size = get_frame_size (); ! 1578: ! 1579: /* Since profiling requires a call, make sure r1 is saved. */ ! 1580: if (profile_flag) ! 1581: save_regs[1] = 1; ! 1582: ! 1583: /* If we are producing debug information, store r1 and r30 where the ! 1584: debugger wants to find them (r30 at r30+0, r1 at r30+4). Space has ! 1585: already been reserved for r1/r30 in STARTING_FRAME_OFFSET. */ ! 1586: if (write_symbols != NO_DEBUG && !TARGET_OCS_FRAME_POSITION) ! 1587: save_regs[1] = 1; ! 1588: ! 1589: /* If there is a call, alloca is used, __builtin_alloca is used, or ! 1590: a dynamic-sized object is defined, add the 8 additional words ! 1591: for the callee's argument area. The common denominator is that the ! 1592: FP is required. may_call_alloca only gets calls to alloca; ! 1593: current_function_calls_alloca gets alloca and __builtin_alloca. */ ! 1594: if (regs_ever_live[1] || frame_pointer_needed) ! 1595: { ! 1596: save_regs[1] = 1; ! 1597: sp_size += REG_PARM_STACK_SPACE (0); ! 1598: } ! 1599: ! 1600: /* If we are producing PIC, save the addressing base register and r1. */ ! 1601: if (flag_pic && current_function_uses_pic_offset_table) ! 1602: { ! 1603: save_regs[PIC_OFFSET_TABLE_REGNUM] = 1; ! 1604: nregs++; ! 1605: } ! 1606: ! 1607: /* If a frame is requested, save the previous FP, and the return ! 1608: address (r1), so that a traceback can be done without using tdesc ! 1609: information. */ ! 1610: if (frame_pointer_needed) ! 1611: save_regs[FRAME_POINTER_REGNUM] = save_regs[1] = 1; ! 1612: ! 1613: /* Figure out which normal register(s) needs to be saved. */ ! 1614: for (regno = 2; regno < FRAME_POINTER_REGNUM; regno++) ! 1615: if (regs_ever_live[regno] && ! call_used_regs[regno]) ! 1616: { ! 1617: save_regs[regno] = 1; ! 1618: nregs++; ! 1619: } ! 1620: ! 1621: /* Achieve greatest use of double memory ops. Either we end up saving ! 1622: r30 or we use that slot to align the regsters we do save. */ ! 1623: if (nregs >= 2 && save_regs[1] && !save_regs[FRAME_POINTER_REGNUM]) ! 1624: sp_size += 4; ! 1625: ! 1626: nregs += save_regs[1] + save_regs[FRAME_POINTER_REGNUM]; ! 1627: sp_size += 4 * nregs; ! 1628: sp_size += current_function_outgoing_args_size; ! 1629: ! 1630: /* The first two saved registers are placed above the new frame pointer ! 1631: if any. In the only case this matters, they are r1 and r30. */ ! 1632: if (frame_pointer_needed || sp_size) ! 1633: { ! 1634: m88k_fp_offset = ROUND_CALL_BLOCK_SIZE (sp_size - STARTING_FRAME_OFFSET); ! 1635: m88k_stack_size = m88k_fp_offset + STARTING_FRAME_OFFSET; ! 1636: } ! 1637: else ! 1638: { ! 1639: m88k_stack_size = m88k_fp_offset = 0; ! 1640: } ! 1641: ! 1642: /* First, combine m88k_stack_size and size. If m88k_stack_size is ! 1643: non-zero, align the frame size to 8 mod 16; otherwise align the ! 1644: frame size to 0 mod 16. (If stacks are 8 byte aligned, this ends ! 1645: up as a NOP. */ ! 1646: { ! 1647: int need ! 1648: = ((m88k_stack_size ? STACK_UNIT_BOUNDARY - STARTING_FRAME_OFFSET : 0) ! 1649: - (frame_size % STACK_UNIT_BOUNDARY)); ! 1650: if (need) ! 1651: { ! 1652: if (need < 0) ! 1653: need += STACK_UNIT_BOUNDARY; ! 1654: (void) assign_stack_local (BLKmode, need, BITS_PER_UNIT); ! 1655: frame_size = get_frame_size (); ! 1656: } ! 1657: m88k_stack_size ! 1658: = ROUND_CALL_BLOCK_SIZE (m88k_stack_size + frame_size ! 1659: + current_function_pretend_args_size); ! 1660: } ! 1661: } ! 1662: ! 1663: /* Return true if this function is known to have a null epilogue. */ ! 1664: ! 1665: int ! 1666: null_epilogue () ! 1667: { ! 1668: if (! reload_completed) ! 1669: return 0; ! 1670: if (! frame_laid_out) ! 1671: m88k_layout_frame (); ! 1672: return (! frame_pointer_needed ! 1673: && nregs == 0 ! 1674: && m88k_stack_size == 0); ! 1675: } ! 1676: ! 1677: /* Determine the number of instructions needed for the function epilogue. */ ! 1678: ! 1679: #define MAX_EPILOGUE_DELAY_INSNS 4 ! 1680: ! 1681: static char epilogue_dead_regs[FIRST_PSEUDO_REGISTER]; ! 1682: ! 1683: delay_slots_for_epilogue () ! 1684: { ! 1685: register int insns = save_regs[1] + save_regs[FRAME_POINTER_REGNUM]; ! 1686: register int regs = nregs - insns; ! 1687: ! 1688: if (regs > 3) ! 1689: insns += 1 + (regs & 1); ! 1690: else if (nregs == 4) ! 1691: /* This is a special cases of ld/ld/ld.d which has no start-up delay. */ ! 1692: return 0; ! 1693: ! 1694: if (insns) ! 1695: { ! 1696: bzero ((char *) &epilogue_dead_regs[0], sizeof (epilogue_dead_regs)); ! 1697: epilogue_dead_regs[1] = save_regs[1]; ! 1698: epilogue_dead_regs[STACK_POINTER_REGNUM] = frame_pointer_needed; ! 1699: epilogue_dead_regs[TEMP_REGNUM] = ! ADD_INTVAL (m88k_fp_offset); ! 1700: } ! 1701: ! 1702: return insns; ! 1703: } ! 1704: ! 1705: /* Return 1 if X is safe to use as an epilogue insn. */ ! 1706: ! 1707: int ! 1708: ok_for_epilogue_p (x) ! 1709: rtx x; ! 1710: { ! 1711: register char *fmt; ! 1712: register int i, j; ! 1713: ! 1714: switch (GET_CODE (x)) ! 1715: { ! 1716: case REG: ! 1717: for (i = REGNO (x), j = i + HARD_REGNO_NREGS (i, GET_MODE (x)); ! 1718: i < j; ! 1719: i++) ! 1720: if (epilogue_dead_regs[i]) ! 1721: return 0; ! 1722: ! 1723: case CONST_INT: ! 1724: case CONST_DOUBLE: ! 1725: case CONST: ! 1726: case PC: ! 1727: case CC0: ! 1728: case LABEL_REF: ! 1729: case SYMBOL_REF: ! 1730: case CODE_LABEL: ! 1731: return 1; ! 1732: } ! 1733: ! 1734: fmt = GET_RTX_FORMAT (GET_CODE (x)); ! 1735: for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--) ! 1736: { ! 1737: if (fmt[i] == 'e') ! 1738: { ! 1739: if (!ok_for_epilogue_p (XEXP (x, i))) ! 1740: return 0; ! 1741: } ! 1742: else if (fmt[i] == 'E') ! 1743: { ! 1744: for (j = XVECLEN (x, i) - 1; j >= 0; j--) ! 1745: if (!ok_for_epilogue_p (XVECEXP (x, i, j))) ! 1746: return 0; ! 1747: } ! 1748: } ! 1749: return 1; ! 1750: } ! 1751: ! 1752: int ! 1753: eligible_for_epilogue_delay (insn) ! 1754: rtx insn; ! 1755: { ! 1756: switch (get_attr_type (insn)) ! 1757: { ! 1758: case TYPE_STORE: ! 1759: case TYPE_LOADA: ! 1760: case TYPE_ARITH: ! 1761: case TYPE_MARITH: ! 1762: case TYPE_MSTORE: ! 1763: return ok_for_epilogue_p (PATTERN (insn)); ! 1764: default: ! 1765: return 0; ! 1766: } ! 1767: } ! 1768: ! 1769: /* Determine if the current function has any references to the arg pointer. ! 1770: This is done indirectly by examining the DECL_ARGUMENTS' DECL_RTL. ! 1771: It is OK to return TRUE if there are no references, but FALSE must be ! 1772: correct. */ ! 1773: ! 1774: static int ! 1775: uses_arg_area_p () ! 1776: { ! 1777: register tree parm; ! 1778: ! 1779: if (current_function_decl == 0 ! 1780: || current_function_varargs ! 1781: || variable_args_p) ! 1782: return 1; ! 1783: ! 1784: for (parm = DECL_ARGUMENTS (current_function_decl); ! 1785: parm; ! 1786: parm = TREE_CHAIN (parm)) ! 1787: { ! 1788: if (DECL_RTL (parm) == 0 ! 1789: || GET_CODE (DECL_RTL (parm)) == MEM) ! 1790: return 1; ! 1791: ! 1792: if (DECL_INCOMING_RTL (parm) == 0 ! 1793: || GET_CODE (DECL_INCOMING_RTL (parm)) == MEM) ! 1794: return 1; ! 1795: } ! 1796: return 0; ! 1797: } ! 1798: ! 1799: void ! 1800: m88k_output_prologue (stream, size) ! 1801: FILE *stream; ! 1802: int size; ! 1803: { ! 1804: int old_fp_offset = m88k_fp_offset; ! 1805: int old_stack_size = m88k_stack_size; ! 1806: ! 1807: m88k_layout_frame (); ! 1808: #if (MONITOR_GCC & 0x8) /* Watch for suspicious register elimination changes. */ ! 1809: if (frame_laid_out > 1) ! 1810: { ! 1811: if (old_fp_offset != m88k_fp_offset) ! 1812: warning ("Internal gcc error: FP offset has changed by %d bytes", ! 1813: m88k_fp_offset - old_fp_offset); ! 1814: if (old_stack_size != m88k_stack_size) ! 1815: warning ("Internal gcc error: stack size has changed by %d bytes", ! 1816: m88k_stack_size - old_stack_size); ! 1817: } ! 1818: #endif ! 1819: frame_laid_out = 0; ! 1820: ! 1821: if (TARGET_OPTIMIZE_ARG_AREA ! 1822: && m88k_stack_size ! 1823: && ! uses_arg_area_p ()) ! 1824: { ! 1825: /* The incoming argument area is used for stack space if it is not ! 1826: used (or if -mno-use-arg-area is given). */ ! 1827: if ((m88k_stack_size -= REG_PARM_STACK_SPACE (0)) < 0) ! 1828: m88k_stack_size = 0; ! 1829: } ! 1830: ! 1831: if (m88k_stack_size) ! 1832: output_reg_adjust (stream, 31, 31, -m88k_stack_size, 0); ! 1833: ! 1834: if (nregs) ! 1835: preserve_registers (stream, m88k_fp_offset + 4, 1); ! 1836: ! 1837: if (frame_pointer_needed) ! 1838: output_reg_adjust (stream, 30, 31, m88k_fp_offset, 0); ! 1839: ! 1840: if (TARGET_OCS_DEBUG_INFO) ! 1841: PUT_OCS_FUNCTION_START (stream); ! 1842: ! 1843: if (flag_pic && save_regs[PIC_OFFSET_TABLE_REGNUM]) ! 1844: { ! 1845: char label[256]; ! 1846: ! 1847: if (! save_regs[1]) ! 1848: fprintf (stream, "\tor\t %s,%s,0\n", ! 1849: reg_names[TEMP_REGNUM], reg_names[1]); ! 1850: ASM_GENERATE_INTERNAL_LABEL (label, "Lab", m88k_function_number); ! 1851: fprintf (stream, "\tbsr.n\t %s\n", &label[1]); ! 1852: fprintf (stream, "\tor.u\t %s,%s,%shi16(%s#abdiff)\n", ! 1853: reg_names[PIC_OFFSET_TABLE_REGNUM], reg_names[0], ! 1854: m88k_pound_sign, &label[1]); ! 1855: ASM_OUTPUT_INTERNAL_LABEL (stream, "Lab", m88k_function_number); ! 1856: fprintf (stream, "\tor\t %s,%s,%slo16(%s#abdiff)\n", ! 1857: reg_names[PIC_OFFSET_TABLE_REGNUM], ! 1858: reg_names[PIC_OFFSET_TABLE_REGNUM], ! 1859: m88k_pound_sign, &label[1]); ! 1860: fprintf (stream, "\taddu\t %s,%s,%s\n", ! 1861: reg_names[PIC_OFFSET_TABLE_REGNUM], ! 1862: reg_names[PIC_OFFSET_TABLE_REGNUM], reg_names[1]); ! 1863: if (! save_regs[1]) ! 1864: fprintf (stream, "\tor\t %s,%s,0\n", ! 1865: reg_names[1], reg_names[TEMP_REGNUM]); ! 1866: } ! 1867: ! 1868: m88k_prologue_done = 1; /* it's ok now to put out ln directives */ ! 1869: } ! 1870: ! 1871: /* This function generates the assembly code for function exit, ! 1872: on machines that need it. Args are same as for FUNCTION_PROLOGUE. ! 1873: ! 1874: The function epilogue should not depend on the current stack pointer! ! 1875: It should use the frame pointer only, if there is a frame pointer. ! 1876: This is mandatory because of alloca; we also take advantage of it to ! 1877: omit stack adjustments before returning. */ ! 1878: ! 1879: void ! 1880: m88k_output_epilogue (stream, size) ! 1881: FILE *stream; ! 1882: int size; ! 1883: { ! 1884: rtx insn = get_last_insn (); ! 1885: #if (MONITOR_GCC & 0x4) /* What are interesting prologue/epiloge values? */ ! 1886: fprintf (stream, "; size = %d, m88k_fp_offset = %d, m88k_stack_size = %d\n", ! 1887: size, m88k_fp_offset, m88k_stack_size); ! 1888: #endif ! 1889: ! 1890: output_short_branch_defs (stream); ! 1891: ! 1892: if (TARGET_OCS_DEBUG_INFO) ! 1893: PUT_OCS_FUNCTION_END (stream); ! 1894: ! 1895: /* If the last insn was a BARRIER, we don't have to write any code. */ ! 1896: if (GET_CODE (insn) == NOTE) ! 1897: insn = prev_nonnote_insn (insn); ! 1898: if (insn && GET_CODE (insn) == BARRIER) ! 1899: { ! 1900: if (current_function_epilogue_delay_list) ! 1901: abort (); ! 1902: } ! 1903: else ! 1904: { ! 1905: if (frame_pointer_needed) ! 1906: output_reg_adjust (stream, 31, 30, -m88k_fp_offset, 0); ! 1907: ! 1908: if (nregs) ! 1909: preserve_registers (stream, m88k_fp_offset + 4, 0); ! 1910: ! 1911: output_reg_adjust (stream, 31, 31, m88k_stack_size, 1); ! 1912: } ! 1913: ! 1914: fprintf (stream, "\n"); ! 1915: ! 1916: if (TARGET_OCS_DEBUG_INFO) ! 1917: output_tdesc (stream, m88k_fp_offset + 4); ! 1918: ! 1919: m88k_function_number++; ! 1920: m88k_prologue_done = 0; /* don't put out ln directives */ ! 1921: variable_args_p = 0; /* has variable args */ ! 1922: } ! 1923: ! 1924: /* Output code to STREAM to set DSTREG to SRCREG + AMOUNT. Issue ! 1925: a return instruction and use it's delay slot based on RETURN_P. */ ! 1926: ! 1927: static void ! 1928: output_reg_adjust (stream, dstreg, srcreg, amount, return_p) ! 1929: FILE *stream; ! 1930: int dstreg, srcreg, amount, return_p; ! 1931: { ! 1932: char *opname; ! 1933: char incr[256]; ! 1934: ! 1935: if (amount < 0) ! 1936: { ! 1937: opname = "subu"; ! 1938: amount = -amount; ! 1939: } ! 1940: else ! 1941: opname = "addu"; ! 1942: ! 1943: if (amount == 0 && dstreg == srcreg) ! 1944: { ! 1945: if (return_p) ! 1946: fprintf (stream, "\tjmp\t %s\n", reg_names[1]); ! 1947: return; ! 1948: } ! 1949: else if (SMALL_INTVAL (amount)) ! 1950: sprintf (incr, "\t%s\t %s,%s,%d", opname, ! 1951: reg_names[dstreg], reg_names[srcreg], amount); ! 1952: else ! 1953: { ! 1954: rtx operands[2]; ! 1955: ! 1956: operands[0] = gen_rtx (REG, SImode, TEMP_REGNUM); ! 1957: operands[1] = gen_rtx (CONST_INT, VOIDmode, amount); ! 1958: output_asm_insn (output_load_const_int (SImode, operands), ! 1959: operands); ! 1960: sprintf (incr, "\t%s\t %s,%s,%s", opname, ! 1961: reg_names[dstreg], reg_names[srcreg], reg_names[TEMP_REGNUM]); ! 1962: } ! 1963: ! 1964: if (!return_p) ! 1965: fprintf (stream, "%s\n", incr); ! 1966: else if (flag_delayed_branch) ! 1967: fprintf (stream, "\tjmp.n\t %s\n%s\n", reg_names[1], incr); ! 1968: else ! 1969: fprintf (stream, "%s\n\tjmp\t %s\n", incr, reg_names[1]); ! 1970: } ! 1971: ! 1972: /* Save/restore the preserve registers. base is the highest offset from ! 1973: r31 at which a register is stored. store_p is true if stores are to ! 1974: be done; otherwise loads. When loading, output the epilogue delay ! 1975: insns. */ ! 1976: ! 1977: static void ! 1978: preserve_registers (stream, base, store_p) ! 1979: FILE *stream; ! 1980: int base; ! 1981: int store_p; ! 1982: { ! 1983: int regno, offset; ! 1984: char *fmt = (store_p ? "\tst%s\t %s,%s,%d\n" : "\tld%s\t %s,%s,%d\n"); ! 1985: struct mem_op { ! 1986: int regno; ! 1987: int nregs; ! 1988: int offset; ! 1989: } mem_op[FIRST_PSEUDO_REGISTER]; ! 1990: struct mem_op *mo_ptr = mem_op; ! 1991: ! 1992: /* The 88open OCS mandates that preserved registers be stored in ! 1993: increasing order. For compatibility with current practice, ! 1994: the order is r1, r30, then the preserve registers. */ ! 1995: ! 1996: offset = base; ! 1997: if (save_regs[1]) ! 1998: { ! 1999: /* An extra word is given in this case to make best use of double ! 2000: memory ops. */ ! 2001: if (nregs > 2 && !save_regs[FRAME_POINTER_REGNUM]) ! 2002: offset -= 4; ! 2003: fprintf (stream, fmt, "", reg_names[1], reg_names[31], offset); ! 2004: offset -= 4; ! 2005: base = offset; ! 2006: } ! 2007: ! 2008: /* Walk the registers to save recording all single memory operations. */ ! 2009: for (regno = FRAME_POINTER_REGNUM; regno > 1; regno--) ! 2010: if (save_regs[regno]) ! 2011: { ! 2012: if ((offset & 7) != 4 || (regno & 1) != 1 || !save_regs[regno-1]) ! 2013: { ! 2014: mo_ptr->nregs = 1; ! 2015: mo_ptr->regno = regno; ! 2016: mo_ptr->offset = offset; ! 2017: mo_ptr++; ! 2018: offset -= 4; ! 2019: } ! 2020: else ! 2021: { ! 2022: regno--; ! 2023: offset -= 2*4; ! 2024: } ! 2025: } ! 2026: ! 2027: /* Walk the registers to save recording all double memory operations. ! 2028: This avoids a delay in the epilogue (ld.d/ld). */ ! 2029: offset = base; ! 2030: for (regno = FRAME_POINTER_REGNUM; regno > 1; regno--) ! 2031: if (save_regs[regno]) ! 2032: { ! 2033: if ((offset & 7) != 4 || (regno & 1) != 1 || !save_regs[regno-1]) ! 2034: { ! 2035: offset -= 4; ! 2036: } ! 2037: else ! 2038: { ! 2039: mo_ptr->nregs = 2; ! 2040: mo_ptr->regno = regno-1; ! 2041: mo_ptr->offset = offset-4; ! 2042: mo_ptr++; ! 2043: regno--; ! 2044: offset -= 2*4; ! 2045: } ! 2046: } ! 2047: mo_ptr->regno = 0; ! 2048: ! 2049: /* Output the delay insns interleaved with the memory operations. */ ! 2050: if (! store_p && current_function_epilogue_delay_list) ! 2051: { ! 2052: rtx delay_insns = current_function_epilogue_delay_list; ! 2053: rtx insn; ! 2054: ! 2055: /* The first delay insn goes after the restore of r1. */ ! 2056: if (save_regs[1]) ! 2057: { ! 2058: final_scan_insn (XEXP (delay_insns, 0), stream, 1, 0, 1); ! 2059: delay_insns = XEXP (delay_insns, 1); ! 2060: } ! 2061: ! 2062: while (delay_insns) ! 2063: { ! 2064: /* Find a memory operation that doesn't conflict with this insn. */ ! 2065: for (mo_ptr = mem_op; mo_ptr->regno != 0; mo_ptr++) ! 2066: { ! 2067: if (mo_ptr->nregs) ! 2068: { ! 2069: rtx ok_insns = delay_insns; ! 2070: int i; ! 2071: ! 2072: for (i = 0; i < mo_ptr->nregs; i++) ! 2073: epilogue_dead_regs[mo_ptr->regno + i] = 1; ! 2074: ! 2075: while (ok_insns) ! 2076: { ! 2077: insn = XEXP (ok_insns, 0); ! 2078: ok_insns = XEXP (ok_insns, 1); ! 2079: ! 2080: if (! ok_for_epilogue_p (PATTERN (insn))) ! 2081: { ! 2082: for (i = 0; i < mo_ptr->nregs; i++) ! 2083: epilogue_dead_regs[mo_ptr->regno + i] = 0; ! 2084: insn = 0; ! 2085: break; /* foreach delay insn */ ! 2086: } ! 2087: } ! 2088: if (insn) ! 2089: { ! 2090: fprintf (stream, fmt, mo_ptr->nregs > 1 ? ".d" : "", ! 2091: reg_names[mo_ptr->regno], reg_names[31], ! 2092: mo_ptr->offset); ! 2093: mo_ptr->nregs = 0; ! 2094: break; /* foreach memory operation */ ! 2095: } ! 2096: } ! 2097: } ! 2098: final_scan_insn (XEXP (delay_insns, 0), stream, 1, 0, 1); ! 2099: delay_insns = XEXP (delay_insns, 1); ! 2100: } ! 2101: } ! 2102: ! 2103: /* Output the memory operations. */ ! 2104: for (mo_ptr = mem_op; mo_ptr->regno; mo_ptr++) ! 2105: { ! 2106: if (mo_ptr->nregs) ! 2107: fprintf (stream, fmt, mo_ptr->nregs > 1 ? ".d" : "", ! 2108: reg_names[mo_ptr->regno], reg_names[31], mo_ptr->offset); ! 2109: } ! 2110: } ! 2111: ! 2112: /* Convert the address expression REG to a CFA offset. */ ! 2113: ! 2114: int ! 2115: m88k_debugger_offset (reg, offset) ! 2116: register rtx reg; ! 2117: register int offset; ! 2118: { ! 2119: if (GET_CODE (reg) == PLUS) ! 2120: { ! 2121: offset = INTVAL (XEXP (reg, 1)); ! 2122: reg = XEXP (reg, 0); ! 2123: } ! 2124: ! 2125: /* Put the offset in terms of the CFA (arg pointer). */ ! 2126: if (reg == frame_pointer_rtx) ! 2127: offset += m88k_fp_offset - m88k_stack_size; ! 2128: else if (reg == stack_pointer_rtx) ! 2129: offset -= m88k_stack_size; ! 2130: else if (reg != arg_pointer_rtx) ! 2131: { ! 2132: if (! (GET_CODE (reg) == REG ! 2133: && REGNO (reg) >= FIRST_PSEUDO_REGISTER)) ! 2134: /* @@ For now, I'd like to know if this happens. */ ! 2135: warning ("Internal gcc error: Can't express symbolic location"); ! 2136: return 0; ! 2137: } ! 2138: ! 2139: return offset; ! 2140: } ! 2141: ! 2142: /* Output the 88open OCS proscribed text description information. ! 2143: The information is: ! 2144: 0 8: zero ! 2145: 0 22: info-byte-length (16 bytes) ! 2146: 0 2: info-alignment (word 2) ! 2147: 1 32: info-protocol (version 1) ! 2148: 2 32: starting-address (inclusive, not counting prologue) ! 2149: 3 32: ending-address (exclusive, not counting epilog) ! 2150: 4 8: info-variant (version 1) ! 2151: 4 17: register-save-mask (from register 14 to 30) ! 2152: 4 1: zero ! 2153: 4 1: return-address-info-discriminant ! 2154: 4 5: frame-address-register ! 2155: 5 32: frame-address-offset ! 2156: 6 32: return-address-info ! 2157: 7 32: register-save-offset */ ! 2158: ! 2159: static void ! 2160: output_tdesc (file, offset) ! 2161: FILE *file; ! 2162: int offset; ! 2163: { ! 2164: int regno, i; ! 2165: long mask, return_address_info, register_save_offset; ! 2166: char buf[256]; ! 2167: ! 2168: for (mask = 0, i = 0, regno = FIRST_OCS_PRESERVE_REGISTER; ! 2169: regno <= LAST_OCS_PRESERVE_REGISTER; ! 2170: regno++) ! 2171: { ! 2172: mask <<= 1; ! 2173: if (save_regs[regno]) ! 2174: { ! 2175: mask |= 1; ! 2176: i++; ! 2177: } ! 2178: } ! 2179: ! 2180: if (save_regs[1]) ! 2181: { ! 2182: if (nregs > 2 && !save_regs[FRAME_POINTER_REGNUM]) ! 2183: offset -= 4; ! 2184: return_address_info = - m88k_stack_size + offset; ! 2185: register_save_offset = return_address_info - i*4; ! 2186: } ! 2187: else ! 2188: { ! 2189: return_address_info = 1; ! 2190: register_save_offset = - m88k_stack_size + offset + 4 - i*4; ! 2191: } ! 2192: ! 2193: tdesc_section (); ! 2194: ! 2195: fprintf (file, "%s\t %d", INT_ASM_OP, (16 << 2) | 2 /* 8:0,22:16,2:2 */); ! 2196: fprintf (file, ",%d", flag_pic ? 2 : 1); ! 2197: ! 2198: ASM_GENERATE_INTERNAL_LABEL (buf, OCS_START_PREFIX, m88k_function_number); ! 2199: fprintf (file, ",%s%s", buf+1, flag_pic ? "#rel" : ""); ! 2200: ASM_GENERATE_INTERNAL_LABEL (buf, OCS_END_PREFIX, m88k_function_number); ! 2201: fprintf (file, ",%s%s", buf+1, flag_pic ? "#rel" : ""); ! 2202: ! 2203: fprintf (file, ",0x%x", /* 8:1,17:0x%.3x,1:0,1:%d,5:%d */ ! 2204: (1 << (17+1+1+5)) | ! 2205: (mask << (1+1+5)) | ! 2206: ((!!save_regs[1]) << 5) | ! 2207: ((frame_pointer_needed ! 2208: ? FRAME_POINTER_REGNUM ! 2209: : STACK_POINTER_REGNUM))); ! 2210: ! 2211: fprintf (file, ",0x%x", (m88k_stack_size ! 2212: - (frame_pointer_needed ? m88k_fp_offset : 0))); ! 2213: fprintf (file, ",0x%x", return_address_info); ! 2214: fprintf (file, ",0x%x\n", register_save_offset); ! 2215: ! 2216: text_section (); ! 2217: } ! 2218: ! 2219: /* Output assembler code to FILE to increment profiler label # LABELNO ! 2220: for profiling a function entry. NAME is the mcount function name ! 2221: (varies), SAVEP indicates whether the parameter registers need to ! 2222: be saved and restored. */ ! 2223: ! 2224: void ! 2225: output_function_profiler (file, labelno, name, savep) ! 2226: FILE *file; ! 2227: int labelno; ! 2228: char *name; ! 2229: int savep; ! 2230: { ! 2231: char label[256]; ! 2232: char dbi[256]; ! 2233: char *temp = (savep ? reg_names[2] : reg_names[10]); ! 2234: ! 2235: if (savep) ! 2236: { ! 2237: fprintf (file, "\tsubu\t %s,%s,64\n", reg_names[31], reg_names[31]); ! 2238: fprintf (file, "\tst.d\t %s,%s,32\n", reg_names[2], reg_names[31]); ! 2239: fprintf (file, "\tst.d\t %s,%s,40\n", reg_names[4], reg_names[31]); ! 2240: fprintf (file, "\tst.d\t %s,%s,48\n", reg_names[6], reg_names[31]); ! 2241: fprintf (file, "\tst.d\t %s,%s,56\n", reg_names[8], reg_names[31]); ! 2242: } ! 2243: ! 2244: ASM_GENERATE_INTERNAL_LABEL (label, "LP", labelno); ! 2245: if (flag_pic == 2) ! 2246: { ! 2247: fprintf (file, "\tor.u\t %s,%s,%shi16(%s#got_rel)\n", ! 2248: temp, reg_names[0], m88k_pound_sign, &label[1]); ! 2249: fprintf (file, "\tor\t %s,%s,%slo16(%s#got_rel)\n", ! 2250: temp, temp, m88k_pound_sign, &label[1]); ! 2251: sprintf (dbi, "\tld\t %s,%s,%s\n", temp, ! 2252: reg_names[PIC_OFFSET_TABLE_REGNUM], temp); ! 2253: } ! 2254: else if (flag_pic) ! 2255: { ! 2256: sprintf (dbi, "\tld\t %s,%s,%s#got_rel\n", temp, ! 2257: reg_names[PIC_OFFSET_TABLE_REGNUM], &label[1]); ! 2258: } ! 2259: else ! 2260: { ! 2261: fprintf (file, "\tor.u\t %s,%s,%shi16(%s)\n", ! 2262: temp, reg_names[0], m88k_pound_sign, &label[1]); ! 2263: sprintf (dbi, "\tor\t %s,%s,%slo16(%s)\n", ! 2264: temp, temp, m88k_pound_sign, &label[1]); ! 2265: } ! 2266: ! 2267: if (flag_pic) ! 2268: fprintf (file, "\tbsr.n\t %s#plt\n", name); ! 2269: else ! 2270: fprintf (file, "\tbsr.n\t %s\n", name); ! 2271: fputs (dbi, file); ! 2272: ! 2273: if (savep) ! 2274: { ! 2275: fprintf (file, "\tld.d\t %s,%s,32\n", reg_names[2], reg_names[31]); ! 2276: fprintf (file, "\tld.d\t %s,%s,40\n", reg_names[4], reg_names[31]); ! 2277: fprintf (file, "\tld.d\t %s,%s,48\n", reg_names[6], reg_names[31]); ! 2278: fprintf (file, "\tld.d\t %s,%s,56\n", reg_names[8], reg_names[31]); ! 2279: fprintf (file, "\taddu\t %s,%s,64\n", reg_names[31], reg_names[31]); ! 2280: } ! 2281: } ! 2282: ! 2283: /* Output assembler code to FILE to initialize basic-block profiling for ! 2284: the current module. LABELNO is unique to each instance. */ ! 2285: ! 2286: void ! 2287: output_function_block_profiler (file, labelno) ! 2288: FILE *file; ! 2289: int labelno; ! 2290: { ! 2291: char block[256]; ! 2292: char label[256]; ! 2293: ! 2294: ASM_GENERATE_INTERNAL_LABEL (block, "LPBX", 0); ! 2295: ASM_GENERATE_INTERNAL_LABEL (label, "LPY", labelno); ! 2296: ! 2297: /* @@ Need to deal with PIC. I'm not sure what the requirements are on ! 2298: register usage, so I used r26/r27 to be safe. */ ! 2299: fprintf (file, "\tor.u\t %s,%s,%shi16(%s)\n", reg_names[27], reg_names[0], ! 2300: m88k_pound_sign, &block[1]); ! 2301: fprintf (file, "\tld\t %s,%s,%slo16(%s)\n", reg_names[26], reg_names[27], ! 2302: m88k_pound_sign, &block[1]); ! 2303: fprintf (file, "\tbcnd\t %sne0,%s,%s\n", ! 2304: m88k_pound_sign, reg_names[26], &label[1]); ! 2305: fputs ("\tbsr.n\t ", file); ! 2306: ASM_OUTPUT_LABELREF (file, "__bb_init_func"); ! 2307: putc ('\n', file); ! 2308: fprintf (file, "\tor\t %s,%s,%slo16(%s)\n", reg_names[2], reg_names[27], ! 2309: m88k_pound_sign, &block[1]); ! 2310: ASM_OUTPUT_INTERNAL_LABEL (file, "LPY", labelno); ! 2311: } ! 2312: ! 2313: /* Output assembler code to FILE to increment the count associated with ! 2314: the basic block number BLOCKNO. */ ! 2315: ! 2316: void ! 2317: output_block_profiler (file, blockno) ! 2318: FILE *file; ! 2319: int blockno; ! 2320: { ! 2321: char block[256]; ! 2322: ! 2323: ASM_GENERATE_INTERNAL_LABEL (block, "LPBX", 0); ! 2324: ! 2325: /* @@ Need to deal with PIC. I'm not sure what the requirements are on ! 2326: register usage, so I used r26/r27 to be safe. */ ! 2327: fprintf (file, "\tor.u\t %s,%s,%shi16(%s+%d)\n", reg_names[27], reg_names[0], ! 2328: m88k_pound_sign, &block[1], 4 * blockno); ! 2329: fprintf (file, "\tld\t %s,%s,%slo16(%s+%d)\n", reg_names[26], reg_names[27], ! 2330: m88k_pound_sign, &block[1], 4 * blockno); ! 2331: fprintf (file, "\taddu\t %s,%s,1\n", reg_names[26], reg_names[26]); ! 2332: fprintf (file, "\tst\t %s,%s,%slo16(%s+%d)\n", reg_names[26], reg_names[27], ! 2333: m88k_pound_sign, &block[1], 4 * blockno); ! 2334: } ! 2335: ! 2336: /* Determine whether a function argument is passed in a register, and ! 2337: which register. ! 2338: ! 2339: The arguments are CUM, which summarizes all the previous ! 2340: arguments; MODE, the machine mode of the argument; TYPE, ! 2341: the data type of the argument as a tree node or 0 if that is not known ! 2342: (which happens for C support library functions); and NAMED, ! 2343: which is 1 for an ordinary argument and 0 for nameless arguments that ! 2344: correspond to `...' in the called function's prototype. ! 2345: ! 2346: The value of the expression should either be a `reg' RTX for the ! 2347: hard register in which to pass the argument, or zero to pass the ! 2348: argument on the stack. ! 2349: ! 2350: On the m88000 the first eight words of args are normally in registers ! 2351: and the rest are pushed. Double precision floating point must be ! 2352: double word aligned (and if in a register, starting on an even ! 2353: register). Structures and unions which are not 4 byte, and word ! 2354: aligned are passed in memory rather than registers, even if they ! 2355: would fit completely in the registers under OCS rules. ! 2356: ! 2357: Note that FUNCTION_ARG and FUNCTION_INCOMING_ARG were different. ! 2358: For structures that are passed in memory, but could have been ! 2359: passed in registers, we first load the structure into the ! 2360: register, and then when the last argument is passed, we store ! 2361: the registers into the stack locations. This fixes some bugs ! 2362: where GCC did not expect to have register arguments, followed ! 2363: by stack arguments, followed by register arguments. */ ! 2364: ! 2365: struct rtx_def * ! 2366: m88k_function_arg (args_so_far, mode, type, named) ! 2367: CUMULATIVE_ARGS args_so_far; ! 2368: enum machine_mode mode; ! 2369: tree type; ! 2370: int named; ! 2371: { ! 2372: int bytes, words; ! 2373: ! 2374: if (type != 0 /* undo putting struct in register */ ! 2375: && (TREE_CODE (type) == RECORD_TYPE || TREE_CODE (type) == UNION_TYPE)) ! 2376: mode = BLKmode; ! 2377: ! 2378: if (mode == BLKmode && TARGET_WARN_PASS_STRUCT) ! 2379: warning ("argument #%d is a structure", args_so_far + 1); ! 2380: ! 2381: if ((args_so_far & 1) != 0 ! 2382: && (mode == DImode || mode == DFmode ! 2383: || (type != 0 && TYPE_ALIGN (type) > 32))) ! 2384: args_so_far++; ! 2385: ! 2386: #ifdef ESKIT ! 2387: if (no_reg_params) ! 2388: return (rtx) 0; /* don't put args in registers */ ! 2389: #endif ! 2390: ! 2391: if (type == 0 && mode == BLKmode) ! 2392: abort (); /* m88k_function_arg argument `type' is NULL for BLKmode. */ ! 2393: ! 2394: bytes = (mode != BLKmode) ? GET_MODE_SIZE (mode) : int_size_in_bytes (type); ! 2395: words = (bytes + 3) / 4; ! 2396: ! 2397: if (args_so_far + words > 8) ! 2398: return (rtx) 0; /* args have exhausted registers */ ! 2399: ! 2400: else if (mode == BLKmode ! 2401: && (TYPE_ALIGN (type) != BITS_PER_WORD ! 2402: || bytes != UNITS_PER_WORD)) ! 2403: return (rtx) 0; ! 2404: ! 2405: return gen_rtx (REG, ! 2406: ((mode == BLKmode) ? TYPE_MODE (type) : mode), ! 2407: 2 + args_so_far); ! 2408: } ! 2409: ! 2410: /* Do what is necessary for `va_start'. The argument is ignored; ! 2411: We look at the current function to determine if stdargs or varargs ! 2412: is used and fill in an initial va_list. A pointer to this constructor ! 2413: is returned. */ ! 2414: ! 2415: struct rtx_def * ! 2416: m88k_builtin_saveregs (arglist) ! 2417: tree arglist; ! 2418: { ! 2419: rtx block, addr, argsize; ! 2420: tree fntype = TREE_TYPE (current_function_decl); ! 2421: int argadj = ((!(TYPE_ARG_TYPES (fntype) != 0 ! 2422: && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) ! 2423: != void_type_node))) ! 2424: ? -UNITS_PER_WORD : 0) + UNITS_PER_WORD - 1; ! 2425: int fixed; ! 2426: variable_args_p = 1; ! 2427: ! 2428: if (CONSTANT_P (current_function_arg_offset_rtx)) ! 2429: { ! 2430: fixed = (XINT (current_function_arg_offset_rtx, 0) ! 2431: + argadj) / UNITS_PER_WORD; ! 2432: argsize = gen_rtx (CONST_INT, VOIDmode, fixed); ! 2433: } ! 2434: else ! 2435: { ! 2436: fixed = 0; ! 2437: argsize = plus_constant (current_function_arg_offset_rtx, argadj); ! 2438: argsize = expand_shift (RSHIFT_EXPR, Pmode, argsize, ! 2439: build_int_2 (2, 0), argsize, 0); ! 2440: } ! 2441: ! 2442: /* Allocate the va_list constructor */ ! 2443: block = assign_stack_local (BLKmode, 3 * UNITS_PER_WORD, BITS_PER_UNIT); ! 2444: RTX_UNCHANGING_P (block) = 1; ! 2445: RTX_UNCHANGING_P (XEXP (block, 0)) = 1; ! 2446: ! 2447: /* Store the argsize as the __va_arg member. */ ! 2448: emit_move_insn (change_address (block, SImode, XEXP (block, 0)), ! 2449: argsize); ! 2450: ! 2451: /* Store the arg pointer in the __va_stk member. */ ! 2452: emit_move_insn (change_address (block, Pmode, ! 2453: plus_constant (XEXP (block, 0), ! 2454: UNITS_PER_WORD)), ! 2455: copy_to_reg (virtual_incoming_args_rtx)); ! 2456: ! 2457: /* Allocate the register space, and store it as the __va_reg member. */ ! 2458: addr = assign_stack_local (BLKmode, 8 * UNITS_PER_WORD, -1); ! 2459: MEM_IN_STRUCT_P (addr) = 1; ! 2460: RTX_UNCHANGING_P (addr) = 1; ! 2461: RTX_UNCHANGING_P (XEXP (addr, 0)) = 1; ! 2462: emit_move_insn (change_address (block, Pmode, ! 2463: plus_constant (XEXP (block, 0), ! 2464: 2 * UNITS_PER_WORD)), ! 2465: copy_to_reg (XEXP (addr, 0))); ! 2466: ! 2467: /* Now store the incoming registers and return the address of the ! 2468: va_list constructor. */ ! 2469: if (fixed < 8) ! 2470: move_block_from_reg ! 2471: (2 + fixed, ! 2472: change_address (addr, Pmode, ! 2473: plus_constant (XEXP (addr, 0), ! 2474: fixed * UNITS_PER_WORD)), ! 2475: 8 - fixed); ! 2476: ! 2477: return copy_to_reg (XEXP (block, 0)); ! 2478: } ! 2479: ! 2480: /* If cmpsi has not been generated, emit code to do the test. Return the ! 2481: expression describing the test of operator OP. */ ! 2482: ! 2483: rtx ! 2484: emit_test (op, mode) ! 2485: enum rtx_code op; ! 2486: enum machine_mode mode; ! 2487: { ! 2488: if (m88k_compare_reg == 0) ! 2489: emit_insn (gen_test (m88k_compare_op0, m88k_compare_op1)); ! 2490: return (gen_rtx (op, mode, m88k_compare_reg, const0_rtx)); ! 2491: } ! 2492: ! 2493: /* Determine how to best perform cmpsi/bxx, where cmpsi has a constant ! 2494: operand. All tests with zero (albeit swapped) and all equality tests ! 2495: with a constant are done with bcnd. The remaining cases are swapped ! 2496: as needed. */ ! 2497: ! 2498: void ! 2499: emit_bcnd (op, label) ! 2500: enum rtx_code op; ! 2501: rtx label; ! 2502: { ! 2503: if (m88k_compare_op1 == const0_rtx) ! 2504: emit_jump_insn (optimize ! 2505: ? gen_bxx (emit_test (op, VOIDmode), label) ! 2506: : gen_bcnd (gen_rtx (op, VOIDmode, ! 2507: m88k_compare_op0, const0_rtx), ! 2508: label)); ! 2509: else if (m88k_compare_op0 == const0_rtx) ! 2510: emit_jump_insn (optimize ! 2511: ? gen_bxx (emit_test (op, VOIDmode), label) ! 2512: : gen_bcnd (gen_rtx (swap_condition (op), VOIDmode, ! 2513: m88k_compare_op1, const0_rtx), ! 2514: label)); ! 2515: else if (op != EQ && op != NE) ! 2516: emit_jump_insn (gen_bxx (emit_test (op, VOIDmode), label)); ! 2517: else ! 2518: { ! 2519: rtx zero = gen_reg_rtx (SImode); ! 2520: rtx reg, constant; ! 2521: int value; ! 2522: ! 2523: if (GET_CODE (m88k_compare_op1) == CONST_INT) ! 2524: { ! 2525: reg = force_reg (SImode, m88k_compare_op0); ! 2526: constant = m88k_compare_op1; ! 2527: } ! 2528: else ! 2529: { ! 2530: reg = force_reg (SImode, m88k_compare_op1); ! 2531: constant = m88k_compare_op0; ! 2532: } ! 2533: value = INTVAL (constant); ! 2534: ! 2535: /* Perform an arithmetic computation to make the compared-to value ! 2536: zero, but avoid loosing if the bcnd is later changed into sxx. */ ! 2537: if (SMALL_INTVAL (value)) ! 2538: emit_jump_insn (gen_bxx (emit_test (op, VOIDmode), label)); ! 2539: else ! 2540: { ! 2541: if (SMALL_INTVAL (-value)) ! 2542: emit_insn (gen_addsi3 (zero, reg, ! 2543: gen_rtx (CONST_INT, VOIDmode, -value))); ! 2544: else ! 2545: emit_insn (gen_xorsi3 (zero, reg, constant)); ! 2546: ! 2547: emit_jump_insn (gen_bcnd (gen_rtx (op, VOIDmode, ! 2548: zero, const0_rtx), ! 2549: label)); ! 2550: } ! 2551: } ! 2552: } ! 2553: ! 2554: /* Print an operand. Recognize special options, documented below. */ ! 2555: ! 2556: void ! 2557: print_operand (file, x, code) ! 2558: FILE *file; ! 2559: rtx x; ! 2560: char code; ! 2561: { ! 2562: enum rtx_code xc = (x ? GET_CODE (x) : UNKNOWN); ! 2563: register int value = (xc == CONST_INT ? INTVAL (x) : 0); ! 2564: static int sequencep; ! 2565: static int reversep; ! 2566: ! 2567: if (sequencep) ! 2568: { ! 2569: if (code < 'B' || code > 'E') ! 2570: output_operand_lossage ("%R not followed by %B/C/D/E"); ! 2571: if (reversep) ! 2572: xc = reverse_condition (xc); ! 2573: sequencep = 0; ! 2574: } ! 2575: ! 2576: switch (code) ! 2577: { ! 2578: case '*': /* addressing base register for PIC */ ! 2579: fputs (reg_names[PIC_OFFSET_TABLE_REGNUM], file); return; ! 2580: ! 2581: case '#': /* SVR4 pound-sign syntax character (empty if SVR3) */ ! 2582: fputs (m88k_pound_sign, file); return; ! 2583: ! 2584: case 'X': /* print the upper 16 bits... */ ! 2585: value >>= 16; ! 2586: case 'x': /* print the lower 16 bits of the integer constant in hex */ ! 2587: if (xc != CONST_INT) ! 2588: output_operand_lossage ("invalid %x/X value"); ! 2589: fprintf (file, "0x%x", value & 0xffff); return; ! 2590: ! 2591: case 'H': /* print the low 16 bits of the negated integer constant */ ! 2592: if (xc != CONST_INT) ! 2593: output_operand_lossage ("invalid %H value"); ! 2594: value = -value; ! 2595: case 'h': /* print the register or low 16 bits of the integer constant */ ! 2596: if (xc == REG) ! 2597: goto reg; ! 2598: if (xc != CONST_INT) ! 2599: output_operand_lossage ("invalid %h value"); ! 2600: fprintf (file, "%d", value & 0xffff); ! 2601: return; ! 2602: ! 2603: case 'Q': /* print the low 8 bits of the negated integer constant */ ! 2604: if (xc != CONST_INT) ! 2605: output_operand_lossage ("invalid %Q value"); ! 2606: value = -value; ! 2607: case 'q': /* print the register or low 8 bits of the integer constant */ ! 2608: if (xc == REG) ! 2609: goto reg; ! 2610: if (xc != CONST_INT) ! 2611: output_operand_lossage ("invalid %q value"); ! 2612: fprintf (file, "%d", value & 0xff); ! 2613: return; ! 2614: ! 2615: case 'w': /* print the integer constant (X == 32 ? 0 : 32 - X) */ ! 2616: if (xc != CONST_INT) ! 2617: output_operand_lossage ("invalid %o value"); ! 2618: fprintf (file, "%d", value == 32 ? 0 : 32 - value); ! 2619: return; ! 2620: ! 2621: case 'p': /* print the logarithm of the integer constant */ ! 2622: if (xc != CONST_INT ! 2623: || (value = exact_log2 (value)) < 0) ! 2624: output_operand_lossage ("invalid %p value"); ! 2625: fprintf (file, "%d", value); ! 2626: return; ! 2627: ! 2628: case 'S': /* compliment the value and then... */ ! 2629: value = ~value; ! 2630: case 's': /* print the width and offset values forming the integer ! 2631: constant with a SET instruction. See integer_ok_for_set. */ ! 2632: { ! 2633: register unsigned mask, uval = value; ! 2634: register int top, bottom; ! 2635: ! 2636: if (xc != CONST_INT) ! 2637: output_operand_lossage ("invalid %s/S value"); ! 2638: /* All the "one" bits must be contiguous. If so, MASK will be ! 2639: a power of two or zero. */ ! 2640: mask = (uval | (uval - 1)) + 1; ! 2641: if (!(uval && POWER_OF_2_or_0 (mask))) ! 2642: output_operand_lossage ("invalid %s/S value"); ! 2643: top = mask ? exact_log2 (mask) : 32; ! 2644: bottom = exact_log2 (uval & ~(uval - 1)); ! 2645: fprintf (file,"%d<%d>", top - bottom, bottom); ! 2646: return; ! 2647: } ! 2648: ! 2649: case 'P': /* print nothing if pc_rtx; output label_ref */ ! 2650: if (xc == LABEL_REF) ! 2651: output_addr_const (file, x); ! 2652: else if (xc != PC) ! 2653: output_operand_lossage ("invalid %P operand"); ! 2654: return; ! 2655: ! 2656: case 'L': /* print 0 or 1 if operand is label_ref and then... */ ! 2657: fputc (xc == LABEL_REF ? '1' : '0', file); ! 2658: case '.': /* print .n if delay slot is used */ ! 2659: fputs ((final_sequence ! 2660: && ! INSN_ANNULLED_BRANCH_P (XVECEXP (final_sequence, 0, 0))) ! 2661: ? ".n\t" : "\t", file); ! 2662: return; ! 2663: ! 2664: case 'R': /* reverse the condition of the next print_operand ! 2665: if operand is a label_ref. */ ! 2666: sequencep++; ! 2667: reversep = (xc == LABEL_REF); ! 2668: return; ! 2669: ! 2670: case 'B': /* bcnd branch values */ ! 2671: fputs (m88k_pound_sign, file); ! 2672: switch (xc) ! 2673: { ! 2674: case EQ: fputs ("eq0", file); return; ! 2675: case NE: fputs ("ne0", file); return; ! 2676: case GT: fputs ("gt0", file); return; ! 2677: case LE: fputs ("le0", file); return; ! 2678: case LT: fputs ("lt0", file); return; ! 2679: case GE: fputs ("ge0", file); return; ! 2680: default: output_operand_lossage ("invalid %B value"); ! 2681: } ! 2682: ! 2683: case 'C': /* bb0/bb1 branch values for comparisons */ ! 2684: fputs (m88k_pound_sign, file); ! 2685: switch (xc) ! 2686: { ! 2687: case EQ: fputs ("eq", file); return; ! 2688: case NE: fputs ("ne", file); return; ! 2689: case GT: fputs ("gt", file); return; ! 2690: case LE: fputs ("le", file); return; ! 2691: case LT: fputs ("lt", file); return; ! 2692: case GE: fputs ("ge", file); return; ! 2693: case GTU: fputs ("hi", file); return; ! 2694: case LEU: fputs ("ls", file); return; ! 2695: case LTU: fputs ("lo", file); return; ! 2696: case GEU: fputs ("hs", file); return; ! 2697: default: output_operand_lossage ("invalid %C value"); ! 2698: } ! 2699: ! 2700: case 'D': /* bcnd branch values for float comparisons */ ! 2701: switch (xc) ! 2702: { ! 2703: case EQ: fputs ("0xa", file); return; ! 2704: case NE: fputs ("0x5", file); return; ! 2705: case GT: fputs (m88k_pound_sign, file); ! 2706: fputs ("gt0", file); return; ! 2707: case LE: fputs ("0xe", file); return; ! 2708: case LT: fputs ("0x4", file); return; ! 2709: case GE: fputs ("0xb", file); return; ! 2710: default: output_operand_lossage ("invalid %D value"); ! 2711: } ! 2712: ! 2713: case 'E': /* bcnd branch values for special integers */ ! 2714: switch (xc) ! 2715: { ! 2716: case EQ: fputs ("0x8", file); return; ! 2717: case NE: fputs ("0x7", file); return; ! 2718: default: output_operand_lossage ("invalid %E value"); ! 2719: } ! 2720: ! 2721: case 'd': /* second register of a two register pair */ ! 2722: if (xc != REG) ! 2723: output_operand_lossage ("`%d' operand isn't a register"); ! 2724: fputs (reg_names[REGNO (x) + 1], file); ! 2725: return; ! 2726: ! 2727: case 'r': /* an immediate 0 should be repesented as `r0' */ ! 2728: if (x == const0_rtx) ! 2729: { ! 2730: fputs (reg_names[0], file); ! 2731: return; ! 2732: } ! 2733: else if (xc != REG) ! 2734: output_operand_lossage ("invalid %r value"); ! 2735: case 0: ! 2736: name: ! 2737: if (xc == REG) ! 2738: { ! 2739: reg: ! 2740: if (REGNO (x) == ARG_POINTER_REGNUM) ! 2741: output_operand_lossage ("operand is r0"); ! 2742: else ! 2743: fputs (reg_names[REGNO (x)], file); ! 2744: } ! 2745: else if (xc == PLUS) ! 2746: output_address (x); ! 2747: else if (xc == MEM) ! 2748: output_address (XEXP (x, 0)); ! 2749: else if (xc == CONST_DOUBLE) ! 2750: output_operand_lossage ("operand is const_double"); ! 2751: else ! 2752: output_addr_const (file, x); ! 2753: return; ! 2754: ! 2755: case 'g': /* append #got_rel as needed */ ! 2756: if (flag_pic && (xc == SYMBOL_REF || xc == LABEL_REF)) ! 2757: { ! 2758: output_addr_const (file, x); ! 2759: fputs ("#got_rel", file); ! 2760: return; ! 2761: } ! 2762: goto name; ! 2763: ! 2764: case 'a': /* (standard), assume operand is an address */ ! 2765: case 'c': /* (standard), assume operand is an immediate value */ ! 2766: case 'l': /* (standard), assume operand is a label_ref */ ! 2767: case 'n': /* (standard), like %c, except negate first */ ! 2768: default: ! 2769: output_operand_lossage ("invalid code"); ! 2770: } ! 2771: } ! 2772: ! 2773: void ! 2774: print_operand_address (file, addr) ! 2775: FILE *file; ! 2776: rtx addr; ! 2777: { ! 2778: register rtx reg0, reg1, temp; ! 2779: ! 2780: switch (GET_CODE (addr)) ! 2781: { ! 2782: case REG: ! 2783: if (REGNO (addr) == ARG_POINTER_REGNUM) ! 2784: abort (); ! 2785: else ! 2786: fprintf (file, "%s,%s", reg_names[0], reg_names [REGNO (addr)]); ! 2787: break; ! 2788: ! 2789: case LO_SUM: ! 2790: fprintf (file, "%s,%slo16(", ! 2791: reg_names[REGNO (XEXP (addr, 0))], m88k_pound_sign); ! 2792: output_addr_const (file, XEXP (addr, 1)); ! 2793: fputc (')', file); ! 2794: break; ! 2795: ! 2796: case PLUS: ! 2797: reg0 = XEXP (addr, 0); ! 2798: reg1 = XEXP (addr, 1); ! 2799: if (GET_CODE (reg0) == MULT || GET_CODE (reg0) == CONST_INT) ! 2800: { ! 2801: rtx tmp = reg0; ! 2802: reg0 = reg1; ! 2803: reg1 = tmp; ! 2804: } ! 2805: ! 2806: if ((REG_P (reg0) && REGNO (reg0) == ARG_POINTER_REGNUM) ! 2807: || (REG_P (reg1) && REGNO (reg1) == ARG_POINTER_REGNUM)) ! 2808: abort (); ! 2809: ! 2810: else if (REG_P (reg0)) ! 2811: { ! 2812: if (REG_P (reg1)) ! 2813: fprintf (file, "%s,%s", ! 2814: reg_names [REGNO (reg0)], reg_names [REGNO (reg1)]); ! 2815: ! 2816: else if (GET_CODE (reg1) == CONST_INT) ! 2817: fprintf (file, "%s,%d", ! 2818: reg_names [REGNO (reg0)], INTVAL (reg1)); ! 2819: ! 2820: else if (GET_CODE (reg1) == MULT) ! 2821: { ! 2822: rtx mreg = XEXP (reg1, 0); ! 2823: if (REGNO (mreg) == ARG_POINTER_REGNUM) ! 2824: abort (); ! 2825: ! 2826: fprintf (file, "%s[%s]", reg_names[REGNO (reg0)], ! 2827: reg_names[REGNO (mreg)]); ! 2828: } ! 2829: ! 2830: else if (GET_CODE (reg1) == ZERO_EXTRACT) ! 2831: { ! 2832: fprintf (file, "%s,%slo16(", ! 2833: reg_names[REGNO (reg0)], m88k_pound_sign); ! 2834: output_addr_const (file, XEXP (reg1, 0)); ! 2835: fputc (')', file); ! 2836: } ! 2837: ! 2838: else if (flag_pic) ! 2839: { ! 2840: fprintf (file, "%s,", reg_names[REGNO (reg0)]); ! 2841: output_addr_const (file, reg1); ! 2842: fputs ("#got_rel", file); ! 2843: } ! 2844: else abort (); ! 2845: } ! 2846: ! 2847: else ! 2848: abort (); ! 2849: break; ! 2850: ! 2851: case MULT: ! 2852: if (REGNO (XEXP (addr, 0)) == ARG_POINTER_REGNUM) ! 2853: abort (); ! 2854: ! 2855: fprintf (file, "%s[%s]", ! 2856: reg_names[0], reg_names[REGNO (XEXP (addr, 0))]); ! 2857: break; ! 2858: ! 2859: case LSHIFT: ! 2860: fprintf (file, "%s,%shi16(", reg_names[0], m88k_pound_sign); ! 2861: output_addr_const (file, XEXP (addr, 0)); ! 2862: fputc (')', file); ! 2863: break; ! 2864: ! 2865: case CONST_INT: ! 2866: fprintf (file, "%s,%d", reg_names[0], INTVAL (addr)); ! 2867: break; ! 2868: ! 2869: default: ! 2870: fprintf (file, "%s,", reg_names[0]); ! 2871: if (SHORT_ADDRESS_P (addr, temp)) ! 2872: { ! 2873: fprintf (file, "%siw16(", m88k_pound_sign); ! 2874: output_addr_const (file, addr); ! 2875: fputc (')', file); ! 2876: } ! 2877: else ! 2878: output_addr_const (file, addr); ! 2879: } ! 2880: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.