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