|
|
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: ! 873: The 88110 can lose when a jsr.n r1 is issued and a page fault ! 874: occurs accessing the delay slot. So don't use jsr.n form when ! 875: jumping thru r1. ! 876: */ ! 877: #ifdef AS_BUG_IMMEDIATE_LABEL /* The assembler restricts immediate values. */ ! 878: if (optimize < 2 ! 879: || ! ADD_INTVAL (delta * 2) ! 880: #else ! 881: if (optimize < 2 ! 882: || ! ADD_INTVAL (delta) ! 883: #endif ! 884: || (REG_P (addr) && REGNO (addr) == 1)) ! 885: { ! 886: operands[1] = dest; ! 887: return (REG_P (addr) ! 888: ? "jsr %0\n\tbr %l1" ! 889: : (flag_pic ! 890: ? "bsr %0#plt\n\tbr %l1" ! 891: : "bsr %0\n\tbr %l1")); ! 892: } ! 893: ! 894: /* Output the short branch form. */ ! 895: output_asm_insn ((REG_P (addr) ! 896: ? "jsr.n %0" ! 897: : (flag_pic ? "bsr.n %0#plt" : "bsr.n %0")), ! 898: operands); ! 899: ! 900: #ifdef USE_GAS ! 901: last = (delta < 0 ! 902: ? "subu %#r1,%#r1,.-%l0+4" ! 903: : "addu %#r1,%#r1,%l0-.-4"); ! 904: operands[0] = dest; ! 905: #else ! 906: operands[0] = gen_label_rtx (); ! 907: operands[1] = gen_label_rtx (); ! 908: if (delta < 0) ! 909: { ! 910: low = dest; ! 911: high = operands[1]; ! 912: last = "subu %#r1,%#r1,%l0\n%l1:"; ! 913: } ! 914: else ! 915: { ! 916: low = operands[1]; ! 917: high = dest; ! 918: last = "addu %#r1,%#r1,%l0\n%l1:"; ! 919: } ! 920: ! 921: /* Record the values to be computed later as "def name,high-low". */ ! 922: sb_name = gen_rtx (EXPR_LIST, VOIDmode, operands[0], sb_name); ! 923: sb_high = gen_rtx (EXPR_LIST, VOIDmode, high, sb_high); ! 924: sb_low = gen_rtx (EXPR_LIST, VOIDmode, low, sb_low); ! 925: #endif /* Don't USE_GAS */ ! 926: ! 927: return last; ! 928: } ! 929: } ! 930: return (REG_P (addr) ! 931: ? "jsr%. %0" ! 932: : (flag_pic ? "bsr%. %0#plt" : "bsr%. %0")); ! 933: } ! 934: ! 935: static void ! 936: output_short_branch_defs (stream) ! 937: FILE *stream; ! 938: { ! 939: char name[256], high[256], low[256]; ! 940: ! 941: for (; sb_name && sb_high && sb_low; ! 942: sb_name = XEXP (sb_name, 1), ! 943: sb_high = XEXP (sb_high, 1), ! 944: sb_low = XEXP (sb_low, 1)) ! 945: { ! 946: ASM_GENERATE_INTERNAL_LABEL ! 947: (name, "L", CODE_LABEL_NUMBER (XEXP (sb_name, 0))); ! 948: ASM_GENERATE_INTERNAL_LABEL ! 949: (high, "L", CODE_LABEL_NUMBER (XEXP (sb_high, 0))); ! 950: ASM_GENERATE_INTERNAL_LABEL ! 951: (low, "L", CODE_LABEL_NUMBER (XEXP (sb_low, 0))); ! 952: /* This will change as the assembler requirements become known. */ ! 953: fprintf (stream, "\t%s\t %s,%s-%s\n", ! 954: SET_ASM_OP, &name[1], &high[1], &low[1]); ! 955: } ! 956: if (sb_name || sb_high || sb_low) ! 957: abort (); ! 958: } ! 959: ! 960: /* Return truth value of the statement that this conditional branch is likely ! 961: to fall through. CONDITION, is the condition that JUMP_INSN is testing. */ ! 962: ! 963: int ! 964: mostly_false_jump (jump_insn, condition) ! 965: rtx jump_insn, condition; ! 966: { ! 967: rtx target_label = JUMP_LABEL (jump_insn); ! 968: rtx insnt, insnj; ! 969: ! 970: /* Much of this isn't computed unless we're optimizing. */ ! 971: if (optimize == 0) ! 972: return 0; ! 973: ! 974: /* Determine if one path or the other leads to a return. */ ! 975: for (insnt = NEXT_INSN (target_label); ! 976: insnt; ! 977: insnt = NEXT_INSN (insnt)) ! 978: { ! 979: if (GET_CODE (insnt) == JUMP_INSN) ! 980: break; ! 981: else if (GET_CODE (insnt) == INSN ! 982: && GET_CODE (PATTERN (insnt)) == SEQUENCE ! 983: && GET_CODE (XVECEXP (PATTERN (insnt), 0, 0)) == JUMP_INSN) ! 984: { ! 985: insnt = XVECEXP (PATTERN (insnt), 0, 0); ! 986: break; ! 987: } ! 988: } ! 989: if (insnt ! 990: && (GET_CODE (PATTERN (insnt)) == RETURN ! 991: || (GET_CODE (PATTERN (insnt)) == SET ! 992: && GET_CODE (SET_SRC (PATTERN (insnt))) == REG ! 993: && REGNO (SET_SRC (PATTERN (insnt))) == 1))) ! 994: insnt = 0; ! 995: ! 996: for (insnj = NEXT_INSN (jump_insn); ! 997: insnj; ! 998: insnj = NEXT_INSN (insnj)) ! 999: { ! 1000: if (GET_CODE (insnj) == JUMP_INSN) ! 1001: break; ! 1002: else if (GET_CODE (insnj) == INSN ! 1003: && GET_CODE (PATTERN (insnj)) == SEQUENCE ! 1004: && GET_CODE (XVECEXP (PATTERN (insnj), 0, 0)) == JUMP_INSN) ! 1005: { ! 1006: insnj = XVECEXP (PATTERN (insnj), 0, 0); ! 1007: break; ! 1008: } ! 1009: } ! 1010: if (insnj ! 1011: && (GET_CODE (PATTERN (insnj)) == RETURN ! 1012: || (GET_CODE (PATTERN (insnj)) == SET ! 1013: && GET_CODE (SET_SRC (PATTERN (insnj))) == REG ! 1014: && REGNO (SET_SRC (PATTERN (insnj))) == 1))) ! 1015: insnj = 0; ! 1016: ! 1017: /* Predict to not return. */ ! 1018: if ((insnt == 0) != (insnj == 0)) ! 1019: return (insnt == 0); ! 1020: ! 1021: /* Predict loops to loop. */ ! 1022: for (insnt = PREV_INSN (target_label); ! 1023: insnt && GET_CODE (insnt) == NOTE; ! 1024: insnt = PREV_INSN (insnt)) ! 1025: if (NOTE_LINE_NUMBER (insnt) == NOTE_INSN_LOOP_END) ! 1026: return 1; ! 1027: else if (NOTE_LINE_NUMBER (insnt) == NOTE_INSN_LOOP_BEG) ! 1028: return 0; ! 1029: else if (NOTE_LINE_NUMBER (insnt) == NOTE_INSN_LOOP_CONT) ! 1030: return 0; ! 1031: ! 1032: /* Predict backward branches usually take. */ ! 1033: if (final_sequence) ! 1034: insnj = NEXT_INSN (PREV_INSN (XVECEXP (final_sequence, 0, 0))); ! 1035: else ! 1036: insnj = jump_insn; ! 1037: if (insn_addresses[INSN_UID (insnj)] ! 1038: > insn_addresses[INSN_UID (target_label)]) ! 1039: return 0; ! 1040: ! 1041: /* EQ tests are usually false and NE tests are usually true. Also, ! 1042: most quantities are positive, so we can make the appropriate guesses ! 1043: about signed comparisons against zero. Consider unsigned comparisons ! 1044: to be a range check and assume quantities to be in range. */ ! 1045: switch (GET_CODE (condition)) ! 1046: { ! 1047: case CONST_INT: ! 1048: /* Unconditional branch. */ ! 1049: return 0; ! 1050: case EQ: ! 1051: return 1; ! 1052: case NE: ! 1053: return 0; ! 1054: case LE: ! 1055: case LT: ! 1056: case GEU: ! 1057: case GTU: /* Must get casesi right at least. */ ! 1058: if (XEXP (condition, 1) == const0_rtx) ! 1059: return 1; ! 1060: break; ! 1061: case GE: ! 1062: case GT: ! 1063: case LEU: ! 1064: case LTU: ! 1065: if (XEXP (condition, 1) == const0_rtx) ! 1066: return 0; ! 1067: break; ! 1068: } ! 1069: ! 1070: return 0; ! 1071: } ! 1072: ! 1073: /* Return true if the operand is a power of two and is a floating ! 1074: point type (to optimize division by power of two into multiplication). */ ! 1075: ! 1076: int ! 1077: real_power_of_2_operand (op, mode) ! 1078: rtx op; ! 1079: enum machine_mode mode; ! 1080: { ! 1081: union { ! 1082: REAL_VALUE_TYPE d; ! 1083: int i[sizeof (REAL_VALUE_TYPE) / sizeof (int)]; ! 1084: struct { /* IEEE double precision format */ ! 1085: unsigned sign : 1; ! 1086: unsigned exponent : 11; ! 1087: unsigned mantissa1 : 20; ! 1088: unsigned mantissa2; ! 1089: } s; ! 1090: struct { /* IEEE double format to quick check */ ! 1091: unsigned sign : 1; /* if it fits in a float */ ! 1092: unsigned exponent1 : 4; ! 1093: unsigned exponent2 : 7; ! 1094: unsigned mantissa1 : 20; ! 1095: unsigned mantissa2; ! 1096: } s2; ! 1097: } u; ! 1098: ! 1099: if (GET_MODE (op) != DFmode && GET_MODE (op) != SFmode) ! 1100: return 0; ! 1101: ! 1102: if (GET_CODE (op) != CONST_DOUBLE) ! 1103: return 0; ! 1104: ! 1105: u.i[0] = CONST_DOUBLE_LOW (op); ! 1106: u.i[1] = CONST_DOUBLE_HIGH (op); ! 1107: ! 1108: if (u.s.mantissa1 != 0 || u.s.mantissa2 != 0 /* not a power of two */ ! 1109: || u.s.exponent == 0 /* constant 0.0 */ ! 1110: || u.s.exponent == 0x7ff /* NAN */ ! 1111: || (u.s2.exponent1 != 0x8 && u.s2.exponent1 != 0x7)) ! 1112: return 0; /* const won't fit in float */ ! 1113: ! 1114: return 1; ! 1115: } ! 1116: ! 1117: /* Make OP legitimate for mode MODE. Currently this only deals with DFmode ! 1118: operands, putting them in registers and making CONST_DOUBLE values ! 1119: SFmode where possible. */ ! 1120: ! 1121: struct rtx_def * ! 1122: legitimize_operand (op, mode) ! 1123: rtx op; ! 1124: enum machine_mode mode; ! 1125: { ! 1126: rtx temp; ! 1127: union { ! 1128: union real_extract r; ! 1129: struct { /* IEEE double precision format */ ! 1130: unsigned sign : 1; ! 1131: unsigned exponent : 11; ! 1132: unsigned mantissa1 : 20; ! 1133: unsigned mantissa2; ! 1134: } d; ! 1135: struct { /* IEEE double format to quick check */ ! 1136: unsigned sign : 1; /* if it fits in a float */ ! 1137: unsigned exponent1 : 4; ! 1138: unsigned exponent2 : 7; ! 1139: unsigned mantissa1 : 20; ! 1140: unsigned mantissa2; ! 1141: } s; ! 1142: } u; ! 1143: ! 1144: if (GET_CODE (op) == REG || mode != DFmode) ! 1145: return op; ! 1146: ! 1147: if (GET_CODE (op) == CONST_DOUBLE) ! 1148: { ! 1149: bcopy (&CONST_DOUBLE_LOW (op), &u.r, sizeof u); ! 1150: if (u.d.exponent != 0x7ff /* NaN */ ! 1151: && u.d.mantissa2 == 0 /* Mantissa fits */ ! 1152: && (u.s.exponent1 == 0x8 || u.s.exponent1 == 0x7) /* Exponent fits */ ! 1153: && (temp = simplify_unary_operation (FLOAT_TRUNCATE, SFmode, ! 1154: op, mode)) != 0) ! 1155: return gen_rtx (FLOAT_EXTEND, mode, force_reg (SFmode, temp)); ! 1156: } ! 1157: else if (register_operand (op, mode)) ! 1158: return op; ! 1159: ! 1160: return force_reg (mode, op); ! 1161: } ! 1162: ! 1163: /* Return true if OP is a suitable input for a move insn. */ ! 1164: ! 1165: int ! 1166: move_operand (op, mode) ! 1167: rtx op; ! 1168: enum machine_mode mode; ! 1169: { ! 1170: if (register_operand (op, mode)) ! 1171: return 1; ! 1172: if (GET_CODE (op) == CONST_INT) ! 1173: return (classify_integer (mode, INTVAL (op)) < m88k_oru_hi16); ! 1174: if (GET_MODE (op) != mode) ! 1175: return 0; ! 1176: if (GET_CODE (op) == SUBREG) ! 1177: op = SUBREG_REG (op); ! 1178: if (GET_CODE (op) != MEM) ! 1179: return 0; ! 1180: ! 1181: op = XEXP (op, 0); ! 1182: if (GET_CODE (op) == LO_SUM) ! 1183: return (REG_P (XEXP (op, 0)) ! 1184: && symbolic_address_p (XEXP (op, 1))); ! 1185: return memory_address_p (mode, op); ! 1186: } ! 1187: ! 1188: /* Return true if OP is suitable for a call insn. */ ! 1189: ! 1190: int ! 1191: call_address_operand (op, mode) ! 1192: rtx op; ! 1193: enum machine_mode mode; ! 1194: { ! 1195: return (REG_P (op) || symbolic_address_p (op)); ! 1196: } ! 1197: ! 1198: /* Returns true if OP is either a symbol reference or a sum of a symbol ! 1199: reference and a constant. */ ! 1200: ! 1201: int ! 1202: symbolic_address_p (op) ! 1203: register rtx op; ! 1204: { ! 1205: switch (GET_CODE (op)) ! 1206: { ! 1207: case SYMBOL_REF: ! 1208: case LABEL_REF: ! 1209: return 1; ! 1210: ! 1211: case CONST: ! 1212: op = XEXP (op, 0); ! 1213: return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF ! 1214: || GET_CODE (XEXP (op, 0)) == LABEL_REF) ! 1215: && GET_CODE (XEXP (op, 1)) == CONST_INT); ! 1216: ! 1217: default: ! 1218: return 0; ! 1219: } ! 1220: } ! 1221: ! 1222: /* Return true if OP is a register or const0_rtx. */ ! 1223: ! 1224: int ! 1225: reg_or_0_operand (op, mode) ! 1226: rtx op; ! 1227: enum machine_mode mode; ! 1228: { ! 1229: return (op == const0_rtx || register_operand (op, mode)); ! 1230: } ! 1231: ! 1232: /* Nonzero if OP is a valid second operand for an arithmetic insn. */ ! 1233: ! 1234: int ! 1235: arith_operand (op, mode) ! 1236: rtx op; ! 1237: enum machine_mode mode; ! 1238: { ! 1239: return (register_operand (op, mode) ! 1240: || (GET_CODE (op) == CONST_INT && SMALL_INT (op))); ! 1241: } ! 1242: ! 1243: /* Return true if OP is a register or 5 bit integer. */ ! 1244: ! 1245: int ! 1246: arith5_operand (op, mode) ! 1247: rtx op; ! 1248: enum machine_mode mode; ! 1249: { ! 1250: return (register_operand (op, mode) ! 1251: || (GET_CODE (op) == CONST_INT && (unsigned) INTVAL (op) < 32)); ! 1252: } ! 1253: ! 1254: int ! 1255: arith32_operand (op, mode) ! 1256: rtx op; ! 1257: enum machine_mode mode; ! 1258: { ! 1259: return (register_operand (op, mode) || GET_CODE (op) == CONST_INT); ! 1260: } ! 1261: ! 1262: int ! 1263: arith64_operand (op, mode) ! 1264: rtx op; ! 1265: enum machine_mode mode; ! 1266: { ! 1267: return (register_operand (op, mode) ! 1268: || GET_CODE (op) == CONST_INT ! 1269: || (GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == DImode)); ! 1270: } ! 1271: ! 1272: int ! 1273: int5_operand (op, mode) ! 1274: rtx op; ! 1275: enum machine_mode mode; ! 1276: { ! 1277: return (GET_CODE (op) == CONST_INT && (unsigned) INTVAL (op) < 32); ! 1278: } ! 1279: ! 1280: int ! 1281: int32_operand (op, mode) ! 1282: rtx op; ! 1283: enum machine_mode mode; ! 1284: { ! 1285: return (GET_CODE (op) == CONST_INT); ! 1286: } ! 1287: ! 1288: /* Return true if OP is a register or a valid immediate operand for ! 1289: addu or subu. */ ! 1290: ! 1291: int ! 1292: add_operand (op, mode) ! 1293: rtx op; ! 1294: enum machine_mode mode; ! 1295: { ! 1296: return (register_operand (op, mode) ! 1297: || (GET_CODE (op) == CONST_INT && ADD_INT (op))); ! 1298: } ! 1299: ! 1300: /* Nonzero if this is a bitmask filling the bottom bits, for optimizing and + ! 1301: shift left combinations into a single mak instruction. */ ! 1302: ! 1303: int ! 1304: mak_mask_p (value) ! 1305: int value; ! 1306: { ! 1307: return (value && POWER_OF_2_or_0 (value + 1)); ! 1308: } ! 1309: ! 1310: int ! 1311: reg_or_bbx_mask_operand (op, mode) ! 1312: rtx op; ! 1313: enum machine_mode mode; ! 1314: { ! 1315: int value; ! 1316: if (register_operand (op, mode)) ! 1317: return 1; ! 1318: if (GET_CODE (op) != CONST_INT) ! 1319: return 0; ! 1320: ! 1321: value = INTVAL (op); ! 1322: if (POWER_OF_2 (value)) ! 1323: return 1; ! 1324: ! 1325: return 0; ! 1326: } ! 1327: ! 1328: /* Return true if OP is valid to use in the context of a floating ! 1329: point operation. Special case 0.0, since we can use r0. */ ! 1330: ! 1331: int ! 1332: real_or_0_operand (op, mode) ! 1333: rtx op; ! 1334: enum machine_mode mode; ! 1335: { ! 1336: if (mode != SFmode && mode != DFmode) ! 1337: return 0; ! 1338: ! 1339: return (register_operand (op, mode) ! 1340: || (GET_CODE (op) == CONST_DOUBLE ! 1341: && op == CONST0_RTX (mode))); ! 1342: } ! 1343: ! 1344: /* Return true if OP is valid to use in the context of logic aritmethic ! 1345: on condition codes. */ ! 1346: ! 1347: int ! 1348: partial_ccmode_register_operand (op, mode) ! 1349: rtx op; ! 1350: enum machine_mode mode; ! 1351: { ! 1352: return register_operand (op, CCmode) || register_operand (op, CCEVENmode); ! 1353: } ! 1354: ! 1355: /* Return true if OP is a relational operator. */ ! 1356: ! 1357: int ! 1358: relop (op, mode) ! 1359: rtx op; ! 1360: enum machine_mode mode; ! 1361: { ! 1362: switch (GET_CODE (op)) ! 1363: { ! 1364: case EQ: ! 1365: case NE: ! 1366: case LT: ! 1367: case LE: ! 1368: case GE: ! 1369: case GT: ! 1370: case LTU: ! 1371: case LEU: ! 1372: case GEU: ! 1373: case GTU: ! 1374: return 1; ! 1375: default: ! 1376: return 0; ! 1377: } ! 1378: } ! 1379: ! 1380: int ! 1381: even_relop (op, mode) ! 1382: rtx op; ! 1383: enum machine_mode mode; ! 1384: { ! 1385: switch (GET_CODE (op)) ! 1386: { ! 1387: case EQ: ! 1388: case LT: ! 1389: case GT: ! 1390: case LTU: ! 1391: case GTU: ! 1392: return 1; ! 1393: default: ! 1394: return 0; ! 1395: } ! 1396: } ! 1397: ! 1398: int ! 1399: odd_relop (op, mode) ! 1400: rtx op; ! 1401: enum machine_mode mode; ! 1402: { ! 1403: switch (GET_CODE (op)) ! 1404: { ! 1405: case NE: ! 1406: case LE: ! 1407: case GE: ! 1408: case LEU: ! 1409: case GEU: ! 1410: return 1; ! 1411: default: ! 1412: return 0; ! 1413: } ! 1414: } ! 1415: ! 1416: /* Return true if OP is a relational operator, and is not an unsigned ! 1417: relational operator. */ ! 1418: ! 1419: int ! 1420: relop_no_unsigned (op, mode) ! 1421: rtx op; ! 1422: enum machine_mode mode; ! 1423: { ! 1424: switch (GET_CODE (op)) ! 1425: { ! 1426: case EQ: ! 1427: case NE: ! 1428: case LT: ! 1429: case LE: ! 1430: case GE: ! 1431: case GT: ! 1432: /* @@ What is this test doing? Why not use `mode'? */ ! 1433: if (GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT ! 1434: || GET_MODE (op) == DImode ! 1435: || GET_MODE_CLASS (GET_MODE (XEXP (op, 0))) == MODE_FLOAT ! 1436: || GET_MODE (XEXP (op, 0)) == DImode ! 1437: || GET_MODE_CLASS (GET_MODE (XEXP (op, 1))) == MODE_FLOAT ! 1438: || GET_MODE (XEXP (op, 1)) == DImode) ! 1439: return 0; ! 1440: return 1; ! 1441: default: ! 1442: return 0; ! 1443: } ! 1444: } ! 1445: ! 1446: /* Return true if the code of this rtx pattern is EQ or NE. */ ! 1447: ! 1448: int ! 1449: equality_op (op, mode) ! 1450: rtx op; ! 1451: enum machine_mode mode; ! 1452: { ! 1453: return (GET_CODE (op) == EQ || GET_CODE (op) == NE); ! 1454: } ! 1455: ! 1456: /* Return true if the code of this rtx pattern is pc or label_ref. */ ! 1457: ! 1458: int ! 1459: pc_or_label_ref (op, mode) ! 1460: rtx op; ! 1461: enum machine_mode mode; ! 1462: { ! 1463: return (GET_CODE (op) == PC || GET_CODE (op) == LABEL_REF); ! 1464: } ! 1465: ! 1466: /* Output to FILE the start of the assembler file. */ ! 1467: ! 1468: struct option ! 1469: { ! 1470: char *string; ! 1471: int *variable; ! 1472: int on_value; ! 1473: }; ! 1474: ! 1475: static int ! 1476: output_option (file, sep, type, name, indent, pos, max) ! 1477: FILE *file; ! 1478: char *sep; ! 1479: char *type; ! 1480: char *name; ! 1481: char *indent; ! 1482: int pos; ! 1483: int max; ! 1484: { ! 1485: if (strlen (sep) + strlen (type) + strlen (name) + pos > max) ! 1486: { ! 1487: fprintf (file, indent); ! 1488: return fprintf (file, "%s%s", type, name); ! 1489: } ! 1490: return pos + fprintf (file, "%s%s%s", sep, type, name); ! 1491: } ! 1492: ! 1493: static struct { char *name; int value; } m_options[] = TARGET_SWITCHES; ! 1494: ! 1495: static void ! 1496: output_options (file, f_options, f_len, W_options, W_len, ! 1497: pos, max, sep, indent, term) ! 1498: FILE *file; ! 1499: struct option *f_options; ! 1500: struct option *W_options; ! 1501: int f_len, W_len; ! 1502: int pos; ! 1503: int max; ! 1504: char *indent; ! 1505: char *term; ! 1506: { ! 1507: register int j; ! 1508: ! 1509: if (optimize) ! 1510: pos = output_option (file, sep, "-O", "", indent, pos, max); ! 1511: if (write_symbols != NO_DEBUG) ! 1512: pos = output_option (file, sep, "-g", "", indent, pos, max); ! 1513: if (flag_traditional) ! 1514: pos = output_option (file, sep, "-traditional", "", indent, pos, max); ! 1515: if (profile_flag) ! 1516: pos = output_option (file, sep, "-p", "", indent, pos, max); ! 1517: if (profile_block_flag) ! 1518: pos = output_option (file, sep, "-a", "", indent, pos, max); ! 1519: ! 1520: for (j = 0; j < f_len; j++) ! 1521: if (*f_options[j].variable == f_options[j].on_value) ! 1522: pos = output_option (file, sep, "-f", f_options[j].string, ! 1523: indent, pos, max); ! 1524: ! 1525: for (j = 0; j < W_len; j++) ! 1526: if (*W_options[j].variable == W_options[j].on_value) ! 1527: pos = output_option (file, sep, "-W", W_options[j].string, ! 1528: indent, pos, max); ! 1529: ! 1530: for (j = 0; j < sizeof m_options / sizeof m_options[0]; j++) ! 1531: if (m_options[j].name[0] != '\0' ! 1532: && m_options[j].value > 0 ! 1533: && ((m_options[j].value & target_flags) ! 1534: == m_options[j].value)) ! 1535: pos = output_option (file, sep, "-m", m_options[j].name, ! 1536: indent, pos, max); ! 1537: ! 1538: if (m88k_short_data) ! 1539: pos = output_option (file, sep, "-mshort-data-", m88k_short_data, ! 1540: indent, pos, max); ! 1541: ! 1542: fprintf (file, term); ! 1543: } ! 1544: ! 1545: void ! 1546: output_file_start (file, f_options, f_len, W_options, W_len) ! 1547: FILE *file; ! 1548: struct option *f_options; ! 1549: struct option *W_options; ! 1550: int f_len, W_len; ! 1551: { ! 1552: register int pos; ! 1553: ! 1554: ASM_FIRST_LINE (file); ! 1555: if (TARGET_88110 ! 1556: && m88k_version != 0 && strcmp (m88k_version, "04.00") >= 0) ! 1557: fprintf (file, "\t%s\n", REQUIRES_88110_ASM_OP); ! 1558: output_file_directive (file, main_input_filename); ! 1559: /* Switch to the data section so that the coffsem symbol and the ! 1560: gcc2_compiled. symbol aren't in the text section. */ ! 1561: data_section (); ! 1562: ASM_COFFSEM (file); ! 1563: ! 1564: pos = fprintf (file, "\n; cc1 (%s) arguments:", VERSION_STRING); ! 1565: output_options (file, f_options, f_len, W_options, W_len, ! 1566: pos, 75, " ", "\n; ", "\n\n"); ! 1567: ! 1568: if (TARGET_IDENTIFY_REVISION) ! 1569: { ! 1570: char indent[256]; ! 1571: ! 1572: time_t now = time ((time_t *)0); ! 1573: sprintf (indent, "]\"\n\t%s\t \"@(#)%s [", IDENT_ASM_OP, main_input_filename); ! 1574: fprintf (file, indent+3); ! 1575: pos = fprintf (file, "gcc %s, %.24s,", VERSION_STRING, ctime (&now)); ! 1576: output_options (file, f_options, f_len, W_options, W_len, ! 1577: pos, 150 - strlen (indent), " ", indent, "]\"\n\n"); ! 1578: } ! 1579: } ! 1580: ! 1581: /* Output an ascii string. */ ! 1582: ! 1583: void ! 1584: output_ascii (file, opcode, max, p, size) ! 1585: FILE *file; ! 1586: char *opcode; ! 1587: int max; ! 1588: unsigned char *p; ! 1589: int size; ! 1590: { ! 1591: int i; ! 1592: int in_escape = 0; ! 1593: ! 1594: register int num = 0; ! 1595: ! 1596: fprintf (file, "\t%s\t \"", opcode); ! 1597: for (i = 0; i < size; i++) ! 1598: { ! 1599: register int c = p[i]; ! 1600: ! 1601: if (num > max) ! 1602: { ! 1603: fprintf (file, "\"\n\t%s\t \"", opcode); ! 1604: num = 0; ! 1605: } ! 1606: ! 1607: if (c == '\"' || c == '\\') ! 1608: { ! 1609: escape: ! 1610: putc ('\\', file); ! 1611: putc (c, file); ! 1612: num += 2; ! 1613: in_escape = 0; ! 1614: } ! 1615: else if (in_escape && c >= '0' && c <= '9') ! 1616: { ! 1617: /* If a digit follows an octal-escape, the Vax assembler fails ! 1618: to stop reading the escape after three digits. Continue to ! 1619: output the values as an octal-escape until a non-digit is ! 1620: found. */ ! 1621: fprintf (file, "\\%03o", c); ! 1622: num += 4; ! 1623: } ! 1624: else if (c >= ' ' && c < 0177) ! 1625: { ! 1626: putc (c, file); ! 1627: num++; ! 1628: in_escape = 0; ! 1629: } ! 1630: else ! 1631: { ! 1632: switch (c) ! 1633: { ! 1634: /* Some assemblers can't handle \a, \v, or \?. */ ! 1635: case '\t': c = 't'; goto escape; ! 1636: case '\f': c = 'f'; goto escape; ! 1637: case '\b': c = 'b'; goto escape; ! 1638: case '\r': c = 'r'; goto escape; ! 1639: case '\n': c = 'n'; goto escape; ! 1640: } ! 1641: ! 1642: fprintf (file, "\\%03o", c); ! 1643: num += 4; ! 1644: in_escape = 1; ! 1645: } ! 1646: } ! 1647: fprintf (file, "\"\n"); ! 1648: } ! 1649: ! 1650: /* Output a label (allows insn-output.c to be compiled without including ! 1651: m88k.c or needing to include stdio.h). */ ! 1652: ! 1653: void ! 1654: output_label (label_number) ! 1655: int label_number; ! 1656: { ! 1657: ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", label_number); ! 1658: } ! 1659: ! 1660: /* Generate the assembly code for function entry. ! 1661: ! 1662: The prologue is responsible for setting up the stack frame, ! 1663: initializing the frame pointer register, saving registers that must be ! 1664: saved, and allocating SIZE additional bytes of storage for the ! 1665: local variables. SIZE is an integer. FILE is a stdio ! 1666: stream to which the assembler code should be output. ! 1667: ! 1668: The label for the beginning of the function need not be output by this ! 1669: macro. That has already been done when the macro is run. ! 1670: ! 1671: To determine which registers to save, the macro can refer to the array ! 1672: `regs_ever_live': element R is nonzero if hard register ! 1673: R is used anywhere within the function. This implies the ! 1674: function prologue should save register R, but not if it is one ! 1675: of the call-used registers. ! 1676: ! 1677: On machines where functions may or may not have frame-pointers, the ! 1678: function entry code must vary accordingly; it must set up the frame ! 1679: pointer if one is wanted, and not otherwise. To determine whether a ! 1680: frame pointer is in wanted, the macro can refer to the variable ! 1681: `frame_pointer_needed'. The variable's value will be 1 at run ! 1682: time in a function that needs a frame pointer. ! 1683: ! 1684: On machines where an argument may be passed partly in registers and ! 1685: partly in memory, this macro must examine the variable ! 1686: `current_function_pretend_args_size', and allocate that many bytes ! 1687: of uninitialized space on the stack just underneath the first argument ! 1688: arriving on the stack. (This may not be at the very end of the stack, ! 1689: if the calling sequence has pushed anything else since pushing the stack ! 1690: arguments. But usually, on such machines, nothing else has been pushed ! 1691: yet, because the function prologue itself does all the pushing.) ! 1692: ! 1693: If `ACCUMULATE_OUTGOING_ARGS' is defined, the variable ! 1694: `current_function_outgoing_args_size' contains the size in bytes ! 1695: required for the outgoing arguments. This macro must add that ! 1696: amount of uninitialized space to very bottom of the stack. ! 1697: ! 1698: The stack frame we use looks like this: ! 1699: ! 1700: caller callee ! 1701: |==============================================| ! 1702: | caller's frame | ! 1703: |==============================================| ! 1704: | [caller's outgoing memory arguments] | ! 1705: |==============================================| ! 1706: | caller's outgoing argument area (32 bytes) | ! 1707: sp -> |==============================================| <- ap ! 1708: | [local variable space] | ! 1709: |----------------------------------------------| ! 1710: | [return address (r1)] | ! 1711: |----------------------------------------------| ! 1712: | [previous frame pointer (r30)] | ! 1713: |==============================================| <- fp ! 1714: | [preserved registers (r25..r14)] | ! 1715: |----------------------------------------------| ! 1716: | [preserved registers (x29..x22)] | ! 1717: |==============================================| ! 1718: | [dynamically allocated space (alloca)] | ! 1719: |==============================================| ! 1720: | [callee's outgoing memory arguments] | ! 1721: |==============================================| ! 1722: | [callee's outgoing argument area (32 bytes)] | ! 1723: |==============================================| <- sp ! 1724: ! 1725: Notes: ! 1726: ! 1727: r1 and r30 must be saved if debugging. ! 1728: ! 1729: fp (if present) is located two words down from the local ! 1730: variable space. ! 1731: */ ! 1732: ! 1733: static void emit_add (); ! 1734: static void preserve_registers (); ! 1735: static void emit_ldst (); ! 1736: static void output_tdesc (); ! 1737: ! 1738: static int nregs; ! 1739: static int nxregs; ! 1740: static char save_regs[FIRST_PSEUDO_REGISTER]; ! 1741: static int frame_laid_out; ! 1742: static int frame_size; ! 1743: static int variable_args_p; ! 1744: static int epilogue_marked; ! 1745: static int prologue_marked; ! 1746: ! 1747: extern char call_used_regs[]; ! 1748: extern int current_function_pretend_args_size; ! 1749: extern int current_function_outgoing_args_size; ! 1750: extern int frame_pointer_needed; ! 1751: ! 1752: #define FIRST_OCS_PRESERVE_REGISTER 14 ! 1753: #define LAST_OCS_PRESERVE_REGISTER 30 ! 1754: ! 1755: #define FIRST_OCS_EXTENDED_PRESERVE_REGISTER (32 + 22) ! 1756: #define LAST_OCS_EXTENDED_PRESERVE_REGISTER (32 + 31) ! 1757: ! 1758: #define STACK_UNIT_BOUNDARY (STACK_BOUNDARY / BITS_PER_UNIT) ! 1759: #define ROUND_CALL_BLOCK_SIZE(BYTES) \ ! 1760: (((BYTES) + (STACK_UNIT_BOUNDARY - 1)) & ~(STACK_UNIT_BOUNDARY - 1)) ! 1761: ! 1762: /* Establish the position of the FP relative to the SP. This is done ! 1763: either during FUNCTION_PROLOGUE or by INITIAL_ELIMINATION_OFFSET. */ ! 1764: ! 1765: void ! 1766: m88k_layout_frame () ! 1767: { ! 1768: int regno, sp_size; ! 1769: ! 1770: frame_laid_out++; ! 1771: ! 1772: bzero ((char *) &save_regs[0], sizeof (save_regs)); ! 1773: sp_size = nregs = nxregs = 0; ! 1774: frame_size = get_frame_size (); ! 1775: ! 1776: /* Since profiling requires a call, make sure r1 is saved. */ ! 1777: if (profile_flag || profile_block_flag) ! 1778: save_regs[1] = 1; ! 1779: ! 1780: /* If we are producing debug information, store r1 and r30 where the ! 1781: debugger wants to find them (r30 at r30+0, r1 at r30+4). Space has ! 1782: already been reserved for r1/r30 in STARTING_FRAME_OFFSET. */ ! 1783: if (write_symbols != NO_DEBUG && !TARGET_OCS_FRAME_POSITION) ! 1784: save_regs[1] = 1; ! 1785: ! 1786: /* If there is a call, alloca is used, __builtin_alloca is used, or ! 1787: a dynamic-sized object is defined, add the 8 additional words ! 1788: for the callee's argument area. The common denominator is that the ! 1789: FP is required. may_call_alloca only gets calls to alloca; ! 1790: current_function_calls_alloca gets alloca and __builtin_alloca. */ ! 1791: if (regs_ever_live[1] || frame_pointer_needed) ! 1792: { ! 1793: save_regs[1] = 1; ! 1794: sp_size += REG_PARM_STACK_SPACE (0); ! 1795: } ! 1796: ! 1797: /* If we are producing PIC, save the addressing base register and r1. */ ! 1798: if (flag_pic && current_function_uses_pic_offset_table) ! 1799: { ! 1800: save_regs[PIC_OFFSET_TABLE_REGNUM] = 1; ! 1801: nregs++; ! 1802: } ! 1803: ! 1804: /* If a frame is requested, save the previous FP, and the return ! 1805: address (r1), so that a traceback can be done without using tdesc ! 1806: information. Otherwise, simply save the FP if it is used as ! 1807: a preserve register. */ ! 1808: if (frame_pointer_needed) ! 1809: save_regs[FRAME_POINTER_REGNUM] = save_regs[1] = 1; ! 1810: else if (regs_ever_live[FRAME_POINTER_REGNUM]) ! 1811: save_regs[FRAME_POINTER_REGNUM] = 1; ! 1812: ! 1813: /* Figure out which extended register(s) needs to be saved. */ ! 1814: for (regno = FIRST_EXTENDED_REGISTER + 1; regno < FIRST_PSEUDO_REGISTER; ! 1815: regno++) ! 1816: if (regs_ever_live[regno] && ! call_used_regs[regno]) ! 1817: { ! 1818: save_regs[regno] = 1; ! 1819: nxregs++; ! 1820: } ! 1821: ! 1822: /* Figure out which normal register(s) needs to be saved. */ ! 1823: for (regno = 2; regno < FRAME_POINTER_REGNUM; regno++) ! 1824: if (regs_ever_live[regno] && ! call_used_regs[regno]) ! 1825: { ! 1826: save_regs[regno] = 1; ! 1827: nregs++; ! 1828: } ! 1829: ! 1830: /* Achieve greatest use of double memory ops. Either we end up saving ! 1831: r30 or we use that slot to align the registers we do save. */ ! 1832: if (nregs >= 2 && save_regs[1] && !save_regs[FRAME_POINTER_REGNUM]) ! 1833: sp_size += 4; ! 1834: ! 1835: nregs += save_regs[1] + save_regs[FRAME_POINTER_REGNUM]; ! 1836: /* if we need to align extended registers, add a word */ ! 1837: if (nxregs > 0 && (nregs & 1) != 0) ! 1838: sp_size +=4; ! 1839: sp_size += 4 * nregs; ! 1840: sp_size += 8 * nxregs; ! 1841: sp_size += current_function_outgoing_args_size; ! 1842: ! 1843: /* The first two saved registers are placed above the new frame pointer ! 1844: if any. In the only case this matters, they are r1 and r30. */ ! 1845: if (frame_pointer_needed || sp_size) ! 1846: m88k_fp_offset = ROUND_CALL_BLOCK_SIZE (sp_size - STARTING_FRAME_OFFSET); ! 1847: else ! 1848: m88k_fp_offset = -STARTING_FRAME_OFFSET; ! 1849: m88k_stack_size = m88k_fp_offset + STARTING_FRAME_OFFSET; ! 1850: ! 1851: /* First, combine m88k_stack_size and size. If m88k_stack_size is ! 1852: non-zero, align the frame size to 8 mod 16; otherwise align the ! 1853: frame size to 0 mod 16. (If stacks are 8 byte aligned, this ends ! 1854: up as a NOP. */ ! 1855: { ! 1856: int need ! 1857: = ((m88k_stack_size ? STACK_UNIT_BOUNDARY - STARTING_FRAME_OFFSET : 0) ! 1858: - (frame_size % STACK_UNIT_BOUNDARY)); ! 1859: if (need) ! 1860: { ! 1861: if (need < 0) ! 1862: need += STACK_UNIT_BOUNDARY; ! 1863: (void) assign_stack_local (BLKmode, need, BITS_PER_UNIT); ! 1864: frame_size = get_frame_size (); ! 1865: } ! 1866: m88k_stack_size ! 1867: = ROUND_CALL_BLOCK_SIZE (m88k_stack_size + frame_size ! 1868: + current_function_pretend_args_size); ! 1869: } ! 1870: } ! 1871: ! 1872: /* Return true if this function is known to have a null prologue. */ ! 1873: ! 1874: int ! 1875: null_prologue () ! 1876: { ! 1877: if (! reload_completed) ! 1878: return 0; ! 1879: if (! frame_laid_out) ! 1880: m88k_layout_frame (); ! 1881: return (! frame_pointer_needed ! 1882: && nregs == 0 ! 1883: && nxregs == 0 ! 1884: && m88k_stack_size == 0); ! 1885: } ! 1886: ! 1887: /* Determine if the current function has any references to the arg pointer. ! 1888: This is done indirectly by examining the DECL_ARGUMENTS' DECL_RTL. ! 1889: It is OK to return TRUE if there are no references, but FALSE must be ! 1890: correct. */ ! 1891: ! 1892: static int ! 1893: uses_arg_area_p () ! 1894: { ! 1895: register tree parm; ! 1896: ! 1897: if (current_function_decl == 0 ! 1898: || current_function_varargs ! 1899: || variable_args_p) ! 1900: return 1; ! 1901: ! 1902: for (parm = DECL_ARGUMENTS (current_function_decl); ! 1903: parm; ! 1904: parm = TREE_CHAIN (parm)) ! 1905: { ! 1906: if (DECL_RTL (parm) == 0 ! 1907: || GET_CODE (DECL_RTL (parm)) == MEM) ! 1908: return 1; ! 1909: ! 1910: if (DECL_INCOMING_RTL (parm) == 0 ! 1911: || GET_CODE (DECL_INCOMING_RTL (parm)) == MEM) ! 1912: return 1; ! 1913: } ! 1914: return 0; ! 1915: } ! 1916: ! 1917: void ! 1918: m88k_begin_prologue (stream, size) ! 1919: FILE *stream; ! 1920: int size; ! 1921: { ! 1922: m88k_prologue_done = 1; /* it's ok now to put out ln directives */ ! 1923: } ! 1924: ! 1925: void ! 1926: m88k_end_prologue (stream) ! 1927: FILE *stream; ! 1928: { ! 1929: if (TARGET_OCS_DEBUG_INFO && !prologue_marked) ! 1930: { ! 1931: PUT_OCS_FUNCTION_START (stream); ! 1932: prologue_marked = 1; ! 1933: ! 1934: /* If we've already passed the start of the epilogue, say that ! 1935: it starts here. This marks the function as having a null body, ! 1936: but at a point where the return address is in a known location. ! 1937: ! 1938: Originally, I thought this couldn't happen, but the pic prologue ! 1939: for leaf functions ends with the instruction that restores the ! 1940: return address from the temporary register. If the temporary ! 1941: register is never used, that instruction can float all the way ! 1942: to the end of the function. */ ! 1943: if (epilogue_marked) ! 1944: PUT_OCS_FUNCTION_END (stream); ! 1945: } ! 1946: } ! 1947: ! 1948: void ! 1949: m88k_expand_prologue () ! 1950: { ! 1951: m88k_layout_frame (); ! 1952: ! 1953: if (TARGET_OPTIMIZE_ARG_AREA ! 1954: && m88k_stack_size ! 1955: && ! uses_arg_area_p ()) ! 1956: { ! 1957: /* The incoming argument area is used for stack space if it is not ! 1958: used (or if -mno-optimize-arg-area is given). */ ! 1959: if ((m88k_stack_size -= REG_PARM_STACK_SPACE (0)) < 0) ! 1960: m88k_stack_size = 0; ! 1961: } ! 1962: ! 1963: if (m88k_stack_size) ! 1964: emit_add (stack_pointer_rtx, stack_pointer_rtx, -m88k_stack_size); ! 1965: ! 1966: if (nregs || nxregs) ! 1967: preserve_registers (m88k_fp_offset + 4, 1); ! 1968: ! 1969: if (frame_pointer_needed) ! 1970: emit_add (frame_pointer_rtx, stack_pointer_rtx, m88k_fp_offset); ! 1971: ! 1972: if (flag_pic && save_regs[PIC_OFFSET_TABLE_REGNUM]) ! 1973: { ! 1974: rtx return_reg = gen_rtx (REG, SImode, 1); ! 1975: rtx label = gen_label_rtx (); ! 1976: rtx temp_reg; ! 1977: ! 1978: if (! save_regs[1]) ! 1979: { ! 1980: temp_reg = gen_rtx (REG, SImode, TEMP_REGNUM); ! 1981: emit_move_insn (temp_reg, return_reg); ! 1982: } ! 1983: emit_insn (gen_locate1 (pic_offset_table_rtx, label)); ! 1984: emit_insn (gen_locate2 (pic_offset_table_rtx, label)); ! 1985: emit_insn (gen_addsi3 (pic_offset_table_rtx, ! 1986: pic_offset_table_rtx, return_reg)); ! 1987: if (! save_regs[1]) ! 1988: emit_move_insn (return_reg, temp_reg); ! 1989: } ! 1990: if (profile_flag || profile_block_flag) ! 1991: emit_insn (gen_blockage ()); ! 1992: } ! 1993: ! 1994: /* This function generates the assembly code for function exit, ! 1995: on machines that need it. Args are same as for FUNCTION_PROLOGUE. ! 1996: ! 1997: The function epilogue should not depend on the current stack pointer! ! 1998: It should use the frame pointer only, if there is a frame pointer. ! 1999: This is mandatory because of alloca; we also take advantage of it to ! 2000: omit stack adjustments before returning. */ ! 2001: ! 2002: void ! 2003: m88k_begin_epilogue (stream) ! 2004: FILE *stream; ! 2005: { ! 2006: if (TARGET_OCS_DEBUG_INFO && !epilogue_marked && prologue_marked) ! 2007: { ! 2008: PUT_OCS_FUNCTION_END (stream); ! 2009: } ! 2010: epilogue_marked = 1; ! 2011: } ! 2012: ! 2013: void ! 2014: m88k_end_epilogue (stream, size) ! 2015: FILE *stream; ! 2016: int size; ! 2017: { ! 2018: rtx insn = get_last_insn (); ! 2019: ! 2020: if (TARGET_OCS_DEBUG_INFO && !epilogue_marked) ! 2021: PUT_OCS_FUNCTION_END (stream); ! 2022: ! 2023: /* If the last insn isn't a BARRIER, we must write a return insn. This ! 2024: should only happen if the function has no prologe and no body. */ ! 2025: if (GET_CODE (insn) == NOTE) ! 2026: insn = prev_nonnote_insn (insn); ! 2027: if (insn == 0 || GET_CODE (insn) != BARRIER) ! 2028: fprintf (stream, "\tjmp\t %s\n", reg_names[1]); ! 2029: ! 2030: output_short_branch_defs (stream); ! 2031: ! 2032: fprintf (stream, "\n"); ! 2033: ! 2034: if (TARGET_OCS_DEBUG_INFO) ! 2035: output_tdesc (stream, m88k_fp_offset + 4); ! 2036: ! 2037: m88k_function_number++; ! 2038: m88k_prologue_done = 0; /* don't put out ln directives */ ! 2039: variable_args_p = 0; /* has variable args */ ! 2040: frame_laid_out = 0; ! 2041: epilogue_marked = 0; ! 2042: prologue_marked = 0; ! 2043: } ! 2044: ! 2045: void ! 2046: m88k_expand_epilogue () ! 2047: { ! 2048: #if (MONITOR_GCC & 0x4) /* What are interesting prologue/epilogue values? */ ! 2049: fprintf (stream, "; size = %d, m88k_fp_offset = %d, m88k_stack_size = %d\n", ! 2050: size, m88k_fp_offset, m88k_stack_size); ! 2051: #endif ! 2052: ! 2053: if (frame_pointer_needed) ! 2054: emit_add (stack_pointer_rtx, frame_pointer_rtx, -m88k_fp_offset); ! 2055: ! 2056: if (nregs || nxregs) ! 2057: preserve_registers (m88k_fp_offset + 4, 0); ! 2058: ! 2059: if (m88k_stack_size) ! 2060: emit_add (stack_pointer_rtx, stack_pointer_rtx, m88k_stack_size); ! 2061: } ! 2062: ! 2063: /* Emit insns to set DSTREG to SRCREG + AMOUNT during the prologue or ! 2064: epilogue. */ ! 2065: ! 2066: static void ! 2067: emit_add (dstreg, srcreg, amount) ! 2068: rtx dstreg; ! 2069: rtx srcreg; ! 2070: int amount; ! 2071: { ! 2072: rtx incr = gen_rtx (CONST_INT, VOIDmode, abs (amount)); ! 2073: if (! ADD_INTVAL (amount)) ! 2074: { ! 2075: rtx temp = gen_rtx (REG, SImode, TEMP_REGNUM); ! 2076: emit_move_insn (temp, incr); ! 2077: incr = temp; ! 2078: } ! 2079: emit_insn ((amount < 0 ? gen_subsi3 : gen_addsi3) (dstreg, srcreg, incr)); ! 2080: } ! 2081: ! 2082: /* Save/restore the preserve registers. base is the highest offset from ! 2083: r31 at which a register is stored. store_p is true if stores are to ! 2084: be done; otherwise loads. */ ! 2085: ! 2086: static void ! 2087: preserve_registers (base, store_p) ! 2088: int base; ! 2089: int store_p; ! 2090: { ! 2091: int regno, offset; ! 2092: struct mem_op { ! 2093: int regno; ! 2094: int nregs; ! 2095: int offset; ! 2096: } mem_op[FIRST_PSEUDO_REGISTER]; ! 2097: struct mem_op *mo_ptr = mem_op; ! 2098: ! 2099: /* The 88open OCS mandates that preserved registers be stored in ! 2100: increasing order. For compatibility with current practice, ! 2101: the order is r1, r30, then the preserve registers. */ ! 2102: ! 2103: offset = base; ! 2104: if (save_regs[1]) ! 2105: { ! 2106: /* An extra word is given in this case to make best use of double ! 2107: memory ops. */ ! 2108: if (nregs > 2 && !save_regs[FRAME_POINTER_REGNUM]) ! 2109: offset -= 4; ! 2110: emit_ldst (store_p, 1, SImode, offset); ! 2111: offset -= 4; ! 2112: base = offset; ! 2113: } ! 2114: ! 2115: /* Walk the registers to save recording all single memory operations. */ ! 2116: for (regno = FRAME_POINTER_REGNUM; regno > 1; regno--) ! 2117: if (save_regs[regno]) ! 2118: { ! 2119: if ((offset & 7) != 4 || (regno & 1) != 1 || !save_regs[regno-1]) ! 2120: { ! 2121: mo_ptr->nregs = 1; ! 2122: mo_ptr->regno = regno; ! 2123: mo_ptr->offset = offset; ! 2124: mo_ptr++; ! 2125: offset -= 4; ! 2126: } ! 2127: else ! 2128: { ! 2129: regno--; ! 2130: offset -= 2*4; ! 2131: } ! 2132: } ! 2133: ! 2134: /* Walk the registers to save recording all double memory operations. ! 2135: This avoids a delay in the epilogue (ld.d/ld). */ ! 2136: offset = base; ! 2137: for (regno = FRAME_POINTER_REGNUM; regno > 1; regno--) ! 2138: if (save_regs[regno]) ! 2139: { ! 2140: if ((offset & 7) != 4 || (regno & 1) != 1 || !save_regs[regno-1]) ! 2141: { ! 2142: offset -= 4; ! 2143: } ! 2144: else ! 2145: { ! 2146: mo_ptr->nregs = 2; ! 2147: mo_ptr->regno = regno-1; ! 2148: mo_ptr->offset = offset-4; ! 2149: mo_ptr++; ! 2150: regno--; ! 2151: offset -= 2*4; ! 2152: } ! 2153: } ! 2154: ! 2155: /* Walk the extended registers to record all memory operations. */ ! 2156: /* Be sure the offset is double word aligned. */ ! 2157: offset = (offset - 1) & ~7; ! 2158: for (regno = FIRST_PSEUDO_REGISTER - 1; regno > FIRST_EXTENDED_REGISTER; ! 2159: regno--) ! 2160: if (save_regs[regno]) ! 2161: { ! 2162: mo_ptr->nregs = 2; ! 2163: mo_ptr->regno = regno; ! 2164: mo_ptr->offset = offset; ! 2165: mo_ptr++; ! 2166: offset -= 2*4; ! 2167: } ! 2168: ! 2169: mo_ptr->regno = 0; ! 2170: ! 2171: /* Output the memory operations. */ ! 2172: for (mo_ptr = mem_op; mo_ptr->regno; mo_ptr++) ! 2173: { ! 2174: if (mo_ptr->nregs) ! 2175: emit_ldst (store_p, mo_ptr->regno, ! 2176: (mo_ptr->nregs > 1 ? DImode : SImode), ! 2177: mo_ptr->offset); ! 2178: } ! 2179: } ! 2180: ! 2181: static void ! 2182: emit_ldst (store_p, regno, mode, offset) ! 2183: int store_p; ! 2184: int regno; ! 2185: enum machine_mode mode; ! 2186: int offset; ! 2187: { ! 2188: rtx reg = gen_rtx (REG, mode, regno); ! 2189: rtx mem; ! 2190: ! 2191: if (SMALL_INTVAL (offset)) ! 2192: { ! 2193: mem = gen_rtx (MEM, mode, plus_constant (stack_pointer_rtx, offset)); ! 2194: } ! 2195: else ! 2196: { ! 2197: /* offset is too large for immediate index must use register */ ! 2198: ! 2199: rtx disp = gen_rtx (CONST_INT, VOIDmode, offset); ! 2200: rtx temp = gen_rtx (REG, SImode, TEMP_REGNUM); ! 2201: rtx regi = gen_rtx (PLUS, SImode, stack_pointer_rtx, temp); ! 2202: emit_move_insn (temp, disp); ! 2203: mem = gen_rtx (MEM, mode, regi); ! 2204: } ! 2205: ! 2206: if (store_p) ! 2207: emit_move_insn (mem, reg); ! 2208: else ! 2209: emit_move_insn (reg, mem); ! 2210: } ! 2211: ! 2212: /* Convert the address expression REG to a CFA offset. */ ! 2213: ! 2214: int ! 2215: m88k_debugger_offset (reg, offset) ! 2216: register rtx reg; ! 2217: register int offset; ! 2218: { ! 2219: if (GET_CODE (reg) == PLUS) ! 2220: { ! 2221: offset = INTVAL (XEXP (reg, 1)); ! 2222: reg = XEXP (reg, 0); ! 2223: } ! 2224: ! 2225: /* Put the offset in terms of the CFA (arg pointer). */ ! 2226: if (reg == frame_pointer_rtx) ! 2227: offset += m88k_fp_offset - m88k_stack_size; ! 2228: else if (reg == stack_pointer_rtx) ! 2229: offset -= m88k_stack_size; ! 2230: else if (reg != arg_pointer_rtx) ! 2231: { ! 2232: #if (MONITOR_GCC & 0x10) /* Watch for suspicious symbolic locations. */ ! 2233: if (! (GET_CODE (reg) == REG ! 2234: && REGNO (reg) >= FIRST_PSEUDO_REGISTER)) ! 2235: warning ("Internal gcc error: Can't express symbolic location"); ! 2236: #endif ! 2237: return 0; ! 2238: } ! 2239: ! 2240: return offset; ! 2241: } ! 2242: ! 2243: /* Output the 88open OCS proscribed text description information. ! 2244: The information is: ! 2245: 0 8: zero ! 2246: 0 22: info-byte-length (16 or 20 bytes) ! 2247: 0 2: info-alignment (word 2) ! 2248: 1 32: info-protocol (version 1 or 2(pic)) ! 2249: 2 32: starting-address (inclusive, not counting prologue) ! 2250: 3 32: ending-address (exclusive, not counting epilog) ! 2251: 4 8: info-variant (version 1 or 3(extended registers)) ! 2252: 4 17: register-save-mask (from register 14 to 30) ! 2253: 4 1: zero ! 2254: 4 1: return-address-info-discriminant ! 2255: 4 5: frame-address-register ! 2256: 5 32: frame-address-offset ! 2257: 6 32: return-address-info ! 2258: 7 32: register-save-offset ! 2259: 8 16: extended-register-save-mask (x16 - x31) ! 2260: 8 16: extended-register-save-offset (WORDS from register-save-offset) */ ! 2261: ! 2262: static void ! 2263: output_tdesc (file, offset) ! 2264: FILE *file; ! 2265: int offset; ! 2266: { ! 2267: int regno, i, j; ! 2268: long mask, return_address_info, register_save_offset; ! 2269: long xmask, xregister_save_offset; ! 2270: char buf[256]; ! 2271: ! 2272: for (mask = 0, i = 0, regno = FIRST_OCS_PRESERVE_REGISTER; ! 2273: regno <= LAST_OCS_PRESERVE_REGISTER; ! 2274: regno++) ! 2275: { ! 2276: mask <<= 1; ! 2277: if (save_regs[regno]) ! 2278: { ! 2279: mask |= 1; ! 2280: i++; ! 2281: } ! 2282: } ! 2283: ! 2284: for (xmask = 0, j = 0, regno = FIRST_OCS_EXTENDED_PRESERVE_REGISTER; ! 2285: regno <= LAST_OCS_EXTENDED_PRESERVE_REGISTER; ! 2286: regno++) ! 2287: { ! 2288: xmask <<= 1; ! 2289: if (save_regs[regno]) ! 2290: { ! 2291: xmask |= 1; ! 2292: j++; ! 2293: } ! 2294: } ! 2295: ! 2296: if (save_regs[1]) ! 2297: { ! 2298: if ((nxregs > 0 || nregs > 2) && !save_regs[FRAME_POINTER_REGNUM]) ! 2299: offset -= 4; ! 2300: return_address_info = - m88k_stack_size + offset; ! 2301: register_save_offset = return_address_info - i*4; ! 2302: } ! 2303: else ! 2304: { ! 2305: return_address_info = 1; ! 2306: register_save_offset = - m88k_stack_size + offset + 4 - i*4; ! 2307: } ! 2308: ! 2309: xregister_save_offset = - (j * 2 + ((register_save_offset >> 2) & 1)); ! 2310: ! 2311: tdesc_section (); ! 2312: ! 2313: fprintf (file, "\t%s\t %d,%d", INT_ASM_OP, /* 8:0,22:(20 or 16),2:2 */ ! 2314: (((xmask != 0) ? 20 : 16) << 2) | 2, ! 2315: flag_pic ? 2 : 1); ! 2316: ! 2317: ASM_GENERATE_INTERNAL_LABEL (buf, OCS_START_PREFIX, m88k_function_number); ! 2318: fprintf (file, ",%s%s", buf+1, flag_pic ? "#rel" : ""); ! 2319: ASM_GENERATE_INTERNAL_LABEL (buf, OCS_END_PREFIX, m88k_function_number); ! 2320: fprintf (file, ",%s%s", buf+1, flag_pic ? "#rel" : ""); ! 2321: ! 2322: fprintf (file, ",0x%x,0x%x,0x%x,0x%x", ! 2323: /* 8:1,17:0x%.3x,1:0,1:%d,5:%d */ ! 2324: (((xmask ? 3 : 1) << (17+1+1+5)) ! 2325: | (mask << (1+1+5)) ! 2326: | ((!!save_regs[1]) << 5) ! 2327: | (frame_pointer_needed ! 2328: ? FRAME_POINTER_REGNUM ! 2329: : STACK_POINTER_REGNUM)), ! 2330: (m88k_stack_size - (frame_pointer_needed ? m88k_fp_offset : 0)), ! 2331: return_address_info, ! 2332: register_save_offset); ! 2333: if (xmask) ! 2334: fprintf (file, ",0x%x%04x", xmask, (0xffff & xregister_save_offset)); ! 2335: fputc ('\n', file); ! 2336: ! 2337: text_section (); ! 2338: } ! 2339: ! 2340: /* Output assembler code to FILE to increment profiler label # LABELNO ! 2341: for profiling a function entry. NAME is the mcount function name ! 2342: (varies), SAVEP indicates whether the parameter registers need to ! 2343: be saved and restored. */ ! 2344: ! 2345: void ! 2346: output_function_profiler (file, labelno, name, savep) ! 2347: FILE *file; ! 2348: int labelno; ! 2349: char *name; ! 2350: int savep; ! 2351: { ! 2352: char label[256]; ! 2353: char dbi[256]; ! 2354: char *temp = (savep ? reg_names[2] : reg_names[10]); ! 2355: ! 2356: /* Remember to update FUNCTION_PROFILER_LENGTH. */ ! 2357: ! 2358: if (savep) ! 2359: { ! 2360: fprintf (file, "\tsubu\t %s,%s,64\n", reg_names[31], reg_names[31]); ! 2361: fprintf (file, "\tst.d\t %s,%s,32\n", reg_names[2], reg_names[31]); ! 2362: fprintf (file, "\tst.d\t %s,%s,40\n", reg_names[4], reg_names[31]); ! 2363: fprintf (file, "\tst.d\t %s,%s,48\n", reg_names[6], reg_names[31]); ! 2364: fprintf (file, "\tst.d\t %s,%s,56\n", reg_names[8], reg_names[31]); ! 2365: } ! 2366: ! 2367: ASM_GENERATE_INTERNAL_LABEL (label, "LP", labelno); ! 2368: if (flag_pic == 2) ! 2369: { ! 2370: fprintf (file, "\tor.u\t %s,%s,%shi16(%s#got_rel)\n", ! 2371: temp, reg_names[0], m88k_pound_sign, &label[1]); ! 2372: fprintf (file, "\tor\t %s,%s,%slo16(%s#got_rel)\n", ! 2373: temp, temp, m88k_pound_sign, &label[1]); ! 2374: sprintf (dbi, "\tld\t %s,%s,%s\n", temp, ! 2375: reg_names[PIC_OFFSET_TABLE_REGNUM], temp); ! 2376: } ! 2377: else if (flag_pic) ! 2378: { ! 2379: sprintf (dbi, "\tld\t %s,%s,%s#got_rel\n", temp, ! 2380: reg_names[PIC_OFFSET_TABLE_REGNUM], &label[1]); ! 2381: } ! 2382: else ! 2383: { ! 2384: fprintf (file, "\tor.u\t %s,%s,%shi16(%s)\n", ! 2385: temp, reg_names[0], m88k_pound_sign, &label[1]); ! 2386: sprintf (dbi, "\tor\t %s,%s,%slo16(%s)\n", ! 2387: temp, temp, m88k_pound_sign, &label[1]); ! 2388: } ! 2389: ! 2390: if (flag_pic) ! 2391: fprintf (file, "\tbsr.n\t %s#plt\n", name); ! 2392: else ! 2393: fprintf (file, "\tbsr.n\t %s\n", name); ! 2394: fputs (dbi, file); ! 2395: ! 2396: if (savep) ! 2397: { ! 2398: fprintf (file, "\tld.d\t %s,%s,32\n", reg_names[2], reg_names[31]); ! 2399: fprintf (file, "\tld.d\t %s,%s,40\n", reg_names[4], reg_names[31]); ! 2400: fprintf (file, "\tld.d\t %s,%s,48\n", reg_names[6], reg_names[31]); ! 2401: fprintf (file, "\tld.d\t %s,%s,56\n", reg_names[8], reg_names[31]); ! 2402: fprintf (file, "\taddu\t %s,%s,64\n", reg_names[31], reg_names[31]); ! 2403: } ! 2404: } ! 2405: ! 2406: /* Output assembler code to FILE to initialize basic-block profiling for ! 2407: the current module. LABELNO is unique to each instance. */ ! 2408: ! 2409: void ! 2410: output_function_block_profiler (file, labelno) ! 2411: FILE *file; ! 2412: int labelno; ! 2413: { ! 2414: char block[256]; ! 2415: char label[256]; ! 2416: ! 2417: /* Remember to update FUNCTION_BLOCK_PROFILER_LENGTH. */ ! 2418: ! 2419: ASM_GENERATE_INTERNAL_LABEL (block, "LPBX", 0); ! 2420: ASM_GENERATE_INTERNAL_LABEL (label, "LPY", labelno); ! 2421: ! 2422: /* @@ Need to deal with PIC. I'm not sure what the requirements are on ! 2423: register usage, so I used r26/r27 to be safe. */ ! 2424: fprintf (file, "\tor.u\t %s,%s,%shi16(%s)\n", reg_names[27], reg_names[0], ! 2425: m88k_pound_sign, &block[1]); ! 2426: fprintf (file, "\tld\t %s,%s,%slo16(%s)\n", reg_names[26], reg_names[27], ! 2427: m88k_pound_sign, &block[1]); ! 2428: fprintf (file, "\tbcnd\t %sne0,%s,%s\n", ! 2429: m88k_pound_sign, reg_names[26], &label[1]); ! 2430: fprintf (file, "\tsubu\t %s,%s,64\n", reg_names[31], reg_names[31]); ! 2431: fprintf (file, "\tst.d\t %s,%s,32\n", reg_names[2], reg_names[31]); ! 2432: fprintf (file, "\tst.d\t %s,%s,40\n", reg_names[4], reg_names[31]); ! 2433: fprintf (file, "\tst.d\t %s,%s,48\n", reg_names[6], reg_names[31]); ! 2434: fprintf (file, "\tst.d\t %s,%s,56\n", reg_names[8], reg_names[31]); ! 2435: fputs ("\tbsr.n\t ", file); ! 2436: ASM_OUTPUT_LABELREF (file, "__bb_init_func"); ! 2437: putc ('\n', file); ! 2438: fprintf (file, "\tor\t %s,%s,%slo16(%s)\n", reg_names[2], reg_names[27], ! 2439: m88k_pound_sign, &block[1]); ! 2440: fprintf (file, "\tld.d\t %s,%s,32\n", reg_names[2], reg_names[31]); ! 2441: fprintf (file, "\tld.d\t %s,%s,40\n", reg_names[4], reg_names[31]); ! 2442: fprintf (file, "\tld.d\t %s,%s,48\n", reg_names[6], reg_names[31]); ! 2443: fprintf (file, "\tld.d\t %s,%s,56\n", reg_names[8], reg_names[31]); ! 2444: fprintf (file, "\taddu\t %s,%s,64\n", reg_names[31], reg_names[31]); ! 2445: ASM_OUTPUT_INTERNAL_LABEL (file, "LPY", labelno); ! 2446: } ! 2447: ! 2448: /* Output assembler code to FILE to increment the count associated with ! 2449: the basic block number BLOCKNO. */ ! 2450: ! 2451: void ! 2452: output_block_profiler (file, blockno) ! 2453: FILE *file; ! 2454: int blockno; ! 2455: { ! 2456: char block[256]; ! 2457: ! 2458: /* Remember to update BLOCK_PROFILER_LENGTH. */ ! 2459: ! 2460: ASM_GENERATE_INTERNAL_LABEL (block, "LPBX", 2); ! 2461: ! 2462: /* @@ Need to deal with PIC. I'm not sure what the requirements are on ! 2463: register usage, so I used r26/r27 to be safe. */ ! 2464: fprintf (file, "\tor.u\t %s,%s,%shi16(%s+%d)\n", reg_names[27], reg_names[0], ! 2465: m88k_pound_sign, &block[1], 4 * blockno); ! 2466: fprintf (file, "\tld\t %s,%s,%slo16(%s+%d)\n", reg_names[26], reg_names[27], ! 2467: m88k_pound_sign, &block[1], 4 * blockno); ! 2468: fprintf (file, "\taddu\t %s,%s,1\n", reg_names[26], reg_names[26]); ! 2469: fprintf (file, "\tst\t %s,%s,%slo16(%s+%d)\n", reg_names[26], reg_names[27], ! 2470: m88k_pound_sign, &block[1], 4 * blockno); ! 2471: } ! 2472: ! 2473: /* Determine whether a function argument is passed in a register, and ! 2474: which register. ! 2475: ! 2476: The arguments are CUM, which summarizes all the previous ! 2477: arguments; MODE, the machine mode of the argument; TYPE, ! 2478: the data type of the argument as a tree node or 0 if that is not known ! 2479: (which happens for C support library functions); and NAMED, ! 2480: which is 1 for an ordinary argument and 0 for nameless arguments that ! 2481: correspond to `...' in the called function's prototype. ! 2482: ! 2483: The value of the expression should either be a `reg' RTX for the ! 2484: hard register in which to pass the argument, or zero to pass the ! 2485: argument on the stack. ! 2486: ! 2487: On the m88000 the first eight words of args are normally in registers ! 2488: and the rest are pushed. Double precision floating point must be ! 2489: double word aligned (and if in a register, starting on an even ! 2490: register). Structures and unions which are not 4 byte, and word ! 2491: aligned are passed in memory rather than registers, even if they ! 2492: would fit completely in the registers under OCS rules. ! 2493: ! 2494: Note that FUNCTION_ARG and FUNCTION_INCOMING_ARG were different. ! 2495: For structures that are passed in memory, but could have been ! 2496: passed in registers, we first load the structure into the ! 2497: register, and then when the last argument is passed, we store ! 2498: the registers into the stack locations. This fixes some bugs ! 2499: where GCC did not expect to have register arguments, followed ! 2500: by stack arguments, followed by register arguments. */ ! 2501: ! 2502: struct rtx_def * ! 2503: m88k_function_arg (args_so_far, mode, type, named) ! 2504: CUMULATIVE_ARGS args_so_far; ! 2505: enum machine_mode mode; ! 2506: tree type; ! 2507: int named; ! 2508: { ! 2509: int bytes, words; ! 2510: ! 2511: if (type != 0 /* undo putting struct in register */ ! 2512: && (TREE_CODE (type) == RECORD_TYPE || TREE_CODE (type) == UNION_TYPE)) ! 2513: mode = BLKmode; ! 2514: ! 2515: if (mode == BLKmode && TARGET_WARN_PASS_STRUCT) ! 2516: warning ("argument #%d is a structure", args_so_far + 1); ! 2517: ! 2518: if ((args_so_far & 1) != 0 ! 2519: && (mode == DImode || mode == DFmode ! 2520: || (type != 0 && TYPE_ALIGN (type) > 32))) ! 2521: args_so_far++; ! 2522: ! 2523: #ifdef ESKIT ! 2524: if (no_reg_params) ! 2525: return (rtx) 0; /* don't put args in registers */ ! 2526: #endif ! 2527: ! 2528: if (type == 0 && mode == BLKmode) ! 2529: abort (); /* m88k_function_arg argument `type' is NULL for BLKmode. */ ! 2530: ! 2531: bytes = (mode != BLKmode) ? GET_MODE_SIZE (mode) : int_size_in_bytes (type); ! 2532: words = (bytes + 3) / 4; ! 2533: ! 2534: if (args_so_far + words > 8) ! 2535: return (rtx) 0; /* args have exhausted registers */ ! 2536: ! 2537: else if (mode == BLKmode ! 2538: && (TYPE_ALIGN (type) != BITS_PER_WORD ! 2539: || bytes != UNITS_PER_WORD)) ! 2540: return (rtx) 0; ! 2541: ! 2542: return gen_rtx (REG, ! 2543: ((mode == BLKmode) ? TYPE_MODE (type) : mode), ! 2544: 2 + args_so_far); ! 2545: } ! 2546: ! 2547: /* Do what is necessary for `va_start'. The argument is ignored; ! 2548: We look at the current function to determine if stdargs or varargs ! 2549: is used and fill in an initial va_list. A pointer to this constructor ! 2550: is returned. */ ! 2551: ! 2552: struct rtx_def * ! 2553: m88k_builtin_saveregs (arglist) ! 2554: tree arglist; ! 2555: { ! 2556: rtx block, addr, argsize; ! 2557: tree fntype = TREE_TYPE (current_function_decl); ! 2558: int argadj = ((!(TYPE_ARG_TYPES (fntype) != 0 ! 2559: && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) ! 2560: != void_type_node))) ! 2561: ? -UNITS_PER_WORD : 0) + UNITS_PER_WORD - 1; ! 2562: int fixed; ! 2563: variable_args_p = 1; ! 2564: ! 2565: if (CONSTANT_P (current_function_arg_offset_rtx)) ! 2566: { ! 2567: fixed = (XINT (current_function_arg_offset_rtx, 0) ! 2568: + argadj) / UNITS_PER_WORD; ! 2569: argsize = gen_rtx (CONST_INT, VOIDmode, fixed); ! 2570: } ! 2571: else ! 2572: { ! 2573: fixed = 0; ! 2574: argsize = plus_constant (current_function_arg_offset_rtx, argadj); ! 2575: argsize = expand_shift (RSHIFT_EXPR, Pmode, argsize, ! 2576: build_int_2 (2, 0), argsize, 0); ! 2577: } ! 2578: ! 2579: /* Allocate the va_list constructor */ ! 2580: block = assign_stack_local (BLKmode, 3 * UNITS_PER_WORD, BITS_PER_WORD); ! 2581: RTX_UNCHANGING_P (block) = 1; ! 2582: RTX_UNCHANGING_P (XEXP (block, 0)) = 1; ! 2583: ! 2584: /* Store the argsize as the __va_arg member. */ ! 2585: emit_move_insn (change_address (block, SImode, XEXP (block, 0)), ! 2586: argsize); ! 2587: ! 2588: /* Store the arg pointer in the __va_stk member. */ ! 2589: emit_move_insn (change_address (block, Pmode, ! 2590: plus_constant (XEXP (block, 0), ! 2591: UNITS_PER_WORD)), ! 2592: copy_to_reg (virtual_incoming_args_rtx)); ! 2593: ! 2594: /* Allocate the register space, and store it as the __va_reg member. */ ! 2595: addr = assign_stack_local (BLKmode, 8 * UNITS_PER_WORD, -1); ! 2596: MEM_IN_STRUCT_P (addr) = 1; ! 2597: RTX_UNCHANGING_P (addr) = 1; ! 2598: RTX_UNCHANGING_P (XEXP (addr, 0)) = 1; ! 2599: emit_move_insn (change_address (block, Pmode, ! 2600: plus_constant (XEXP (block, 0), ! 2601: 2 * UNITS_PER_WORD)), ! 2602: copy_to_reg (XEXP (addr, 0))); ! 2603: ! 2604: /* Now store the incoming registers. */ ! 2605: if (fixed < 8) ! 2606: move_block_from_reg ! 2607: (2 + fixed, ! 2608: change_address (addr, Pmode, ! 2609: plus_constant (XEXP (addr, 0), ! 2610: fixed * UNITS_PER_WORD)), ! 2611: 8 - fixed, (8 - fixed) * UNITS_PER_WORD); ! 2612: ! 2613: /* Return the address of the va_list constructor, but don't put it in a ! 2614: register. This fails when not optimizing and produces worse code when ! 2615: optimizing. */ ! 2616: return XEXP (block, 0); ! 2617: } ! 2618: ! 2619: /* If cmpsi has not been generated, emit code to do the test. Return the ! 2620: expression describing the test of operator OP. */ ! 2621: ! 2622: rtx ! 2623: emit_test (op, mode) ! 2624: enum rtx_code op; ! 2625: enum machine_mode mode; ! 2626: { ! 2627: if (m88k_compare_reg == 0) ! 2628: emit_insn (gen_test (m88k_compare_op0, m88k_compare_op1)); ! 2629: return (gen_rtx (op, mode, m88k_compare_reg, const0_rtx)); ! 2630: } ! 2631: ! 2632: /* Determine how to best perform cmpsi/bxx, where cmpsi has a constant ! 2633: operand. All tests with zero (albeit swapped) and all equality tests ! 2634: with a constant are done with bcnd. The remaining cases are swapped ! 2635: as needed. */ ! 2636: ! 2637: void ! 2638: emit_bcnd (op, label) ! 2639: enum rtx_code op; ! 2640: rtx label; ! 2641: { ! 2642: if (m88k_compare_op1 == const0_rtx) ! 2643: emit_jump_insn (optimize ! 2644: ? gen_bxx (emit_test (op, VOIDmode), label) ! 2645: : gen_bcnd (gen_rtx (op, VOIDmode, ! 2646: m88k_compare_op0, const0_rtx), ! 2647: label)); ! 2648: else if (m88k_compare_op0 == const0_rtx) ! 2649: emit_jump_insn (optimize ! 2650: ? gen_bxx (emit_test (op, VOIDmode), label) ! 2651: : gen_bcnd (gen_rtx (swap_condition (op), VOIDmode, ! 2652: m88k_compare_op1, const0_rtx), ! 2653: label)); ! 2654: else if (op != EQ && op != NE) ! 2655: emit_jump_insn (gen_bxx (emit_test (op, VOIDmode), label)); ! 2656: else ! 2657: { ! 2658: rtx zero = gen_reg_rtx (SImode); ! 2659: rtx reg, constant; ! 2660: int value; ! 2661: ! 2662: if (GET_CODE (m88k_compare_op1) == CONST_INT) ! 2663: { ! 2664: reg = force_reg (SImode, m88k_compare_op0); ! 2665: constant = m88k_compare_op1; ! 2666: } ! 2667: else ! 2668: { ! 2669: reg = force_reg (SImode, m88k_compare_op1); ! 2670: constant = m88k_compare_op0; ! 2671: } ! 2672: value = INTVAL (constant); ! 2673: ! 2674: /* Perform an arithmetic computation to make the compared-to value ! 2675: zero, but avoid loosing if the bcnd is later changed into sxx. */ ! 2676: if (SMALL_INTVAL (value)) ! 2677: emit_jump_insn (gen_bxx (emit_test (op, VOIDmode), label)); ! 2678: else ! 2679: { ! 2680: if (SMALL_INTVAL (-value)) ! 2681: emit_insn (gen_addsi3 (zero, reg, ! 2682: gen_rtx (CONST_INT, VOIDmode, -value))); ! 2683: else ! 2684: emit_insn (gen_xorsi3 (zero, reg, constant)); ! 2685: ! 2686: emit_jump_insn (gen_bcnd (gen_rtx (op, VOIDmode, ! 2687: zero, const0_rtx), ! 2688: label)); ! 2689: } ! 2690: } ! 2691: } ! 2692: ! 2693: /* Print an operand. Recognize special options, documented below. */ ! 2694: ! 2695: void ! 2696: print_operand (file, x, code) ! 2697: FILE *file; ! 2698: rtx x; ! 2699: char code; ! 2700: { ! 2701: enum rtx_code xc = (x ? GET_CODE (x) : UNKNOWN); ! 2702: register int value = (xc == CONST_INT ? INTVAL (x) : 0); ! 2703: static int sequencep; ! 2704: static int reversep; ! 2705: ! 2706: if (sequencep) ! 2707: { ! 2708: if (code < 'B' || code > 'E') ! 2709: output_operand_lossage ("%R not followed by %B/C/D/E"); ! 2710: if (reversep) ! 2711: xc = reverse_condition (xc); ! 2712: sequencep = 0; ! 2713: } ! 2714: ! 2715: switch (code) ! 2716: { ! 2717: case '*': /* addressing base register for PIC */ ! 2718: fputs (reg_names[PIC_OFFSET_TABLE_REGNUM], file); return; ! 2719: ! 2720: case '#': /* SVR4 pound-sign syntax character (empty if SVR3) */ ! 2721: fputs (m88k_pound_sign, file); return; ! 2722: ! 2723: case 'V': /* Output a serializing instruction as needed if the operand ! 2724: (assumed to be a MEM) is a volatile load. */ ! 2725: case 'v': /* ditto for a volatile store. */ ! 2726: if (MEM_VOLATILE_P (x) && TARGET_SERIALIZE_VOLATILE) ! 2727: { ! 2728: /* The m88110 implements two FIFO queues, one for loads and ! 2729: one for stores. These queues mean that loads complete in ! 2730: their issue order as do stores. An interaction between the ! 2731: history buffer and the store reservation station ensures ! 2732: that a store will not bypass load. Finally, a load will not ! 2733: bypass store, but only when they reference the same address. ! 2734: ! 2735: To avoid this reordering (a load bypassing a store) for ! 2736: volatile references, a serializing instruction is output. ! 2737: We choose the fldcr instruction as it does not serialize on ! 2738: the m88100 so that -m88000 code will not be degraded. ! 2739: ! 2740: The mechanism below is completed by having CC_STATUS_INIT set ! 2741: the code to the unknown value. */ ! 2742: ! 2743: /* ! 2744: hassey 6/30/93 ! 2745: A problem with 88110 4.1 & 4.2 makes the use of fldcr for ! 2746: this purpose undesirable. Instead we will use tb1, this will ! 2747: cause serialization on the 88100 but such is life. ! 2748: */ ! 2749: ! 2750: static rtx last_addr = 0; ! 2751: if (code == 'V' /* Only need to serialize before a load. */ ! 2752: && m88k_volatile_code != 'V' /* Loads complete in FIFO order. */ ! 2753: && !(m88k_volatile_code == 'v' ! 2754: && GET_CODE (XEXP (x, 0)) == LO_SUM ! 2755: && rtx_equal_p (XEXP (XEXP (x, 0), 1), last_addr))) ! 2756: fprintf (file, ! 2757: #if 0 ! 2758: #ifdef AS_BUG_FLDCR ! 2759: "fldcr\t %s,%scr63\n\t", ! 2760: #else ! 2761: "fldcr\t %s,%sfcr63\n\t", ! 2762: #endif ! 2763: reg_names[0], m88k_pound_sign); ! 2764: #else /* 0 */ ! 2765: "tb1\t 1,%s,0xff\n\t", reg_names[0]); ! 2766: #endif /* 0 */ ! 2767: m88k_volatile_code = code; ! 2768: last_addr = (GET_CODE (XEXP (x, 0)) == LO_SUM ! 2769: ? XEXP (XEXP (x, 0), 1) : 0); ! 2770: } ! 2771: return; ! 2772: ! 2773: case 'X': /* print the upper 16 bits... */ ! 2774: value >>= 16; ! 2775: case 'x': /* print the lower 16 bits of the integer constant in hex */ ! 2776: if (xc != CONST_INT) ! 2777: output_operand_lossage ("invalid %x/X value"); ! 2778: fprintf (file, "0x%x", value & 0xffff); return; ! 2779: ! 2780: case 'H': /* print the low 16 bits of the negated integer constant */ ! 2781: if (xc != CONST_INT) ! 2782: output_operand_lossage ("invalid %H value"); ! 2783: value = -value; ! 2784: case 'h': /* print the register or low 16 bits of the integer constant */ ! 2785: if (xc == REG) ! 2786: goto reg; ! 2787: if (xc != CONST_INT) ! 2788: output_operand_lossage ("invalid %h value"); ! 2789: fprintf (file, "%d", value & 0xffff); ! 2790: return; ! 2791: ! 2792: case 'Q': /* print the low 8 bits of the negated integer constant */ ! 2793: if (xc != CONST_INT) ! 2794: output_operand_lossage ("invalid %Q value"); ! 2795: value = -value; ! 2796: case 'q': /* print the register or low 8 bits of the integer constant */ ! 2797: if (xc == REG) ! 2798: goto reg; ! 2799: if (xc != CONST_INT) ! 2800: output_operand_lossage ("invalid %q value"); ! 2801: fprintf (file, "%d", value & 0xff); ! 2802: return; ! 2803: ! 2804: case 'w': /* print the integer constant (X == 32 ? 0 : 32 - X) */ ! 2805: if (xc != CONST_INT) ! 2806: output_operand_lossage ("invalid %o value"); ! 2807: fprintf (file, "%d", value == 32 ? 0 : 32 - value); ! 2808: return; ! 2809: ! 2810: case 'p': /* print the logarithm of the integer constant */ ! 2811: if (xc != CONST_INT ! 2812: || (value = exact_log2 (value)) < 0) ! 2813: output_operand_lossage ("invalid %p value"); ! 2814: fprintf (file, "%d", value); ! 2815: return; ! 2816: ! 2817: case 'S': /* compliment the value and then... */ ! 2818: value = ~value; ! 2819: case 's': /* print the width and offset values forming the integer ! 2820: constant with a SET instruction. See integer_ok_for_set. */ ! 2821: { ! 2822: register unsigned mask, uval = value; ! 2823: register int top, bottom; ! 2824: ! 2825: if (xc != CONST_INT) ! 2826: output_operand_lossage ("invalid %s/S value"); ! 2827: /* All the "one" bits must be contiguous. If so, MASK will be ! 2828: a power of two or zero. */ ! 2829: mask = (uval | (uval - 1)) + 1; ! 2830: if (!(uval && POWER_OF_2_or_0 (mask))) ! 2831: output_operand_lossage ("invalid %s/S value"); ! 2832: top = mask ? exact_log2 (mask) : 32; ! 2833: bottom = exact_log2 (uval & ~(uval - 1)); ! 2834: fprintf (file,"%d<%d>", top - bottom, bottom); ! 2835: return; ! 2836: } ! 2837: ! 2838: case 'P': /* print nothing if pc_rtx; output label_ref */ ! 2839: if (xc == LABEL_REF) ! 2840: output_addr_const (file, x); ! 2841: else if (xc != PC) ! 2842: output_operand_lossage ("invalid %P operand"); ! 2843: return; ! 2844: ! 2845: case 'L': /* print 0 or 1 if operand is label_ref and then... */ ! 2846: fputc (xc == LABEL_REF ? '1' : '0', file); ! 2847: case '.': /* print .n if delay slot is used */ ! 2848: fputs ((final_sequence ! 2849: && ! INSN_ANNULLED_BRANCH_P (XVECEXP (final_sequence, 0, 0))) ! 2850: ? ".n\t" : "\t", file); ! 2851: return; ! 2852: ! 2853: case '!': /* Reverse the following condition. */ ! 2854: sequencep++; ! 2855: reversep = 1; ! 2856: return; ! 2857: case 'R': /* reverse the condition of the next print_operand ! 2858: if operand is a label_ref. */ ! 2859: sequencep++; ! 2860: reversep = (xc == LABEL_REF); ! 2861: return; ! 2862: ! 2863: case 'B': /* bcnd branch values */ ! 2864: fputs (m88k_pound_sign, file); ! 2865: switch (xc) ! 2866: { ! 2867: case EQ: fputs ("eq0", file); return; ! 2868: case NE: fputs ("ne0", file); return; ! 2869: case GT: fputs ("gt0", file); return; ! 2870: case LE: fputs ("le0", file); return; ! 2871: case LT: fputs ("lt0", file); return; ! 2872: case GE: fputs ("ge0", file); return; ! 2873: default: output_operand_lossage ("invalid %B value"); ! 2874: } ! 2875: ! 2876: case 'C': /* bb0/bb1 branch values for comparisons */ ! 2877: fputs (m88k_pound_sign, file); ! 2878: switch (xc) ! 2879: { ! 2880: case EQ: fputs ("eq", file); return; ! 2881: case NE: fputs ("ne", file); return; ! 2882: case GT: fputs ("gt", file); return; ! 2883: case LE: fputs ("le", file); return; ! 2884: case LT: fputs ("lt", file); return; ! 2885: case GE: fputs ("ge", file); return; ! 2886: case GTU: fputs ("hi", file); return; ! 2887: case LEU: fputs ("ls", file); return; ! 2888: case LTU: fputs ("lo", file); return; ! 2889: case GEU: fputs ("hs", file); return; ! 2890: default: output_operand_lossage ("invalid %C value"); ! 2891: } ! 2892: ! 2893: case 'D': /* bcnd branch values for float comparisons */ ! 2894: switch (xc) ! 2895: { ! 2896: case EQ: fputs ("0xa", file); return; ! 2897: case NE: fputs ("0x5", file); return; ! 2898: case GT: fputs (m88k_pound_sign, file); ! 2899: fputs ("gt0", file); return; ! 2900: case LE: fputs ("0xe", file); return; ! 2901: case LT: fputs ("0x4", file); return; ! 2902: case GE: fputs ("0xb", file); return; ! 2903: default: output_operand_lossage ("invalid %D value"); ! 2904: } ! 2905: ! 2906: case 'E': /* bcnd branch values for special integers */ ! 2907: switch (xc) ! 2908: { ! 2909: case EQ: fputs ("0x8", file); return; ! 2910: case NE: fputs ("0x7", file); return; ! 2911: default: output_operand_lossage ("invalid %E value"); ! 2912: } ! 2913: ! 2914: case 'd': /* second register of a two register pair */ ! 2915: if (xc != REG) ! 2916: output_operand_lossage ("`%d' operand isn't a register"); ! 2917: fputs (reg_names[REGNO (x) + 1], file); ! 2918: return; ! 2919: ! 2920: case 'r': /* an immediate 0 should be represented as `r0' */ ! 2921: if (x == const0_rtx) ! 2922: { ! 2923: fputs (reg_names[0], file); ! 2924: return; ! 2925: } ! 2926: else if (xc != REG) ! 2927: output_operand_lossage ("invalid %r value"); ! 2928: case 0: ! 2929: name: ! 2930: if (xc == REG) ! 2931: { ! 2932: reg: ! 2933: if (REGNO (x) == ARG_POINTER_REGNUM) ! 2934: output_operand_lossage ("operand is r0"); ! 2935: else ! 2936: fputs (reg_names[REGNO (x)], file); ! 2937: } ! 2938: else if (xc == PLUS) ! 2939: output_address (x); ! 2940: else if (xc == MEM) ! 2941: output_address (XEXP (x, 0)); ! 2942: else if (xc == CONST_DOUBLE) ! 2943: output_operand_lossage ("operand is const_double"); ! 2944: else ! 2945: output_addr_const (file, x); ! 2946: return; ! 2947: ! 2948: case 'g': /* append #got_rel as needed */ ! 2949: if (flag_pic && (xc == SYMBOL_REF || xc == LABEL_REF)) ! 2950: { ! 2951: output_addr_const (file, x); ! 2952: fputs ("#got_rel", file); ! 2953: return; ! 2954: } ! 2955: goto name; ! 2956: ! 2957: case 'a': /* (standard), assume operand is an address */ ! 2958: case 'c': /* (standard), assume operand is an immediate value */ ! 2959: case 'l': /* (standard), assume operand is a label_ref */ ! 2960: case 'n': /* (standard), like %c, except negate first */ ! 2961: default: ! 2962: output_operand_lossage ("invalid code"); ! 2963: } ! 2964: } ! 2965: ! 2966: void ! 2967: print_operand_address (file, addr) ! 2968: FILE *file; ! 2969: rtx addr; ! 2970: { ! 2971: register rtx reg0, reg1, temp; ! 2972: ! 2973: switch (GET_CODE (addr)) ! 2974: { ! 2975: case REG: ! 2976: if (REGNO (addr) == ARG_POINTER_REGNUM) ! 2977: abort (); ! 2978: else ! 2979: fprintf (file, "%s,%s", reg_names[0], reg_names [REGNO (addr)]); ! 2980: break; ! 2981: ! 2982: case LO_SUM: ! 2983: fprintf (file, "%s,%slo16(", ! 2984: reg_names[REGNO (XEXP (addr, 0))], m88k_pound_sign); ! 2985: output_addr_const (file, XEXP (addr, 1)); ! 2986: fputc (')', file); ! 2987: break; ! 2988: ! 2989: case PLUS: ! 2990: reg0 = XEXP (addr, 0); ! 2991: reg1 = XEXP (addr, 1); ! 2992: if (GET_CODE (reg0) == MULT || GET_CODE (reg0) == CONST_INT) ! 2993: { ! 2994: rtx tmp = reg0; ! 2995: reg0 = reg1; ! 2996: reg1 = tmp; ! 2997: } ! 2998: ! 2999: if ((REG_P (reg0) && REGNO (reg0) == ARG_POINTER_REGNUM) ! 3000: || (REG_P (reg1) && REGNO (reg1) == ARG_POINTER_REGNUM)) ! 3001: abort (); ! 3002: ! 3003: else if (REG_P (reg0)) ! 3004: { ! 3005: if (REG_P (reg1)) ! 3006: fprintf (file, "%s,%s", ! 3007: reg_names [REGNO (reg0)], reg_names [REGNO (reg1)]); ! 3008: ! 3009: else if (GET_CODE (reg1) == CONST_INT) ! 3010: fprintf (file, "%s,%d", ! 3011: reg_names [REGNO (reg0)], INTVAL (reg1)); ! 3012: ! 3013: else if (GET_CODE (reg1) == MULT) ! 3014: { ! 3015: rtx mreg = XEXP (reg1, 0); ! 3016: if (REGNO (mreg) == ARG_POINTER_REGNUM) ! 3017: abort (); ! 3018: ! 3019: fprintf (file, "%s[%s]", reg_names[REGNO (reg0)], ! 3020: reg_names[REGNO (mreg)]); ! 3021: } ! 3022: ! 3023: else if (GET_CODE (reg1) == ZERO_EXTRACT) ! 3024: { ! 3025: fprintf (file, "%s,%slo16(", ! 3026: reg_names[REGNO (reg0)], m88k_pound_sign); ! 3027: output_addr_const (file, XEXP (reg1, 0)); ! 3028: fputc (')', file); ! 3029: } ! 3030: ! 3031: else if (flag_pic) ! 3032: { ! 3033: fprintf (file, "%s,", reg_names[REGNO (reg0)]); ! 3034: output_addr_const (file, reg1); ! 3035: fputs ("#got_rel", file); ! 3036: } ! 3037: else abort (); ! 3038: } ! 3039: ! 3040: else ! 3041: abort (); ! 3042: break; ! 3043: ! 3044: case MULT: ! 3045: if (REGNO (XEXP (addr, 0)) == ARG_POINTER_REGNUM) ! 3046: abort (); ! 3047: ! 3048: fprintf (file, "%s[%s]", ! 3049: reg_names[0], reg_names[REGNO (XEXP (addr, 0))]); ! 3050: break; ! 3051: ! 3052: case LSHIFT: ! 3053: fprintf (file, "%s,%shi16(", reg_names[0], m88k_pound_sign); ! 3054: output_addr_const (file, XEXP (addr, 0)); ! 3055: fputc (')', file); ! 3056: break; ! 3057: ! 3058: case CONST_INT: ! 3059: fprintf (file, "%s,%d", reg_names[0], INTVAL (addr)); ! 3060: break; ! 3061: ! 3062: default: ! 3063: fprintf (file, "%s,", reg_names[0]); ! 3064: if (SHORT_ADDRESS_P (addr, temp)) ! 3065: { ! 3066: fprintf (file, "%siw16(", m88k_pound_sign); ! 3067: output_addr_const (file, addr); ! 3068: fputc (')', file); ! 3069: } ! 3070: else ! 3071: output_addr_const (file, addr); ! 3072: } ! 3073: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.