|
|
1.1 ! root 1: /* Subroutines for insn-output.c for HPPA. ! 2: Copyright (C) 1992 Free Software Foundation, Inc. ! 3: Contributed by Tim Moore ([email protected]), based on out-sparc.c ! 4: ! 5: This file is part of GNU CC. ! 6: ! 7: GNU CC is free software; you can redistribute it and/or modify ! 8: it under the terms of the GNU General Public License as published by ! 9: the Free Software Foundation; either version 2, or (at your option) ! 10: any later version. ! 11: ! 12: GNU CC is distributed in the hope that it will be useful, ! 13: but WITHOUT ANY WARRANTY; without even the implied warranty of ! 14: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ! 15: GNU General Public License for more details. ! 16: ! 17: You should have received a copy of the GNU General Public License ! 18: along with GNU CC; see the file COPYING. If not, write to ! 19: the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ ! 20: ! 21: #include <stdio.h> ! 22: #include "config.h" ! 23: #include "rtl.h" ! 24: #include "regs.h" ! 25: #include "hard-reg-set.h" ! 26: #include "real.h" ! 27: #include "insn-config.h" ! 28: #include "conditions.h" ! 29: #include "insn-flags.h" ! 30: #include "output.h" ! 31: #include "insn-attr.h" ! 32: #include "flags.h" ! 33: #include "tree.h" ! 34: #include "c-tree.h" ! 35: ! 36: /* Save the operands last given to a compare for use when we ! 37: generate a scc or bcc insn. */ ! 38: ! 39: rtx hppa_compare_op0, hppa_compare_op1; ! 40: enum cmp_type hppa_branch_type; ! 41: ! 42: /* Global variables set by FUNCTION_PROLOGUE. */ ! 43: /* Size of frame. Need to know this to emit return insns from ! 44: leaf procedures. */ ! 45: int apparent_fsize; ! 46: int actual_fsize; ! 47: ! 48: /* Name of where we pretend to think the frame pointer points. ! 49: Normally, this is "4", but if we are in a leaf procedure, ! 50: this is "something(30)". Will this work? */ ! 51: char *frame_base_name; ! 52: ! 53: static rtx find_addr_reg (); ! 54: ! 55: /* Return non-zero only if OP is a register of mode MODE, ! 56: or const0_rtx. */ ! 57: int ! 58: reg_or_0_operand (op, mode) ! 59: rtx op; ! 60: enum machine_mode mode; ! 61: { ! 62: return (op == const0_rtx || register_operand (op, mode)); ! 63: } ! 64: ! 65: int ! 66: call_operand_address (op, mode) ! 67: rtx op; ! 68: enum machine_mode mode; ! 69: { ! 70: return (REG_P (op) || CONSTANT_P (op)); ! 71: } ! 72: ! 73: int ! 74: symbolic_operand (op, mode) ! 75: register rtx op; ! 76: enum machine_mode mode; ! 77: { ! 78: switch (GET_CODE (op)) ! 79: { ! 80: case SYMBOL_REF: ! 81: case LABEL_REF: ! 82: return 1; ! 83: case CONST: ! 84: op = XEXP (op, 0); ! 85: return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF ! 86: || GET_CODE (XEXP (op, 0)) == LABEL_REF) ! 87: && GET_CODE (XEXP (op, 1)) == CONST_INT); ! 88: default: ! 89: return 0; ! 90: } ! 91: } ! 92: ! 93: /* Return truth value of statement that OP is a symbolic memory ! 94: operand of mode MODE. */ ! 95: ! 96: int ! 97: symbolic_memory_operand (op, mode) ! 98: rtx op; ! 99: enum machine_mode mode; ! 100: { ! 101: if (GET_CODE (op) == SUBREG) ! 102: op = SUBREG_REG (op); ! 103: if (GET_CODE (op) != MEM) ! 104: return 0; ! 105: op = XEXP (op, 0); ! 106: return (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == CONST ! 107: || GET_CODE (op) == HIGH || GET_CODE (op) == LABEL_REF); ! 108: } ! 109: ! 110: /* Return 1 if the operand is either a register or a memory operand that is ! 111: not symbolic. */ ! 112: ! 113: int ! 114: reg_or_nonsymb_mem_operand (op, mode) ! 115: register rtx op; ! 116: enum machine_mode mode; ! 117: { ! 118: if (register_operand (op, mode)) ! 119: return 1; ! 120: ! 121: if (memory_operand (op, mode) && ! symbolic_memory_operand (op, mode)) ! 122: return 1; ! 123: ! 124: return 0; ! 125: } ! 126: ! 127: int ! 128: move_operand (op, mode) ! 129: rtx op; ! 130: enum machine_mode mode; ! 131: { ! 132: if (register_operand (op, mode)) ! 133: return 1; ! 134: ! 135: if (op == CONST0_RTX (mode)) ! 136: return 1; ! 137: ! 138: if (GET_MODE (op) != mode) ! 139: return 0; ! 140: if (GET_CODE (op) == SUBREG) ! 141: op = SUBREG_REG (op); ! 142: if (GET_CODE (op) != MEM) ! 143: return 0; ! 144: ! 145: op = XEXP (op, 0); ! 146: if (GET_CODE (op) == LO_SUM) ! 147: return (register_operand (XEXP (op, 0), Pmode) ! 148: && CONSTANT_P (XEXP (op, 1))); ! 149: return memory_address_p (mode, op); ! 150: } ! 151: ! 152: int ! 153: pic_operand (op, mode) ! 154: rtx op; ! 155: enum machine_mode mode; ! 156: { ! 157: return flag_pic && GET_CODE (op) == LABEL_REF; ! 158: } ! 159: ! 160: int ! 161: short_memory_operand (op, mode) ! 162: rtx op; ! 163: enum machine_mode mode; ! 164: { ! 165: if (GET_CODE (op) == MEM) ! 166: { ! 167: if (GET_CODE (XEXP (op, 0)) == REG) ! 168: return 1; ! 169: else if (GET_CODE (XEXP (op, 0)) == PLUS) ! 170: { ! 171: rtx op1 = XEXP (XEXP (op, 0), 0); ! 172: rtx op2 = XEXP (XEXP (op, 0), 1); ! 173: ! 174: if (GET_CODE (op1) == REG) ! 175: return (GET_CODE (op2) == CONST_INT && INT_5_BITS (op2)); ! 176: else if (GET_CODE (op2) == REG) ! 177: return (GET_CODE (op1) == CONST_INT && INT_5_BITS (op1)); ! 178: } ! 179: } ! 180: return 0; ! 181: } ! 182: ! 183: int ! 184: register_or_short_operand (op, mode) ! 185: rtx op; ! 186: enum machine_mode mode; ! 187: { ! 188: if (register_operand (op, mode)) ! 189: return 1; ! 190: if (GET_CODE (op) == SUBREG) ! 191: op = SUBREG_REG (op); ! 192: return short_memory_operand (op, mode); ! 193: } ! 194: ! 195: int ! 196: fp_reg_operand (op, mode) ! 197: rtx op; ! 198: enum machine_mode mode; ! 199: { ! 200: return reg_renumber && FP_REG_P (op); ! 201: } ! 202: ! 203: extern int current_function_uses_pic_offset_table; ! 204: extern rtx force_reg (), validize_mem (); ! 205: ! 206: /* The rtx for the global offset table which is a special form ! 207: that *is* a position independent symbolic constant. */ ! 208: rtx pic_pc_rtx; ! 209: ! 210: /* Ensure that we are not using patterns that are not OK with PIC. */ ! 211: ! 212: int ! 213: check_pic (i) ! 214: int i; ! 215: { ! 216: extern rtx recog_operand[]; ! 217: switch (flag_pic) ! 218: { ! 219: case 1: ! 220: if (GET_CODE (recog_operand[i]) == SYMBOL_REF ! 221: || (GET_CODE (recog_operand[i]) == CONST ! 222: && ! rtx_equal_p (pic_pc_rtx, recog_operand[i]))) ! 223: abort (); ! 224: case 2: ! 225: default: ! 226: return 1; ! 227: } ! 228: } ! 229: ! 230: /* Return truth value of whether OP is EQ or NE. */ ! 231: ! 232: int ! 233: eq_or_neq (op, mode) ! 234: rtx op; ! 235: enum machine_mode mode; ! 236: { ! 237: return (GET_CODE (op) == EQ || GET_CODE (op) == NE); ! 238: } ! 239: ! 240: /* Return truth value of whether OP can be used as an operands in a three ! 241: address arithmetic insn (such as add %o1,7,%l2) of mode MODE. */ ! 242: ! 243: int ! 244: arith_operand (op, mode) ! 245: rtx op; ! 246: enum machine_mode mode; ! 247: { ! 248: return (register_operand (op, mode) ! 249: || (GET_CODE (op) == CONST_INT && INT_14_BITS (op))); ! 250: } ! 251: ! 252: int ! 253: arith_double_operand (op, mode) ! 254: rtx op; ! 255: enum machine_mode mode; ! 256: { ! 257: return (register_operand (op, mode) ! 258: || (GET_CODE (op) == CONST_DOUBLE ! 259: && GET_MODE (op) == mode ! 260: && VAL_14_BITS_P (CONST_DOUBLE_LOW (op)) ! 261: && (CONST_DOUBLE_HIGH (op) >= 0 ! 262: == ((CONST_DOUBLE_LOW (op) & 0x1000) == 0)))); ! 263: } ! 264: ! 265: /* Return truth value of whether OP is a integer which fits the ! 266: range constraining immediate operands in three-address insns. */ ! 267: ! 268: int ! 269: int5_operand (op, mode) ! 270: rtx op; ! 271: enum machine_mode mode; ! 272: { ! 273: return (GET_CODE (op) == CONST_INT && INT_5_BITS (op)); ! 274: } ! 275: ! 276: int ! 277: uint5_operand (op, mode) ! 278: rtx op; ! 279: enum machine_mode mode; ! 280: { ! 281: return (GET_CODE (op) == CONST_INT && INT_U5_BITS (op)); ! 282: } ! 283: ! 284: ! 285: int ! 286: int11_operand (op, mode) ! 287: rtx op; ! 288: enum machine_mode mode; ! 289: { ! 290: return (GET_CODE (op) == CONST_INT && INT_11_BITS (op)); ! 291: } ! 292: ! 293: int ! 294: arith5_operand (op, mode) ! 295: rtx op; ! 296: enum machine_mode mode; ! 297: { ! 298: return register_operand (op, mode) || int5_operand (op, mode); ! 299: } ! 300: ! 301: /* Return truth value of statement that OP is a call-clobbered register. */ ! 302: int ! 303: clobbered_register (op, mode) ! 304: rtx op; ! 305: enum machine_mode mode; ! 306: { ! 307: return (GET_CODE (op) == REG && call_used_regs[REGNO (op)]); ! 308: } ! 309: ! 310: /* Legitimize PIC addresses. If the address is already ! 311: position-independent, we return ORIG. Newly generated ! 312: position-independent addresses go to REG. If we need more ! 313: than one register, we lose. */ ! 314: ! 315: rtx ! 316: legitimize_pic_address (orig, mode, reg) ! 317: rtx orig, reg; ! 318: enum machine_mode mode; ! 319: { ! 320: rtx pic_ref = orig; ! 321: ! 322: if (GET_CODE (orig) == SYMBOL_REF) ! 323: { ! 324: if (reg == 0) ! 325: abort (); ! 326: ! 327: if (flag_pic == 2) ! 328: { ! 329: emit_insn (gen_rtx (SET, VOIDmode, reg, ! 330: gen_rtx (HIGH, Pmode, orig))); ! 331: emit_insn (gen_rtx (SET, VOIDmode, reg, ! 332: gen_rtx (LO_SUM, Pmode, reg, orig))); ! 333: orig = reg; ! 334: } ! 335: pic_ref = gen_rtx (MEM, Pmode, ! 336: gen_rtx (PLUS, Pmode, ! 337: pic_offset_table_rtx, orig)); ! 338: current_function_uses_pic_offset_table = 1; ! 339: RTX_UNCHANGING_P (pic_ref) = 1; ! 340: emit_move_insn (reg, pic_ref); ! 341: return reg; ! 342: } ! 343: else if (GET_CODE (orig) == CONST) ! 344: { ! 345: rtx base, offset; ! 346: ! 347: if (GET_CODE (XEXP (orig, 0)) == PLUS ! 348: && XEXP (XEXP (orig, 0), 0) == pic_offset_table_rtx) ! 349: return orig; ! 350: ! 351: if (reg == 0) ! 352: abort (); ! 353: ! 354: if (GET_CODE (XEXP (orig, 0)) == PLUS) ! 355: { ! 356: base = legitimize_pic_address (XEXP (XEXP (orig, 0), 0), Pmode, reg); ! 357: orig = legitimize_pic_address (XEXP (XEXP (orig, 0), 1), Pmode, ! 358: base == reg ? 0 : reg); ! 359: } ! 360: else abort (); ! 361: if (GET_CODE (orig) == CONST_INT) ! 362: { ! 363: if (SMALL_INT (orig)) ! 364: return plus_constant_for_output (base, INTVAL (orig)); ! 365: orig = force_reg (Pmode, orig); ! 366: } ! 367: pic_ref = gen_rtx (PLUS, Pmode, base, orig); ! 368: /* Likewise, should we set special REG_NOTEs here? */ ! 369: } ! 370: return pic_ref; ! 371: } ! 372: ! 373: /* Set up PIC-specific rtl. This should not cause any insns ! 374: to be emitted. */ ! 375: ! 376: void ! 377: initialize_pic () ! 378: { ! 379: } ! 380: ! 381: /* Emit special PIC prologues and epilogues. */ ! 382: ! 383: void ! 384: finalize_pic () ! 385: { ! 386: /* The table we use to reference PIC data. */ ! 387: rtx global_offset_table; ! 388: /* Labels to get the PC in the prologue of this function. */ ! 389: rtx l1, l2; ! 390: rtx seq; ! 391: int orig_flag_pic = flag_pic; ! 392: ! 393: if (current_function_uses_pic_offset_table == 0) ! 394: return; ! 395: ! 396: if (! flag_pic) ! 397: abort (); ! 398: ! 399: flag_pic = 0; ! 400: l1 = gen_label_rtx (); ! 401: l2 = gen_label_rtx (); ! 402: ! 403: start_sequence (); ! 404: ! 405: emit_label (l1); ! 406: /* Note that we pun calls and jumps here! */ ! 407: emit_jump_insn (gen_rtx (PARALLEL, VOIDmode, ! 408: gen_rtvec (2, ! 409: gen_rtx (SET, VOIDmode, pc_rtx, gen_rtx (LABEL_REF, VOIDmode, l2)), ! 410: gen_rtx (SET, VOIDmode, gen_rtx (REG, SImode, 15), gen_rtx (LABEL_REF, VOIDmode, l2))))); ! 411: emit_label (l2); ! 412: ! 413: /* Initialize every time through, since we can't easily ! 414: know this to be permanent. */ ! 415: global_offset_table = gen_rtx (SYMBOL_REF, Pmode, "*__GLOBAL_OFFSET_TABLE_"); ! 416: pic_pc_rtx = gen_rtx (CONST, Pmode, ! 417: gen_rtx (MINUS, Pmode, ! 418: global_offset_table, ! 419: gen_rtx (CONST, Pmode, ! 420: gen_rtx (MINUS, Pmode, ! 421: gen_rtx (LABEL_REF, VOIDmode, l1), ! 422: pc_rtx)))); ! 423: ! 424: emit_insn (gen_rtx (SET, VOIDmode, pic_offset_table_rtx, ! 425: gen_rtx (HIGH, Pmode, pic_pc_rtx))); ! 426: emit_insn (gen_rtx (SET, VOIDmode, ! 427: pic_offset_table_rtx, ! 428: gen_rtx (LO_SUM, Pmode, ! 429: pic_offset_table_rtx, pic_pc_rtx))); ! 430: emit_insn (gen_rtx (SET, VOIDmode, ! 431: pic_offset_table_rtx, ! 432: gen_rtx (PLUS, SImode, ! 433: pic_offset_table_rtx, gen_rtx (REG, SImode, 15)))); ! 434: /* emit_insn (gen_rtx (ASM_INPUT, VOIDmode, "!#PROLOGUE# 1")); */ ! 435: LABEL_PRESERVE_P (l1) = 1; ! 436: LABEL_PRESERVE_P (l2) = 1; ! 437: flag_pic = orig_flag_pic; ! 438: ! 439: seq = gen_sequence (); ! 440: end_sequence (); ! 441: emit_insn_after (seq, get_insns ()); ! 442: ! 443: /* Need to emit this whether or not we obey regdecls, ! 444: since setjmp/longjmp can cause life info to screw up. */ ! 445: emit_insn (gen_rtx (USE, VOIDmode, pic_offset_table_rtx)); ! 446: } ! 447: ! 448: /* For the HPPA, REG and REG+CONST is cost 0 ! 449: and addresses involving symbolic constants are cost 2. ! 450: ! 451: PIC addresses are very expensive. ! 452: ! 453: It is no coincidence that this has the same structure ! 454: as GO_IF_LEGITIMATE_ADDRESS. */ ! 455: int ! 456: hppa_address_cost (X) ! 457: rtx X; ! 458: { ! 459: if (GET_CODE (X) == PLUS) ! 460: return 1; ! 461: else if (GET_CODE (X) == LO_SUM) ! 462: return 1; ! 463: else if (GET_CODE (X) == HIGH) ! 464: return 2; ! 465: return 4; ! 466: } ! 467: ! 468: /* Emit insns to move operands[1] into operands[0]. ! 469: ! 470: Return 1 if we have written out everything that needs to be done to ! 471: do the move. Otherwise, return 0 and the caller will emit the move ! 472: normally. */ ! 473: ! 474: int ! 475: emit_move_sequence (operands, mode) ! 476: rtx *operands; ! 477: enum machine_mode mode; ! 478: { ! 479: register rtx operand0 = operands[0]; ! 480: register rtx operand1 = operands[1]; ! 481: ! 482: /* Handle most common case first: storing into a register. */ ! 483: if (register_operand (operand0, mode)) ! 484: { ! 485: if (register_operand (operand1, mode) ! 486: || (GET_CODE (operand1) == CONST_INT && SMALL_INT (operand1)) ! 487: || (GET_CODE (operand1) == HIGH ! 488: && !symbolic_operand (XEXP (operand1, 0))) ! 489: /* Only `general_operands' can come here, so MEM is ok. */ ! 490: || GET_CODE (operand1) == MEM) ! 491: { ! 492: /* Run this case quickly. */ ! 493: emit_insn (gen_rtx (SET, VOIDmode, operand0, operand1)); ! 494: return 1; ! 495: } ! 496: } ! 497: else if (GET_CODE (operand0) == MEM) ! 498: { ! 499: if (register_operand (operand1, mode) || operand1 == const0_rtx) ! 500: { ! 501: /* Run this case quickly. */ ! 502: emit_insn (gen_rtx (SET, VOIDmode, operand0, operand1)); ! 503: return 1; ! 504: } ! 505: if (! reload_in_progress) ! 506: { ! 507: operands[0] = validize_mem (operand0); ! 508: operands[1] = operand1 = force_reg (mode, operand1); ! 509: } ! 510: } ! 511: ! 512: /* Simplify the source if we need to. */ ! 513: #if 0 ! 514: if (GET_CODE (operand1) == HIGH ! 515: && symbolic_operand (XEXP (operand1, 0), mode) ! 516: && !read_only_operand (XEXP (operand1, 0))) ! 517: { ! 518: rtx temp = reload_in_progress ? operand0 : gen_reg_rtx(mode); ! 519: ! 520: emit_insn (gen_rtx (SET, VOIDmode, temp, operand1)); ! 521: emit_insn (gen_rtx (SET, VOIDmode, ! 522: operand0, ! 523: gen_rtx (PLUS, mode, ! 524: temp, gen_rtx (REG, mode, 27)))); ! 525: return 1; ! 526: } ! 527: #endif ! 528: if (GET_CODE (operand1) != HIGH && immediate_operand (operand1, mode)) ! 529: { ! 530: if (symbolic_operand (operand1, mode)) ! 531: { ! 532: if (flag_pic) ! 533: { ! 534: rtx temp = reload_in_progress ? operand0 : gen_reg_rtx (Pmode); ! 535: operands[1] = legitimize_pic_address (operand1, mode, temp); ! 536: } ! 537: /* On the HPPA, references to data space are supposed to */ ! 538: /* use dp, register 27. */ ! 539: else if (read_only_operand (operand1)) ! 540: { ! 541: emit_insn (gen_rtx (SET, VOIDmode, ! 542: operand0, ! 543: gen_rtx (HIGH, mode, operand1))); ! 544: emit_insn (gen_rtx (SET, VOIDmode, ! 545: operand0, ! 546: gen_rtx (LO_SUM, mode, operand0, operand1))); ! 547: return 1; ! 548: } ! 549: else ! 550: { ! 551: /* If reload_in_progress, we can't use addil and r1; we */ ! 552: /* have to use the more expensive ldil sequence. */ ! 553: if (reload_in_progress) ! 554: { ! 555: emit_insn (gen_rtx (SET, VOIDmode, ! 556: operand0, ! 557: gen_rtx (HIGH, mode, operand1))); ! 558: emit_insn (gen_rtx (SET, VOIDmode, ! 559: operand0, ! 560: gen_rtx (PLUS, mode, ! 561: operand0, ! 562: gen_rtx (REG, mode, 27)))); ! 563: emit_insn (gen_rtx (SET, VOIDmode, ! 564: operand0, ! 565: gen_rtx (LO_SUM, mode, ! 566: operand0, operand1))); ! 567: } ! 568: else ! 569: { ! 570: rtx temp1 = gen_reg_rtx (mode), temp2 = gen_reg_rtx (mode); ! 571: ! 572: emit_insn (gen_rtx (SET, VOIDmode, ! 573: temp1, gen_rtx (HIGH, mode, operand1))); ! 574: emit_insn (gen_rtx (SET, VOIDmode, ! 575: temp2, ! 576: gen_rtx (PLUS, mode, ! 577: gen_rtx (REG, mode, 27), ! 578: temp1))); ! 579: emit_insn (gen_rtx (SET, VOIDmode, ! 580: operand0, ! 581: gen_rtx (LO_SUM, mode, ! 582: temp2, operand1))); ! 583: } ! 584: return 1; ! 585: } ! 586: } ! 587: else if (GET_CODE (operand1) == CONST_INT ! 588: ? (! SMALL_INT (operand1) ! 589: && (INTVAL (operand1) & 0x7ff) != 0) : 1) ! 590: { ! 591: rtx temp = reload_in_progress ? operand0 : gen_reg_rtx (mode); ! 592: emit_insn (gen_rtx (SET, VOIDmode, temp, ! 593: gen_rtx (HIGH, mode, operand1))); ! 594: operands[1] = gen_rtx (LO_SUM, mode, temp, operand1); ! 595: } ! 596: } ! 597: /* Now have insn-emit do whatever it normally does. */ ! 598: return 0; ! 599: } ! 600: ! 601: /* Does operand (which is a symbolic_operand) live in text space? If ! 602: so SYMBOL_REF_FLAG, which is set by ENCODE_SECTION_INFO, will be true.*/ ! 603: ! 604: int ! 605: read_only_operand (operand) ! 606: rtx operand; ! 607: { ! 608: if (GET_CODE (operand) == CONST) ! 609: operand = XEXP (XEXP (operand, 0), 0); ! 610: if (GET_CODE (operand) == SYMBOL_REF) ! 611: return SYMBOL_REF_FLAG (operand) || CONSTANT_POOL_ADDRESS_P (operand); ! 612: return 1; ! 613: } ! 614: ! 615: ! 616: /* Return the best assembler insn template ! 617: for moving operands[1] into operands[0] as a fullword. */ ! 618: ! 619: static char * ! 620: singlemove_string (operands) ! 621: rtx *operands; ! 622: { ! 623: if (GET_CODE (operands[0]) == MEM) ! 624: return "stw %r1,%0"; ! 625: if (GET_CODE (operands[1]) == MEM) ! 626: return "ldw %1,%0"; ! 627: if (GET_CODE (operands[1]) == CONST_INT) ! 628: if (INT_14_BITS (operands[1])) ! 629: return (INTVAL (operands[1]) == 0 ? "copy 0,%0" : "ldi %1,%0"); ! 630: else ! 631: return "ldil L'%1,%0\n\tldo R'%1(%0),%0"; ! 632: return "copy %1,%0"; ! 633: } ! 634: ! 635: ! 636: /* Output assembler code to perform a doubleword move insn ! 637: with operands OPERANDS. */ ! 638: ! 639: char * ! 640: output_move_double (operands) ! 641: rtx *operands; ! 642: { ! 643: enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype0, optype1; ! 644: rtx latehalf[2]; ! 645: rtx addreg0 = 0, addreg1 = 0; ! 646: ! 647: /* First classify both operands. */ ! 648: ! 649: if (REG_P (operands[0])) ! 650: optype0 = REGOP; ! 651: else if (offsettable_memref_p (operands[0])) ! 652: optype0 = OFFSOP; ! 653: else if (GET_CODE (operands[0]) == MEM) ! 654: optype0 = MEMOP; ! 655: else ! 656: optype0 = RNDOP; ! 657: ! 658: if (REG_P (operands[1])) ! 659: optype1 = REGOP; ! 660: else if (CONSTANT_P (operands[1]) ! 661: || GET_CODE (operands[1]) == CONST_DOUBLE) ! 662: optype1 = CNSTOP; ! 663: else if (offsettable_memref_p (operands[1])) ! 664: optype1 = OFFSOP; ! 665: else if (GET_CODE (operands[1]) == MEM) ! 666: optype1 = MEMOP; ! 667: else ! 668: optype1 = RNDOP; ! 669: ! 670: /* Check for the cases that the operand constraints are not ! 671: supposed to allow to happen. Abort if we get one, ! 672: because generating code for these cases is painful. */ ! 673: ! 674: if (optype0 == RNDOP || optype1 == RNDOP) ! 675: abort (); ! 676: ! 677: /* If an operand is an unoffsettable memory ref, find a register ! 678: we can increment temporarily to make it refer to the second word. */ ! 679: ! 680: if (optype0 == MEMOP) ! 681: addreg0 = find_addr_reg (operands[0]); ! 682: ! 683: if (optype1 == MEMOP) ! 684: addreg1 = find_addr_reg (operands[1]); ! 685: ! 686: /* Ok, we can do one word at a time. ! 687: Normally we do the low-numbered word first, ! 688: but if either operand is autodecrementing then we ! 689: do the high-numbered word first. ! 690: ! 691: In either case, set up in LATEHALF the operands to use ! 692: for the high-numbered word and in some cases alter the ! 693: operands in OPERANDS to be suitable for the low-numbered word. */ ! 694: ! 695: if (optype0 == REGOP) ! 696: latehalf[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1); ! 697: else if (optype0 == OFFSOP) ! 698: latehalf[0] = adj_offsettable_operand (operands[0], 4); ! 699: else ! 700: latehalf[0] = operands[0]; ! 701: ! 702: if (optype1 == REGOP) ! 703: latehalf[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1); ! 704: else if (optype1 == OFFSOP) ! 705: latehalf[1] = adj_offsettable_operand (operands[1], 4); ! 706: else if (optype1 == CNSTOP) ! 707: { ! 708: if (CONSTANT_P (operands[1])) ! 709: latehalf[1] = const0_rtx; ! 710: else if (GET_CODE (operands[1]) == CONST_DOUBLE) ! 711: { ! 712: latehalf[1] = gen_rtx (CONST_INT, VOIDmode, ! 713: XINT (operands[1], 1)); ! 714: operands[1] = gen_rtx (CONST_INT, VOIDmode, ! 715: XINT (operands[1], 0)); ! 716: } ! 717: } ! 718: else ! 719: latehalf[1] = operands[1]; ! 720: ! 721: /* If the first move would clobber the source of the second one, ! 722: do them in the other order. ! 723: ! 724: RMS says "This happens only for registers; ! 725: such overlap can't happen in memory unless the user explicitly ! 726: sets it up, and that is an undefined circumstance." ! 727: ! 728: but it happens on the sparc when loading parameter registers, ! 729: so I am going to define that circumstance, and make it work ! 730: as expected. */ ! 731: ! 732: if (optype0 == REGOP && (optype1 == MEMOP || optype1 == OFFSOP) ! 733: && reg_overlap_mentioned_p (operands[0], XEXP (operands[1], 0))) ! 734: { ! 735: /* XXX THIS PROBABLY DOESN'T WORK. */ ! 736: /* Do the late half first. */ ! 737: if (addreg1) ! 738: output_asm_insn ("addi 4,%0", &addreg1); ! 739: output_asm_insn (singlemove_string (latehalf), latehalf); ! 740: if (addreg1) ! 741: output_asm_insn ("addi -4,%0", &addreg1); ! 742: /* Then clobber. */ ! 743: return singlemove_string (operands); ! 744: } ! 745: ! 746: /* Normal case: do the two words, low-numbered first. */ ! 747: ! 748: output_asm_insn (singlemove_string (operands), operands); ! 749: ! 750: /* Make any unoffsettable addresses point at high-numbered word. */ ! 751: if (addreg0) ! 752: output_asm_insn ("addi 4,%0", &addreg0); ! 753: if (addreg1) ! 754: output_asm_insn ("addi 4,%0", &addreg1); ! 755: ! 756: /* Do that word. */ ! 757: output_asm_insn (singlemove_string (latehalf), latehalf); ! 758: ! 759: /* Undo the adds we just did. */ ! 760: if (addreg0) ! 761: output_asm_insn ("addi -4,%0", &addreg0); ! 762: if (addreg1) ! 763: output_asm_insn ("addi -4,%0", &addreg1); ! 764: ! 765: return ""; ! 766: } ! 767: ! 768: char * ! 769: output_fp_move_double (operands) ! 770: rtx *operands; ! 771: { ! 772: if (FP_REG_P (operands[0])) ! 773: { ! 774: if (FP_REG_P (operands[1])) ! 775: output_asm_insn ("fcpy,dbl %1,%0", operands); ! 776: else if (GET_CODE (operands[1]) == REG) ! 777: { ! 778: rtx xoperands[3]; ! 779: xoperands[0] = operands[0]; ! 780: xoperands[1] = operands[1]; ! 781: xoperands[2] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1); ! 782: output_asm_insn ! 783: ("stw %1,-16(0,30)\n\tstw %2,-12(0,30)\n\tfldds -16(0,30),%0", ! 784: xoperands); ! 785: } ! 786: else ! 787: output_asm_insn ("fldds%F1 %1,%0", operands); ! 788: } ! 789: else if (FP_REG_P (operands[1])) ! 790: { ! 791: if (GET_CODE (operands[0]) == REG) ! 792: { ! 793: rtx xoperands[3]; ! 794: xoperands[2] = operands[1]; ! 795: xoperands[1] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1); ! 796: xoperands[0] = operands[0]; ! 797: output_asm_insn ! 798: ("fstds %2,-16(0,30)\n\tldw -12(0,30),%1\n\tldw -16(0,30),%0", ! 799: xoperands); ! 800: } ! 801: else ! 802: output_asm_insn ("fstds%F0 %1,%0", operands); ! 803: } ! 804: else abort (); ! 805: return ""; ! 806: } ! 807: ! 808: /* Return a REG that occurs in ADDR with coefficient 1. ! 809: ADDR can be effectively incremented by incrementing REG. */ ! 810: ! 811: static rtx ! 812: find_addr_reg (addr) ! 813: rtx addr; ! 814: { ! 815: while (GET_CODE (addr) == PLUS) ! 816: { ! 817: if (GET_CODE (XEXP (addr, 0)) == REG) ! 818: addr = XEXP (addr, 0); ! 819: else if (GET_CODE (XEXP (addr, 1)) == REG) ! 820: addr = XEXP (addr, 1); ! 821: else if (CONSTANT_P (XEXP (addr, 0))) ! 822: addr = XEXP (addr, 1); ! 823: else if (CONSTANT_P (XEXP (addr, 1))) ! 824: addr = XEXP (addr, 0); ! 825: else ! 826: abort (); ! 827: } ! 828: if (GET_CODE (addr) == REG) ! 829: return addr; ! 830: abort (); ! 831: } ! 832: ! 833: /* Load the address specified by OPERANDS[3] into the register ! 834: specified by OPERANDS[0]. ! 835: ! 836: OPERANDS[3] may be the result of a sum, hence it could either be: ! 837: ! 838: (1) CONST ! 839: (2) REG ! 840: (2) REG + CONST_INT ! 841: (3) REG + REG + CONST_INT ! 842: (4) REG + REG (special case of 3). ! 843: ! 844: Note that (3) is not a legitimate address. ! 845: All cases are handled here. */ ! 846: ! 847: void ! 848: output_load_address (operands) ! 849: rtx *operands; ! 850: { ! 851: rtx base, offset; ! 852: ! 853: if (CONSTANT_P (operands[3])) ! 854: { ! 855: output_asm_insn ("ldi %3,%0", operands); ! 856: return; ! 857: } ! 858: ! 859: if (REG_P (operands[3])) ! 860: { ! 861: if (REGNO (operands[0]) != REGNO (operands[3])) ! 862: output_asm_insn ("copy %3,%0", operands); ! 863: return; ! 864: } ! 865: ! 866: if (GET_CODE (operands[3]) != PLUS) ! 867: abort (); ! 868: ! 869: base = XEXP (operands[3], 0); ! 870: offset = XEXP (operands[3], 1); ! 871: ! 872: if (GET_CODE (base) == CONST_INT) ! 873: { ! 874: rtx tmp = base; ! 875: base = offset; ! 876: offset = tmp; ! 877: } ! 878: ! 879: if (GET_CODE (offset) != CONST_INT) ! 880: { ! 881: /* Operand is (PLUS (REG) (REG)). */ ! 882: base = operands[3]; ! 883: offset = const0_rtx; ! 884: } ! 885: ! 886: if (REG_P (base)) ! 887: { ! 888: operands[6] = base; ! 889: operands[7] = offset; ! 890: if (INT_14_BITS (offset)) ! 891: output_asm_insn ("ldo %7(%6),%0", operands); ! 892: else ! 893: output_asm_insn ("addil L'%7,%6\n\tldo R'%7(1),%0", operands); ! 894: } ! 895: else if (GET_CODE (base) == PLUS) ! 896: { ! 897: operands[6] = XEXP (base, 0); ! 898: operands[7] = XEXP (base, 1); ! 899: operands[8] = offset; ! 900: ! 901: if (offset == const0_rtx) ! 902: output_asm_insn ("add %6,%7,%0", operands); ! 903: else if (INT_14_BITS (offset)) ! 904: output_asm_insn ("add %6,%7,%0\n\taddi %8,%0", operands); ! 905: else ! 906: output_asm_insn ("addil L'%8,%6\n\tldo R'%8(1),%0\n\tadd %0,%7,%0", operands); ! 907: } ! 908: else ! 909: abort (); ! 910: } ! 911: ! 912: /* Output code to place a size count SIZE in register REG. ! 913: ALIGN is the size of the unit of transfer. ! 914: ! 915: Because block moves are pipelined, we don't include the ! 916: first element in the transfer of SIZE to REG. */ ! 917: ! 918: static void ! 919: output_size_for_block_move (size, reg, align) ! 920: rtx size, reg; ! 921: rtx align; ! 922: { ! 923: rtx xoperands[3]; ! 924: ! 925: xoperands[0] = reg; ! 926: xoperands[1] = size; ! 927: xoperands[2] = align; ! 928: if (GET_CODE (size) == REG) ! 929: output_asm_insn ("ldo -%2(%1),%0", xoperands); ! 930: else ! 931: { ! 932: xoperands[1] ! 933: = gen_rtx (CONST_INT, VOIDmode, INTVAL (size) - INTVAL (align)); ! 934: if (INT_14_BITS (xoperands[1])) ! 935: output_asm_insn ("ldi %1,%0", xoperands); ! 936: else ! 937: output_asm_insn ("addil L'%1,0\n\tldo R'%1(1),%0", xoperands); ! 938: } ! 939: } ! 940: ! 941: /* Emit code to perform a block move. ! 942: ! 943: OPERANDS[0] is the destination. ! 944: OPERANDS[1] is the source. ! 945: OPERANDS[2] is the size. ! 946: OPERANDS[3] is the alignment safe to use. ! 947: OPERANDS[4] is a register we can safely clobber as a temp. */ ! 948: ! 949: char * ! 950: output_block_move (operands) ! 951: rtx *operands; ! 952: { ! 953: /* A vector for our computed operands. Note that load_output_address ! 954: makes use of (and can clobber) up to the 8th element of this vector. */ ! 955: rtx xoperands[10]; ! 956: rtx zoperands[10]; ! 957: static int movstrsi_label = 0; ! 958: int i, j; ! 959: rtx temp1 = operands[4]; ! 960: rtx alignrtx = operands[3]; ! 961: int align = INTVAL (alignrtx); ! 962: ! 963: xoperands[0] = operands[0]; ! 964: xoperands[1] = operands[1]; ! 965: xoperands[2] = temp1; ! 966: ! 967: /* We can't move more than four bytes at a time ! 968: because we have only one register to move them through. */ ! 969: if (align > 4) ! 970: { ! 971: align = 4; ! 972: alignrtx = gen_rtx (CONST_INT, VOIDmode, 4); ! 973: } ! 974: ! 975: /* Since we clobber untold things, nix the condition codes. */ ! 976: ! 977: /* Recognize special cases of block moves. These occur ! 978: when GNU C++ is forced to treat something as BLKmode ! 979: to keep it in memory, when its mode could be represented ! 980: with something smaller. ! 981: ! 982: We cannot do this for global variables, since we don't know ! 983: what pages they don't cross. Sigh. */ ! 984: if (GET_CODE (operands[2]) == CONST_INT ! 985: && INTVAL (operands[2]) <= 8 ! 986: && ! CONSTANT_ADDRESS_P (operands[0]) ! 987: && ! CONSTANT_ADDRESS_P (operands[1])) ! 988: { ! 989: int size = INTVAL (operands[2]); ! 990: ! 991: if (align == 1) ! 992: { ! 993: if (memory_address_p (QImode, plus_constant (xoperands[0], size)) ! 994: && memory_address_p (QImode, plus_constant (xoperands[1], size))) ! 995: { ! 996: /* We will store different integers into xoperands[2]. */ ! 997: ! 998: for (i = 0; i <= size-1; i++) ! 999: { ! 1000: xoperands[2] = gen_rtx (CONST_INT, VOIDmode, i); ! 1001: output_asm_insn ("ldbs %2(%1),1\n\tstbs,ma 1,1(0,%0)", ! 1002: xoperands); ! 1003: } ! 1004: return ""; ! 1005: } ! 1006: } ! 1007: else if (align == 2) ! 1008: { ! 1009: if (memory_address_p (HImode, plus_constant (xoperands[0], size)) ! 1010: && memory_address_p (HImode, plus_constant (xoperands[1], size))) ! 1011: { ! 1012: for (i = 0 ; i <= (size>>1)-1; i++) ! 1013: { ! 1014: xoperands[2] = gen_rtx (CONST_INT, VOIDmode, i << 1); ! 1015: output_asm_insn ("ldhs %2(%1),1\n\tsths,ma 1,2(0,%0)", ! 1016: xoperands); ! 1017: } ! 1018: return ""; ! 1019: } ! 1020: } ! 1021: else ! 1022: { ! 1023: if (memory_address_p (SImode, plus_constant (xoperands[0], size)) ! 1024: && memory_address_p (SImode, plus_constant (xoperands[1], size))) ! 1025: { ! 1026: for (i = 0; i <= (size>>2)-1; i++) ! 1027: { ! 1028: xoperands[2] = gen_rtx (CONST_INT, VOIDmode, i << 2); ! 1029: output_asm_insn ("ldws %2(%1),1\n\tstws,ma 1,4(0,%0)", ! 1030: xoperands); ! 1031: } ! 1032: return ""; ! 1033: } ! 1034: } ! 1035: } ! 1036: ! 1037: /* This is the size of the transfer. ! 1038: Either use the register which already contains the size, ! 1039: or use a free register (used by no operands). ! 1040: Also emit code to decrement the size value by ALIGN. */ ! 1041: output_size_for_block_move (operands[2], temp1, alignrtx); ! 1042: ! 1043: zoperands[0] = operands[0]; ! 1044: zoperands[3] = gen_rtx (PLUS, SImode, operands[0], temp1); ! 1045: output_load_address (zoperands); ! 1046: ! 1047: xoperands[3] = gen_rtx (CONST_INT, VOIDmode, movstrsi_label++); ! 1048: xoperands[4] = gen_rtx (CONST_INT, VOIDmode, - align); ! 1049: ! 1050: if (align == 1) ! 1051: output_asm_insn ("\nLm%3\n\tldbx %2(%1),1\n\taddib,>= %4,%2,Lm%3\n\tstbs,ma 1,%4(0,%0)", xoperands); ! 1052: else if (align == 2) ! 1053: output_asm_insn ("\nLm%3\n\tldhx %2(%1),1\n\taddib,>= %4,%2,Lm%3\n\tsths,ma 1,%4(0,%0)", xoperands); ! 1054: else ! 1055: output_asm_insn ("\nLm%3\n\tldwx %2(%1),1\n\taddib,>= %4,%2,Lm%3\n\tstws,ma 1,%4(0,%0)", xoperands); ! 1056: return ""; ! 1057: } ! 1058: ! 1059: ! 1060: /* Output an ascii string. */ ! 1061: output_ascii (file, p, size) ! 1062: FILE *file; ! 1063: unsigned char *p; ! 1064: int size; ! 1065: { ! 1066: int i; ! 1067: int chars_output; ! 1068: unsigned char partial_output[16]; /* Max space 4 chars can occupy. */ ! 1069: ! 1070: /* The HP assember can only take strings of 256 characters at one ! 1071: time. This is a limitation on input line length, *not* the ! 1072: length of the string. Sigh. Even worse, it seems that the ! 1073: restricition is in number of input characters (see \xnn & ! 1074: \whatever). So we have to do this very carefully. */ ! 1075: ! 1076: fprintf (file, "\t.STRING \""); ! 1077: ! 1078: chars_output = 0; ! 1079: for (i = 0; i < size; i += 4) ! 1080: { ! 1081: int co = 0; ! 1082: int io = 0; ! 1083: for (io = 0, co = 0; io < MIN (4, size - i); io++) ! 1084: { ! 1085: register unsigned int c = p[i + io]; ! 1086: ! 1087: if (c == '\"' || c == '\\') ! 1088: partial_output[co++] = '\\'; ! 1089: if (c >= ' ' && c < 0177) ! 1090: partial_output[co++] = c; ! 1091: else ! 1092: { ! 1093: unsigned int hexd; ! 1094: partial_output[co++] = '\\'; ! 1095: partial_output[co++] = 'x'; ! 1096: hexd = c / 16 - 0 + '0'; ! 1097: if (hexd > '9') ! 1098: hexd -= '9' - 'a' + 1; ! 1099: partial_output[co++] = hexd; ! 1100: hexd = c % 16 - 0 + '0'; ! 1101: if (hexd > '9') ! 1102: hexd -= '9' - 'a' + 1; ! 1103: partial_output[co++] = hexd; ! 1104: } ! 1105: } ! 1106: if (chars_output + co > 243) ! 1107: { ! 1108: fprintf (file, "\"\n\t.STRING \""); ! 1109: chars_output = 0; ! 1110: } ! 1111: fwrite (partial_output, 1, co, file); ! 1112: chars_output += co; ! 1113: co = 0; ! 1114: } ! 1115: fprintf (file, "\"\n"); ! 1116: } ! 1117: ! 1118: /* You may have trouble believing this, but this is the HP825 stack ! 1119: layout. Wow. ! 1120: ! 1121: Offset Contents ! 1122: ! 1123: Variable arguments (optional; any number may be allocated) ! 1124: ! 1125: SP-(4*(N+9)) arg word N ! 1126: : : ! 1127: SP-56 arg word 5 ! 1128: SP-52 arg word 4 ! 1129: ! 1130: Fixed arguments (must be allocated; may remain unused) ! 1131: ! 1132: SP-48 arg word 3 ! 1133: SP-44 arg word 2 ! 1134: SP-40 arg word 1 ! 1135: SP-36 arg word 0 ! 1136: ! 1137: Frame Marker ! 1138: ! 1139: SP-32 External Data Pointer (DP) ! 1140: SP-28 External sr4 ! 1141: SP-24 External/stub RP (RP') ! 1142: SP-20 Current RP ! 1143: SP-16 Static Link ! 1144: SP-12 Clean up ! 1145: SP-8 Calling Stub RP (RP'') ! 1146: SP-4 Previous SP ! 1147: ! 1148: Top of Frame ! 1149: ! 1150: SP-0 Stack Pointer (points to next available address) ! 1151: ! 1152: */ ! 1153: ! 1154: /* This function saves registers as follows. Registers marked with ' are ! 1155: this function's registers (as opposed to the previous function's). ! 1156: If a frame_pointer isn't needed, r4 is saved as a general register; ! 1157: the space for the frame pointer is still allocated, though, to keep ! 1158: things simple. ! 1159: ! 1160: ! 1161: Top of Frame ! 1162: ! 1163: SP (FP') Previous FP ! 1164: SP + 4 Alignment filler (sigh) ! 1165: SP + 8 Space for locals reserved here. ! 1166: . ! 1167: . ! 1168: . ! 1169: SP + n All call saved register used. ! 1170: . ! 1171: . ! 1172: . ! 1173: SP + o All call saved fp registers used. ! 1174: . ! 1175: . ! 1176: . ! 1177: SP + p (SP') points to next avaliable address. ! 1178: ! 1179: */ ! 1180: ! 1181: /* Helper functions */ ! 1182: void ! 1183: print_stw (file, r, disp, base) ! 1184: FILE *file; ! 1185: int r, disp, base; ! 1186: { ! 1187: if (VAL_14_BITS_P (disp)) ! 1188: fprintf (file, "\tstw %d,%d(0,%d)\n", r, disp, base); ! 1189: else ! 1190: fprintf (file, "\taddil L'%d,%d\n\tstw %d,R'%d(0,1)\n", disp, base, ! 1191: r, disp); ! 1192: } ! 1193: ! 1194: void ! 1195: print_ldw (file, r, disp, base) ! 1196: FILE *file; ! 1197: int r, disp, base; ! 1198: { ! 1199: if (VAL_14_BITS_P (disp)) ! 1200: fprintf (file, "\tldw %d(0,%d),%d\n", disp, base, r); ! 1201: else ! 1202: fprintf (file, "\taddil L'%d,%d\n\tldw R'%d(0,1),%d\n", disp, base, ! 1203: disp, r); ! 1204: } ! 1205: ! 1206: /* Set by the FUNCTION_PROFILER macro. */ ! 1207: int hp_profile_labelno; ! 1208: extern int profile_flag; ! 1209: ! 1210: int local_fsize, save_fregs, actual_fsize; ! 1211: ! 1212: int ! 1213: compute_frame_size (size, leaf_function) ! 1214: int size; ! 1215: int leaf_function; ! 1216: { ! 1217: extern int current_function_outgoing_args_size; ! 1218: int i; ! 1219: ! 1220: /* 8 is space for frame pointer + filler */ ! 1221: local_fsize = actual_fsize = size + 8; ! 1222: ! 1223: /* fp is stored in a special place. */ ! 1224: for (i = 18; i >= 5; i--) ! 1225: if (regs_ever_live[i]) ! 1226: actual_fsize += 4; ! 1227: ! 1228: if (regs_ever_live[3]) ! 1229: actual_fsize += 4; ! 1230: actual_fsize = (actual_fsize + 7) & ~7; ! 1231: ! 1232: if (!TARGET_SNAKE) ! 1233: { ! 1234: for (i = 47; i >= 44; i--) ! 1235: if (regs_ever_live[i]) ! 1236: { ! 1237: actual_fsize += 8; save_fregs++; ! 1238: } ! 1239: } ! 1240: else ! 1241: { ! 1242: for (i = 90; i >= 72; i -= 2) ! 1243: if (regs_ever_live[i] || regs_ever_live[i + 1]) ! 1244: { ! 1245: actual_fsize += 8; save_fregs++; ! 1246: } ! 1247: } ! 1248: return actual_fsize + current_function_outgoing_args_size; ! 1249: } ! 1250: ! 1251: void ! 1252: output_function_prologue (file, size, leaf_function) ! 1253: FILE *file; ! 1254: int size; ! 1255: int leaf_function; ! 1256: { ! 1257: extern char call_used_regs[]; ! 1258: extern int frame_pointer_needed; ! 1259: int i, offset; ! 1260: ! 1261: actual_fsize = compute_frame_size (size, leaf_function) + 32; ! 1262: /* Let's not try to bullshit more than we need to here. */ ! 1263: /* This might be right a lot of the time */ ! 1264: fprintf (file, "\t.PROC\n\t.CALLINFO FRAME=%d", actual_fsize); ! 1265: if (regs_ever_live[2]) ! 1266: fprintf (file, ",CALLS,SAVE_RP\n"); ! 1267: else ! 1268: fprintf (file, ",NO_CALLS\n"); ! 1269: fprintf (file, "\t.ENTRY\n"); ! 1270: ! 1271: /* Instead of taking one argument, the counter label, as most normal ! 1272: mcounts do, _mcount appears to behave differently on the HPPA. It ! 1273: takes the return address of the caller, the address of this ! 1274: routine, and the address of the label. Also, it isn't magic, so ! 1275: caller saves have to be preserved. We get around this by calling ! 1276: our own gcc_mcount, which takes arguments on the stack and saves ! 1277: argument registers. */ ! 1278: ! 1279: if (profile_flag) ! 1280: { ! 1281: fprintf (file,"\tstw 2,-20(30)\n\tldo 48(30),30\n\ ! 1282: \taddil L'LP$%04d-$global$,27\n\tldo R'LP$%04d-$global$(1),1\n\ ! 1283: \tbl __gcc_mcount,2\n\tstw 1,-16(30)\n\tldo -48(30),30\n\tldw -20(30),2\n", ! 1284: hp_profile_labelno, hp_profile_labelno); ! 1285: } ! 1286: /* Some registers have places to go in the current stack ! 1287: structure. */ ! 1288: ! 1289: #if 0 ! 1290: /* However, according to the hp docs, there's no need to save the ! 1291: sp. */ ! 1292: fprintf (file, "\tstw 30,-4(30)\n"); ! 1293: #endif ! 1294: ! 1295: if (regs_ever_live[2]) ! 1296: fprintf (file, "\tstw 2,-20(0,30)\n"); ! 1297: ! 1298: /* Reserve space for local variables. */ ! 1299: if (frame_pointer_needed) ! 1300: { ! 1301: if (VAL_14_BITS_P (actual_fsize)) ! 1302: fprintf (file, "\tcopy 4,1\n\tcopy 30,4\n\tstwm 1,%d(0,30)\n", ! 1303: actual_fsize); ! 1304: else ! 1305: { ! 1306: fprintf (file, "\tcopy 4,1\n\tcopy 30,4\n\tstw 1,0(0,4)\n"); ! 1307: fprintf (file, "\taddil L'%d,30\n\tldo R'%d(1),30\n", ! 1308: actual_fsize, actual_fsize); ! 1309: } ! 1310: } ! 1311: else ! 1312: /* Used to be abort (); */ ! 1313: { ! 1314: if (VAL_14_BITS_P (actual_fsize)) ! 1315: fprintf (file, "\tldo %d(30),30\n", actual_fsize); ! 1316: else ! 1317: fprintf (file, "\taddil L'%d,30\n\tldo R'%d(1),30\n", ! 1318: actual_fsize, actual_fsize); ! 1319: } ! 1320: ! 1321: /* Normal register save. */ ! 1322: if (frame_pointer_needed) ! 1323: { ! 1324: for (i = 18, offset = local_fsize; i >= 5; i--) ! 1325: if (regs_ever_live[i] && ! call_used_regs[i]) ! 1326: { ! 1327: print_stw (file, i, offset, 4); offset += 4; ! 1328: } ! 1329: if (regs_ever_live[3] && ! call_used_regs[3]) ! 1330: { ! 1331: print_stw (file, 3, offset, 4); offset += 4; ! 1332: } ! 1333: } ! 1334: else ! 1335: { ! 1336: for (i = 18, offset = local_fsize - actual_fsize; i >= 5; i--) ! 1337: if (regs_ever_live[i] && ! call_used_regs[i]) ! 1338: { ! 1339: print_stw (file, i, offset, 30); offset += 4; ! 1340: } ! 1341: if (regs_ever_live[3] && ! call_used_regs[3]) ! 1342: { ! 1343: print_stw (file, 3, offset, 30); offset += 4; ! 1344: } ! 1345: } ! 1346: ! 1347: /* Align pointer properly (doubleword boundary). */ ! 1348: offset = (offset + 7) & ~7; ! 1349: ! 1350: /* Floating point register store. */ ! 1351: if (save_fregs) ! 1352: if (frame_pointer_needed) ! 1353: if (VAL_14_BITS_P (offset)) ! 1354: fprintf (file, "\tldo %d(4),1\n", offset); ! 1355: else ! 1356: fprintf (file, "\taddil L'%d,4\n\tldo R'%d(1),1\n", offset, offset); ! 1357: else ! 1358: if (VAL_14_BITS_P (offset)) ! 1359: fprintf (file, "\tldo %d(30),1\n", offset); ! 1360: else ! 1361: fprintf (file, "\taddil L'%d,30\n\tldo R'%d(1),1\n", offset, offset); ! 1362: if (!TARGET_SNAKE) ! 1363: { ! 1364: for (i = 47; i >= 44; i--) ! 1365: { ! 1366: if (regs_ever_live[i]) ! 1367: fprintf (file, "\tfstds,ma %s,8(0,1)\n", reg_names[i]); ! 1368: } ! 1369: } ! 1370: else ! 1371: { ! 1372: for (i = 90; i >= 72; i -= 2) ! 1373: if (regs_ever_live[i] || regs_ever_live[i + 1]) ! 1374: { ! 1375: fprintf (file, "\tfstds,ma %s,8(0,1)\n", reg_names[i]); ! 1376: } ! 1377: } ! 1378: } ! 1379: ! 1380: void ! 1381: output_function_epilogue (file, size, leaf_function) ! 1382: FILE *file; ! 1383: int size; ! 1384: int leaf_function; ! 1385: { ! 1386: extern char call_used_regs[]; ! 1387: extern int frame_pointer_needed; ! 1388: int i, offset; ! 1389: ! 1390: if (frame_pointer_needed) ! 1391: { ! 1392: for (i = 18, offset = local_fsize; i >= 5; i--) ! 1393: if (regs_ever_live[i] && ! call_used_regs[i]) ! 1394: { ! 1395: print_ldw (file, i, offset, 4); offset += 4; ! 1396: } ! 1397: if (regs_ever_live[3] && ! call_used_regs[3]) ! 1398: { ! 1399: print_ldw (file, 3, offset, 4); offset += 4; ! 1400: } ! 1401: } ! 1402: else ! 1403: { ! 1404: for (i = 18, offset = local_fsize - actual_fsize; i >= 5; i--) ! 1405: if (regs_ever_live[i] && ! call_used_regs[i]) ! 1406: { ! 1407: print_ldw (file, i, offset, 30); offset += 4; ! 1408: } ! 1409: if (regs_ever_live[3] && ! call_used_regs[3]) ! 1410: { ! 1411: print_ldw (file, 3, offset, 30); offset += 4; ! 1412: } ! 1413: } ! 1414: ! 1415: /* Align pointer properly (doubleword boundary). */ ! 1416: offset = (offset + 7) & ~7; ! 1417: ! 1418: /* Floating point register restore. */ ! 1419: if (save_fregs) ! 1420: if (frame_pointer_needed) ! 1421: if (VAL_14_BITS_P (offset)) ! 1422: fprintf (file, "\tldo %d(4),1\n", offset); ! 1423: else ! 1424: fprintf (file, "\taddil L'%d,4\n\tldo R'%d(1),1\n", offset, offset); ! 1425: else ! 1426: if (VAL_14_BITS_P (offset)) ! 1427: fprintf (file, "\tldo %d(30),1\n", offset); ! 1428: else ! 1429: fprintf (file, "\taddil L'%d,30\n\tldo R'%d(1),1\n", offset, offset); ! 1430: if (!TARGET_SNAKE) ! 1431: { ! 1432: for (i = 47; i >= 44; i--) ! 1433: { ! 1434: if (regs_ever_live[i]) ! 1435: fprintf (file, "\tfldds,ma 8(0,1),%s\n", reg_names[i]); ! 1436: } ! 1437: } ! 1438: else ! 1439: { ! 1440: for (i = 90; i >= 72; i -= 2) ! 1441: if (regs_ever_live[i] || regs_ever_live[i + 1]) ! 1442: { ! 1443: fprintf (file, "\tfldds,ma 8(0,1),%s\n", reg_names[i]); ! 1444: } ! 1445: } ! 1446: /* Reset stack pointer (and possibly frame pointer). The stack */ ! 1447: /* pointer is initially set to fp + 8 to avoid a race condition. */ ! 1448: if (frame_pointer_needed) ! 1449: { ! 1450: fprintf (file, "\tldo 8(4),30\n"); ! 1451: if (regs_ever_live[2]) ! 1452: fprintf (file, "\tldw -28(0,30),2\n"); ! 1453: fprintf (file, "\tbv 0(2)\n\tldwm -8(30),4\n"); ! 1454: } ! 1455: else if (actual_fsize) ! 1456: { ! 1457: if (regs_ever_live[2] && VAL_14_BITS_P (actual_fsize + 20)) ! 1458: fprintf (file, "\tldw %d(30),2\n\tbv 0(2)\n\tldo %d(30),30\n", ! 1459: -(actual_fsize + 20), -actual_fsize); ! 1460: else if (regs_ever_live[2]) ! 1461: fprintf (file, ! 1462: "\taddil L'%d,30\n\tldw %d(1),2\n\tbv 0(2)\n\tldo R'%d(1),30\n", ! 1463: - actual_fsize, ! 1464: - ((actual_fsize + 20) - (actual_fsize & ~0x7ff)), ! 1465: - actual_fsize); ! 1466: else if (VAL_14_BITS_P (actual_fsize)) ! 1467: fprintf (file, "\tbv 0(2)\n\tldo %d(30),30\n", - actual_fsize); ! 1468: else ! 1469: fprintf (file, "\taddil L'%d,30\n\tbv 0(2)\n\tldo R'%d(1),30\n"); ! 1470: } ! 1471: else if (current_function_epilogue_delay_list) ! 1472: { ! 1473: fprintf (file, "\tbv 0(2)\n"); ! 1474: final_scan_insn (XEXP (current_function_epilogue_delay_list, 0), ! 1475: file, write_symbols, 1, 0, 1); ! 1476: } ! 1477: else ! 1478: fprintf (file, "\tbv,n 0(2)\n"); ! 1479: fprintf (file, "\t.EXIT\n\t.PROCEND\n"); ! 1480: } ! 1481: ! 1482: rtx ! 1483: gen_compare_reg (code, x, y) ! 1484: enum rtx_code code; ! 1485: rtx x, y; ! 1486: { ! 1487: enum machine_mode mode = SELECT_CC_MODE (code, x); ! 1488: rtx cc_reg = gen_rtx (REG, mode, 0); ! 1489: ! 1490: emit_insn (gen_rtx (SET, VOIDmode, cc_reg, ! 1491: gen_rtx (COMPARE, mode, x, y))); ! 1492: ! 1493: return cc_reg; ! 1494: } ! 1495: ! 1496: /* Return nonzero if TRIAL can go into the function epilogue's ! 1497: delay slot. SLOT is the slot we are trying to fill. */ ! 1498: ! 1499: int ! 1500: eligible_for_epilogue_delay (trial, slot) ! 1501: rtx trial; ! 1502: int slot; ! 1503: { ! 1504: if (slot >= 1) ! 1505: return 0; ! 1506: if (GET_CODE (trial) != INSN ! 1507: || GET_CODE (PATTERN (trial)) != SET) ! 1508: return 0; ! 1509: if (get_attr_length (trial) != 1) ! 1510: return 0; ! 1511: return (leaf_function && ! 1512: get_attr_in_branch_delay (trial) == IN_BRANCH_DELAY_TRUE); ! 1513: } ! 1514: ! 1515: rtx ! 1516: gen_scond_fp (code, operand0) ! 1517: enum rtx_code code; ! 1518: rtx operand0; ! 1519: { ! 1520: return gen_rtx (SET, VOIDmode, operand0, ! 1521: gen_rtx (code, CCFPmode, ! 1522: gen_rtx (REG, CCFPmode, 0), const0_rtx)); ! 1523: } ! 1524: ! 1525: void ! 1526: emit_bcond_fp (code, operand0) ! 1527: enum rtx_code code; ! 1528: rtx operand0; ! 1529: { ! 1530: emit_jump_insn (gen_rtx (SET, VOIDmode, pc_rtx, ! 1531: gen_rtx (IF_THEN_ELSE, VOIDmode, ! 1532: gen_rtx (code, VOIDmode, ! 1533: gen_rtx (REG, CCFPmode, 0), ! 1534: const0_rtx), ! 1535: gen_rtx (LABEL_REF, VOIDmode, operand0), ! 1536: pc_rtx))); ! 1537: ! 1538: } ! 1539: ! 1540: rtx ! 1541: gen_cmp_fp (code, operand0, operand1) ! 1542: enum rtx_code code; ! 1543: rtx operand0, operand1; ! 1544: { ! 1545: return gen_rtx (SET, VOIDmode, gen_rtx (REG, CCFPmode, 0), ! 1546: gen_rtx (code, CCFPmode, operand0, operand1)); ! 1547: } ! 1548: ! 1549: ! 1550: /* Print operand X (an rtx) in assembler syntax to file FILE. ! 1551: CODE is a letter or dot (`z' in `%z0') or 0 if no letter was specified. ! 1552: For `%' followed by punctuation, CODE is the punctuation and X is null. */ ! 1553: ! 1554: void ! 1555: print_operand (file, x, code) ! 1556: FILE *file; ! 1557: rtx x; ! 1558: int code; ! 1559: { ! 1560: switch (code) ! 1561: { ! 1562: case '#': ! 1563: /* Output a 'nop' if there's nothing for the delay slot. */ ! 1564: if (dbr_sequence_length () == 0) ! 1565: fputs ("\n\tnop", file); ! 1566: return; ! 1567: case '*': ! 1568: /* Output an nullification completer if there's nothing for the */ ! 1569: /* delay slot or nullification is requested. */ ! 1570: if (dbr_sequence_length () == 0 || ! 1571: (final_sequence && ! 1572: INSN_ANNULLED_BRANCH_P (XVECEXP (final_sequence, 0, 0)))) ! 1573: fputs (",n", file); ! 1574: return; ! 1575: case 'R': ! 1576: /* Print out the second register name of a register pair. ! 1577: I.e., R (6) => 7. */ ! 1578: fputs (reg_names[REGNO (x)+1], file); ! 1579: return; ! 1580: case 'r': ! 1581: /* A register or zero. */ ! 1582: if (x == const0_rtx) ! 1583: { ! 1584: fputs ("0", file); ! 1585: return; ! 1586: } ! 1587: else ! 1588: break; ! 1589: case 'O': ! 1590: switch (GET_CODE (x)) ! 1591: { ! 1592: case PLUS: ! 1593: fprintf (file, "add%s", ! 1594: GET_CODE (XEXP (x, 1)) == CONST_INT ? "i" : ""); break; ! 1595: case MINUS: ! 1596: fprintf (file, "sub%s", ! 1597: GET_CODE (XEXP (x, 0)) == CONST_INT ? "i" : ""); break; ! 1598: case AND: ! 1599: fprintf (file, "and%s", ! 1600: GET_CODE (XEXP (x, 1)) == NOT ? "cm" : ""); break; ! 1601: case IOR: ! 1602: fprintf (file, "or"); break; ! 1603: case XOR: ! 1604: fprintf (file, "xor"); break; ! 1605: case ASHIFT: ! 1606: fprintf (file, "sh%dadd", INTVAL (XEXP (x, 1))); break; ! 1607: /* Too lazy to handle bitfield conditions yet. */ ! 1608: default: ! 1609: printf ("Can't grok '%c' operator:\n", code); ! 1610: debug_rtx (x); ! 1611: abort (); ! 1612: } ! 1613: return; ! 1614: case 'C': ! 1615: case 'X': ! 1616: switch (GET_CODE (x)) ! 1617: { ! 1618: case EQ: ! 1619: fprintf (file, "="); break; ! 1620: case NE: ! 1621: if (code == 'C') ! 1622: fprintf (file, "<>"); ! 1623: else ! 1624: fprintf (file, "!="); ! 1625: break; ! 1626: case GT: ! 1627: fprintf (file, ">"); break; ! 1628: case GE: ! 1629: fprintf (file, ">="); break; ! 1630: case GEU: ! 1631: fprintf (file, ">>="); break; ! 1632: case GTU: ! 1633: fprintf (file, ">>"); break; ! 1634: case LT: ! 1635: fprintf (file, "<"); break; ! 1636: case LE: ! 1637: fprintf (file, "<="); break; ! 1638: case LEU: ! 1639: fprintf (file, "<<="); break; ! 1640: case LTU: ! 1641: fprintf (file, "<<"); break; ! 1642: default: ! 1643: printf ("Can't grok '%c' operator:\n", code); ! 1644: debug_rtx (x); ! 1645: abort (); ! 1646: } ! 1647: return; ! 1648: case 'N': ! 1649: case 'Y': ! 1650: switch (GET_CODE (x)) ! 1651: { ! 1652: case EQ: ! 1653: if (code == 'N') ! 1654: fprintf (file, "<>"); ! 1655: else ! 1656: fprintf (file, "!="); ! 1657: break; ! 1658: case NE: ! 1659: fprintf (file, "="); break; ! 1660: case GT: ! 1661: fprintf (file, "<="); break; ! 1662: case GE: ! 1663: fprintf (file, "<"); break; ! 1664: case GEU: ! 1665: fprintf (file, "<<"); break; ! 1666: case GTU: ! 1667: fprintf (file, "<<="); break; ! 1668: case LT: ! 1669: fprintf (file, ">="); break; ! 1670: case LE: ! 1671: fprintf (file, ">"); break; ! 1672: case LEU: ! 1673: fprintf (file, ">>"); break; ! 1674: case LTU: ! 1675: fprintf (file, ">>="); break; ! 1676: default: ! 1677: printf ("Can't grok '%c' operator:\n", code); ! 1678: debug_rtx (x); ! 1679: abort (); ! 1680: } ! 1681: return; ! 1682: case 'M': ! 1683: switch (GET_CODE (XEXP (x, 0))) ! 1684: { ! 1685: case PRE_DEC: ! 1686: case PRE_INC: ! 1687: fprintf (file, "s,mb"); ! 1688: break; ! 1689: case POST_DEC: ! 1690: case POST_INC: ! 1691: fprintf (file, "s,ma"); ! 1692: break; ! 1693: default: ! 1694: break; ! 1695: } ! 1696: return; ! 1697: case 'F': ! 1698: switch (GET_CODE (XEXP (x, 0))) ! 1699: { ! 1700: case PRE_DEC: ! 1701: case PRE_INC: ! 1702: fprintf (file, ",mb"); ! 1703: break; ! 1704: case POST_DEC: ! 1705: case POST_INC: ! 1706: fprintf (file, ",ma"); ! 1707: break; ! 1708: default: ! 1709: break; ! 1710: } ! 1711: return; ! 1712: case 'G': ! 1713: output_global_address (file, x); ! 1714: return; ! 1715: case 0: /* Don't do anything special */ ! 1716: break; ! 1717: default: ! 1718: abort (); ! 1719: } ! 1720: if (GET_CODE (x) == REG) ! 1721: fprintf (file, "%s", reg_names [REGNO (x)]); ! 1722: else if (GET_CODE (x) == MEM) ! 1723: { ! 1724: int size = GET_MODE_SIZE (GET_MODE (x)); ! 1725: rtx base = XEXP (XEXP (x, 0), 0); ! 1726: switch (GET_CODE (XEXP (x, 0))) ! 1727: { ! 1728: case PRE_DEC: ! 1729: case POST_DEC: ! 1730: fprintf (file, "-%d(0,%s)", size, reg_names [REGNO (base)]); ! 1731: break; ! 1732: case PRE_INC: ! 1733: case POST_INC: ! 1734: fprintf (file, "%d(0,%s)", size, reg_names [REGNO (base)]); ! 1735: break; ! 1736: default: ! 1737: output_address (XEXP (x, 0)); ! 1738: break; ! 1739: } ! 1740: } ! 1741: else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) == SFmode) ! 1742: { ! 1743: union { double d; int i[2]; } u; ! 1744: union { float f; int i; } u1; ! 1745: u.i[0] = XINT (x, 0); u.i[1] = XINT (x, 1); ! 1746: u1.f = u.d; ! 1747: if (code == 'f') ! 1748: fprintf (file, "0r%.9g", u1.f); ! 1749: else ! 1750: fprintf (file, "0x%x", u1.i); ! 1751: } ! 1752: else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) != DImode) ! 1753: { ! 1754: union { double d; int i[2]; } u; ! 1755: u.i[0] = XINT (x, 0); u.i[1] = XINT (x, 1); ! 1756: fprintf (file, "0r%.20g", u.d); ! 1757: } ! 1758: else ! 1759: output_addr_const (file, x); ! 1760: } ! 1761: ! 1762: /* output a SYMBOL_REF or a CONST expression involving a SYMBOL_REF. */ ! 1763: ! 1764: void ! 1765: output_global_address (file, x) ! 1766: FILE *file; ! 1767: rtx x; ! 1768: { ! 1769: if (GET_CODE (x) == SYMBOL_REF && read_only_operand (x)) ! 1770: assemble_name (file, XSTR (x, 0)); ! 1771: else if (GET_CODE (x) == SYMBOL_REF) ! 1772: { ! 1773: assemble_name (file, XSTR (x, 0)); ! 1774: fprintf (file, "-$global$"); ! 1775: } ! 1776: else if (GET_CODE (x) == CONST) ! 1777: { ! 1778: char *sep = ""; ! 1779: int offset = 0; /* assembler wants -$global$ at end */ ! 1780: rtx base; ! 1781: ! 1782: if (GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF) ! 1783: { ! 1784: base = XEXP (XEXP (x, 0), 0); ! 1785: output_addr_const (file, base); ! 1786: } ! 1787: else ! 1788: if (GET_CODE(XEXP (XEXP (x, 0), 0)) == CONST_INT) ! 1789: offset = INTVAL (XEXP (XEXP (x, 0), 0)); ! 1790: else abort(); ! 1791: if (GET_CODE (XEXP (XEXP (x, 0), 1)) == SYMBOL_REF) ! 1792: { ! 1793: base = XEXP (XEXP (x, 0), 1); ! 1794: output_addr_const (file, base); ! 1795: } ! 1796: else ! 1797: if (GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT) ! 1798: offset = INTVAL (XEXP (XEXP (x, 0),1)); ! 1799: else abort(); ! 1800: if (GET_CODE (XEXP (x, 0)) == PLUS) ! 1801: sep= "+"; ! 1802: else ! 1803: if (GET_CODE (XEXP (x, 0)) == MINUS ! 1804: && (GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF)) ! 1805: sep = "-"; ! 1806: else abort(); ! 1807: if (!read_only_operand (base)) ! 1808: fprintf (file, "-$global$"); ! 1809: fprintf (file, "%s", sep); ! 1810: if (offset) fprintf(file,"%d", offset); ! 1811: } ! 1812: else ! 1813: output_addr_const (file, x); ! 1814: } ! 1815: ! 1816: /* MEM rtls here are never SYMBOL_REFs (I think), so fldws is safe. */ ! 1817: ! 1818: char * ! 1819: output_floatsisf2 (operands) ! 1820: rtx *operands; ! 1821: { ! 1822: if (GET_CODE (operands[1]) == MEM) ! 1823: return "fldws %1,%0\n\tfcnvxf,sgl,sgl %0,%0"; ! 1824: else if (FP_REG_P (operands[1])) ! 1825: return "fcnvxf,sgl,sgl %1,%0"; ! 1826: return "stwm %r1,4(0,30)\n\tfldws,mb -4(0,30),%0\n\tfcnvxf,sgl,sgl %0,%0"; ! 1827: } ! 1828: ! 1829: char * ! 1830: output_floatsidf2 (operands) ! 1831: rtx *operands; ! 1832: { ! 1833: if (GET_CODE (operands[1]) == MEM) ! 1834: return "fldws %1,%0\n\tfcnvxf,sgl,dbl %0,%0"; ! 1835: else if (FP_REG_P (operands[1])) ! 1836: return "fcnvxf,sgl,dbl %1,%0"; ! 1837: return "stwm %r1,4(0,30)\n\tfldws,mb -4(0,30),%0\n\tfcnvxf,sgl,dbl %0,%0"; ! 1838: } ! 1839: ! 1840: enum rtx_code ! 1841: reverse_relop (code) ! 1842: enum rtx_code code; ! 1843: { ! 1844: switch (code) ! 1845: { ! 1846: case GT: ! 1847: return LT; ! 1848: case LT: ! 1849: return GT; ! 1850: case GE: ! 1851: return LE; ! 1852: case LE: ! 1853: return GE; ! 1854: case LTU: ! 1855: return GTU; ! 1856: case GTU: ! 1857: return LTU; ! 1858: case GEU: ! 1859: return LEU; ! 1860: case LEU: ! 1861: return GEU; ! 1862: default: ! 1863: abort (); ! 1864: } ! 1865: } ! 1866: ! 1867: /* What the spectrum lacks in hardware, make up for in software. ! 1868: Compute a fairly good sequence of shift and add insns ! 1869: to make a multiply happen. This should punt and call millicode if ! 1870: the sequence gets too big, but that's hard to do at this stage ! 1871: because it involves clobbering several registers. Oh, well. */ ! 1872: ! 1873: #define ABS(x) ((x) < 0 ? -(x) : x) ! 1874: ! 1875: static rtx *mul_operands; ! 1876: ! 1877: void mul_by_constant_aux (); ! 1878: ! 1879: int ! 1880: emit_mul_by_constant (operands, unsignedp) ! 1881: rtx *operands; ! 1882: int unsignedp; ! 1883: { ! 1884: int constant; ! 1885: mul_operands = operands; ! 1886: ! 1887: constant = INTVAL (operands[2]); ! 1888: if (constant == 0) ! 1889: { ! 1890: /* Does happen, at least when not optimizing. */ ! 1891: emit_insn (gen_rtx (SET, VOIDmode, operands[0], const0_rtx)); ! 1892: return 1; ! 1893: } ! 1894: if (constant == 1) ! 1895: { ! 1896: emit_insn (gen_rtx (SET, VOIDmode, operands[0], operands[1])); ! 1897: return 1; ! 1898: } ! 1899: mul_by_constant_aux (ABS(constant)); ! 1900: if (constant < 0) ! 1901: emit_insn (gen_negsi2 (operands[0], operands[0])); ! 1902: return 1; ! 1903: } ! 1904: ! 1905: /* This greedy algorithm uses the fact that several constant ! 1906: multiplies are rather cheap on the PA: ! 1907: * 2^n (shift n places) ! 1908: * (2^n + 1) and (2^n - 1) (shift n and add or subtract) ! 1909: * 3, 5, 9 (sh1add, sh2add, sh3add) */ ! 1910: ! 1911: void ! 1912: mul_by_constant_aux (constant) ! 1913: int constant; ! 1914: { ! 1915: int log2, diff; /* log2 of constant and difference */ ! 1916: /* from a power of 2 */ ! 1917: int ffs_res; ! 1918: rtx xoperands[4]; ! 1919: ! 1920: xoperands[0] = mul_operands[0]; xoperands[1] = mul_operands[1]; ! 1921: log2 = log2_and_diff(constant, &diff); ! 1922: /* constant = 2^n or 2^n +/- 1? constant = 3,5,9 is handled better ! 1923: by the shift and add instructions. */ ! 1924: if (!diff || (diff == 1 && constant != 3 && constant != 5 && constant != 9) ! 1925: || (diff == -1 && constant != 3)) ! 1926: { ! 1927: emit_insn (gen_rtx (SET, VOIDmode, ! 1928: mul_operands[0], ! 1929: gen_rtx (ASHIFT, SImode, ! 1930: mul_operands[1], ! 1931: gen_rtx (CONST_INT, VOIDmode, log2)))); ! 1932: if (diff == 1) ! 1933: emit_insn (gen_addsi3 (mul_operands[0], ! 1934: mul_operands[0], ! 1935: mul_operands[1])); ! 1936: else if (diff == -1) ! 1937: emit_insn (gen_subsi3 (mul_operands[0], ! 1938: mul_operands[0], ! 1939: mul_operands[1])); ! 1940: } ! 1941: /* The things we try are ordered by how many binary digits of ! 1942: constant they consume */ ! 1943: /* is 2^n a factor of constant, n > 3? */ ! 1944: else if ((ffs_res = ffs (constant)) > 4) ! 1945: { ! 1946: mul_by_constant_aux ((constant >> (ffs_res - 1))); ! 1947: emit_insn (gen_rtx (SET, VOIDmode, ! 1948: mul_operands[0], ! 1949: gen_rtx (ASHIFT, SImode, ! 1950: mul_operands[0], ! 1951: gen_rtx (CONST_INT, VOIDmode, ! 1952: ffs_res - 1)))); ! 1953: } ! 1954: /* If the bottom n bits of constant are all 1's, that's the same as ! 1955: multiplying the total by 2^n and subtracting the multiplicand. */ ! 1956: else if ((ffs_res = ffs (~constant)) > 4) ! 1957: { ! 1958: mul_by_constant_aux ((constant >> (ffs_res - 1)) | 1); ! 1959: emit_insn (gen_rtx (SET, VOIDmode, ! 1960: mul_operands[0], ! 1961: gen_rtx (ASHIFT, SImode, ! 1962: mul_operands[0], ! 1963: gen_rtx (CONST_INT, VOIDmode, ! 1964: ffs_res - 1)))); ! 1965: emit_insn (gen_subsi3 (mul_operands[0], ! 1966: mul_operands[0], ! 1967: mul_operands[1])); ! 1968: } ! 1969: /* factor and test for possible shift/add combinations */ ! 1970: else if (test_factor_and_output (constant, 9)); ! 1971: else if (test_factor_and_output (constant, 8)); ! 1972: else if ((constant & 0x7) == 0x1) ! 1973: { ! 1974: mul_by_constant_aux (constant >> 3); ! 1975: emit_insn (gen_rtx (SET, VOIDmode, ! 1976: mul_operands[0], ! 1977: gen_rtx (PLUS, SImode, ! 1978: mul_operands[1], ! 1979: gen_rtx (MULT, SImode, ! 1980: mul_operands[0], ! 1981: gen_rtx (CONST_INT, VOIDmode, ! 1982: 8))))); ! 1983: } ! 1984: else if (test_factor_and_output (constant, 5)); ! 1985: else if (test_factor_and_output (constant, 4)); ! 1986: else if ((constant & 0x3) == 0x1) ! 1987: { ! 1988: mul_by_constant_aux (constant >> 2); ! 1989: emit_insn (gen_rtx (SET, VOIDmode, ! 1990: mul_operands[0], ! 1991: gen_rtx (PLUS, SImode, ! 1992: mul_operands[1], ! 1993: gen_rtx (MULT, SImode, ! 1994: mul_operands[0], ! 1995: gen_rtx (CONST_INT, VOIDmode, ! 1996: 4))))); ! 1997: } ! 1998: else if (test_factor_and_output (constant, 3)); ! 1999: else if (test_factor_and_output (constant, 2)); ! 2000: else ! 2001: { ! 2002: mul_by_constant_aux (constant >> 1); ! 2003: emit_insn (gen_rtx (SET, VOIDmode, ! 2004: mul_operands[0], ! 2005: gen_rtx (PLUS, SImode, ! 2006: mul_operands[1], ! 2007: gen_rtx (MULT, SImode, ! 2008: mul_operands[0], ! 2009: gen_rtx (CONST_INT, VOIDmode, ! 2010: 2))))); ! 2011: } ! 2012: } ! 2013: ! 2014: /* If FACTOR is a factor of CONSTANT, output the appropriate shift and ! 2015: add instruction */ ! 2016: ! 2017: int ! 2018: test_factor_and_output (constant, factor) ! 2019: int constant, factor; ! 2020: { ! 2021: rtx xoperands0, xoperands1, xoperands2; ! 2022: int shift, add_op = 0; ! 2023: ! 2024: xoperands0 = mul_operands[0]; ! 2025: if (!(constant % factor)) ! 2026: { ! 2027: switch (factor) ! 2028: { ! 2029: case 9: ! 2030: shift = 3; add_op = 1; break; ! 2031: case 8: ! 2032: shift = 3; break; ! 2033: case 5: ! 2034: shift = 2; add_op = 1; break; ! 2035: case 4: ! 2036: shift = 2; break; ! 2037: case 3: ! 2038: shift = 1; add_op = 1; break; ! 2039: case 2: ! 2040: shift = 1; break; ! 2041: default: ! 2042: abort (); ! 2043: } ! 2044: if (constant / factor == 1) ! 2045: { ! 2046: xoperands1 = mul_operands[1]; ! 2047: xoperands2 = mul_operands[1]; ! 2048: } ! 2049: else ! 2050: { ! 2051: mul_by_constant_aux (constant / factor); ! 2052: xoperands1 = mul_operands[0]; ! 2053: xoperands2 = mul_operands[0]; ! 2054: } ! 2055: if (add_op) ! 2056: emit_insn (gen_rtx (SET, VOIDmode, ! 2057: xoperands0, ! 2058: gen_rtx (PLUS, SImode, ! 2059: xoperands1, ! 2060: gen_rtx (MULT, SImode, ! 2061: xoperands2, ! 2062: gen_rtx (CONST_INT, VOIDmode, ! 2063: 1 << shift))))); ! 2064: else ! 2065: emit_insn (gen_rtx (SET, VOIDmode, ! 2066: xoperands0, ! 2067: gen_rtx (ASHIFT, SImode, ! 2068: xoperands1, ! 2069: gen_rtx (CONST_INT, VOIDmode, shift)))); ! 2070: return 1; ! 2071: } ! 2072: return 0; ! 2073: } ! 2074: ! 2075: /* This routine finds the floor_log2 of val and returns it and the ! 2076: difference between val and 2^log2(val). If val is 1 less than a ! 2077: power of 2, that power's log and -1 are returned. */ ! 2078: ! 2079: int log2_and_diff (val, diff) ! 2080: int val, *diff; ! 2081: { ! 2082: int log = floor_log2 (val); ! 2083: ! 2084: *diff = val - (1 << log); ! 2085: if (!*diff || *diff == 1) ! 2086: { ! 2087: return log; ! 2088: } ! 2089: else /* see if val is one less than a power */ ! 2090: { /* of 2 */ ! 2091: int alt_log = floor_log2 (val + 1); ! 2092: if (!((val + 1) - (1 << alt_log))) ! 2093: { ! 2094: *diff = -1; ! 2095: return alt_log; ! 2096: } ! 2097: } ! 2098: return log; ! 2099: } ! 2100: ! 2101: ! 2102: /* HP's millicode routines mean something special to the assembler. ! 2103: Keep track of which ones we have used. */ ! 2104: ! 2105: enum millicodes { remI, remU, divI, divU, mulI, mulU, end1000 }; ! 2106: static char imported[(int)end1000]; ! 2107: static char *milli_names[] = {"remI", "remU", "divI", "divU", "mulI", "mulU"}; ! 2108: static char import_string[] = ".IMPORT $$....,MILLICODE"; ! 2109: #define MILLI_START 10 ! 2110: ! 2111: static int ! 2112: import_milli (code) ! 2113: enum millicodes code; ! 2114: { ! 2115: char str[sizeof(import_string)]; ! 2116: ! 2117: if (!imported[(int)code]) ! 2118: { ! 2119: imported[(int)code] = 1; ! 2120: strcpy (str, import_string); ! 2121: strncpy (str + MILLI_START, milli_names[(int)code], 4); ! 2122: output_asm_insn (str, 0); ! 2123: } ! 2124: } ! 2125: ! 2126: /* The register constraints have put the operands and return value in ! 2127: the proper registers. */ ! 2128: ! 2129: char * ! 2130: output_mul_insn (unsignedp) ! 2131: int unsignedp; ! 2132: { ! 2133: if (unsignedp) ! 2134: { ! 2135: import_milli (mulU); ! 2136: return "bl $$mulU,31\n\tnop"; ! 2137: } ! 2138: else ! 2139: { ! 2140: import_milli (mulI); ! 2141: return "bl $$mulI,31\n\tnop"; ! 2142: } ! 2143: } ! 2144: ! 2145: /* If operands isn't NULL, then it's a CONST_INT with which we can do ! 2146: something */ ! 2147: ! 2148: ! 2149: /* Emit the rtl for doing a division by a constant. */ ! 2150: ! 2151: /* Do magic division millicodes exist for this value? */ ! 2152: ! 2153: static int magic_milli[]= {0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, ! 2154: 1, 1}; ! 2155: ! 2156: /* We'll use an array to keep track of the magic millicodes and ! 2157: whether or not we've used them already. [n][0] is signed, [n][1] is ! 2158: unsigned. */ ! 2159: ! 2160: ! 2161: static int div_milli[16][2]; ! 2162: ! 2163: int ! 2164: div_operand (op, mode) ! 2165: rtx op; ! 2166: enum machine_mode mode; ! 2167: { ! 2168: return (mode == SImode ! 2169: && ((GET_CODE (op) == REG && REGNO (op) == 25) ! 2170: || (GET_CODE (op) == CONST_INT && INTVAL (op) > 0 ! 2171: && INTVAL (op) < 16 && magic_milli[INTVAL (op)]))); ! 2172: } ! 2173: ! 2174: int ! 2175: emit_hpdiv_const(operands, unsignedp) ! 2176: rtx *operands; ! 2177: int unsignedp; ! 2178: { ! 2179: if (GET_CODE (operands[2]) == CONST_INT ! 2180: && INTVAL (operands[2]) > 0 ! 2181: && INTVAL (operands[2]) < 16 ! 2182: && magic_milli[INTVAL (operands[2])]) ! 2183: { ! 2184: emit_move_insn ( gen_rtx (REG, SImode, 26), operands[1]); ! 2185: emit ! 2186: (gen_rtx ! 2187: (PARALLEL, VOIDmode, ! 2188: gen_rtvec (5, gen_rtx (SET, VOIDmode, gen_rtx (REG, SImode, 29), ! 2189: gen_rtx (unsignedp ? UDIV : DIV, SImode, ! 2190: gen_rtx (REG, SImode, 26), ! 2191: operands[2])), ! 2192: gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, SImode, 0)), ! 2193: gen_rtx (CLOBBER, VOIDmode, gen_rtx (REG, SImode, 26)), ! 2194: gen_rtx (CLOBBER, VOIDmode, gen_rtx (REG, SImode, 25)), ! 2195: gen_rtx (CLOBBER, VOIDmode, gen_rtx (REG, SImode, 31))))); ! 2196: emit_move_insn (operands[0], gen_rtx (REG, SImode, 29)); ! 2197: return 1; ! 2198: } ! 2199: return 0; ! 2200: } ! 2201: ! 2202: char * ! 2203: output_div_insn(operands, unsignedp) ! 2204: rtx *operands; ! 2205: int unsignedp; ! 2206: { ! 2207: int divisor; ! 2208: ! 2209: /* If the divisor is a constant, try to use one of the special ! 2210: opcodes .*/ ! 2211: if (GET_CODE (operands[0]) == CONST_INT) ! 2212: { ! 2213: divisor = INTVAL (operands[0]); ! 2214: if (!div_milli[divisor][unsignedp]) ! 2215: { ! 2216: if (unsignedp) ! 2217: output_asm_insn (".IMPORT $$divU_%0,MILLICODE", operands); ! 2218: else ! 2219: output_asm_insn (".IMPORT $$divI_%0,MILLICODE", operands); ! 2220: div_milli[divisor][unsignedp] = 1; ! 2221: } ! 2222: if (unsignedp) ! 2223: return "bl $$divU_%0,31%#"; ! 2224: return "bl $$divI_%0,31%#"; ! 2225: } ! 2226: /* Divisor isn't a special constant. */ ! 2227: else ! 2228: { ! 2229: if (unsignedp) ! 2230: { ! 2231: import_milli (divU); ! 2232: return "bl $$divU,31%#"; ! 2233: } ! 2234: else ! 2235: { ! 2236: import_milli (divI); ! 2237: return "bl $$divI,31%#"; ! 2238: } ! 2239: } ! 2240: } ! 2241: ! 2242: /* Output a $$rem millicode to do mod. */ ! 2243: ! 2244: char * ! 2245: output_mod_insn (unsignedp) ! 2246: int unsignedp; ! 2247: { ! 2248: if (unsignedp) ! 2249: { ! 2250: import_milli (remU); ! 2251: return "bl $$remU,31%#"; ! 2252: } ! 2253: else ! 2254: { ! 2255: import_milli (remI); ! 2256: return "bl $$remI,31%#"; ! 2257: } ! 2258: } ! 2259: ! 2260: void ! 2261: output_arg_descriptor (insn) ! 2262: rtx insn; ! 2263: { ! 2264: char *arg_regs[4]; ! 2265: enum machine_mode arg_mode; ! 2266: rtx prev_insn; ! 2267: int i, output_flag = 0; ! 2268: int regno; ! 2269: ! 2270: for (i = 0; i < 4; i++) ! 2271: arg_regs[i] = 0; ! 2272: ! 2273: for (prev_insn = PREV_INSN (insn); GET_CODE (prev_insn) == INSN; ! 2274: prev_insn = PREV_INSN (prev_insn)) ! 2275: { ! 2276: if (!(GET_CODE (PATTERN (prev_insn)) == USE && ! 2277: GET_CODE (XEXP (PATTERN (prev_insn), 0)) == REG && ! 2278: FUNCTION_ARG_REGNO_P (REGNO (XEXP (PATTERN (prev_insn), 0))))) ! 2279: break; ! 2280: arg_mode = GET_MODE (XEXP (PATTERN (prev_insn), 0)); ! 2281: regno = REGNO (XEXP (PATTERN (prev_insn), 0)); ! 2282: if (regno >= 23 && regno <= 26) ! 2283: arg_regs[26 - regno] = "GR"; ! 2284: else if (!TARGET_SNAKE) /* fp args */ ! 2285: { ! 2286: if (arg_mode == SFmode) ! 2287: arg_regs[regno - 36] = "FR"; ! 2288: else ! 2289: { ! 2290: #ifdef hpux8 ! 2291: arg_regs[regno - 37] = "FR"; ! 2292: arg_regs[regno - 36] = "FU"; ! 2293: #else ! 2294: arg_regs[regno - 37] = "FU"; ! 2295: arg_regs[regno - 36] = "FR"; ! 2296: #endif ! 2297: } ! 2298: } ! 2299: else ! 2300: { ! 2301: if (arg_mode == SFmode) ! 2302: arg_regs[(regno - 56) / 2] = "FR"; ! 2303: else ! 2304: { ! 2305: arg_regs[regno - 58] = "FR"; ! 2306: arg_regs[regno - 57] = "FU"; ! 2307: } ! 2308: } ! 2309: } ! 2310: fputs ("\t.CALL ", asm_out_file); ! 2311: for (i = 0; i < 4; i++) ! 2312: { ! 2313: if (arg_regs[i]) ! 2314: { ! 2315: if (output_flag++) ! 2316: fputc (',', asm_out_file); ! 2317: fprintf (asm_out_file, "ARGW%d=%s", i, arg_regs[i]); ! 2318: } ! 2319: } ! 2320: fputc ('\n', asm_out_file); ! 2321: } ! 2322: ! 2323: /* Memory loads/stores to/from fp registers may need a scratch ! 2324: register in which to reload the address. */ ! 2325: ! 2326: enum reg_class ! 2327: secondary_reload_class (class, mode, in) ! 2328: enum reg_class class; ! 2329: enum machine_mode mode; ! 2330: rtx in; ! 2331: { ! 2332: int regno = true_regnum (in); ! 2333: ! 2334: if (regno >= FIRST_PSEUDO_REGISTER) ! 2335: regno = -1; ! 2336: ! 2337: if (class == FP_REGS || class == SNAKE_FP_REGS || class == HI_SNAKE_FP_REGS) ! 2338: { ! 2339: if (regno = -1 || !REGNO_OK_FOR_FP_P (regno)) ! 2340: return GENERAL_REGS; ! 2341: } ! 2342: return NO_REGS; ! 2343: } ! 2344: ! 2345: enum direction {none, upward, downward}; ! 2346: ! 2347: enum direction ! 2348: function_arg_padding (mode, type) ! 2349: enum machine_mode mode; ! 2350: tree type; ! 2351: { ! 2352: int size; ! 2353: ! 2354: if (mode == BLKmode) ! 2355: { ! 2356: if (type && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST) ! 2357: size = int_size_in_bytes (type) * BITS_PER_UNIT; ! 2358: else ! 2359: return upward; /* Don't know if this is right, but */ ! 2360: /* same as old definition. */ ! 2361: } ! 2362: else ! 2363: size = GET_MODE_BITSIZE (mode); ! 2364: if (size < PARM_BOUNDARY) ! 2365: return downward; ! 2366: else if (size % PARM_BOUNDARY) ! 2367: return upward; ! 2368: else ! 2369: return none; ! 2370: } ! 2371: ! 2372: int ! 2373: use_milli_regs (insn) ! 2374: rtx insn; ! 2375: { ! 2376: return (reg_mentioned_p (gen_rtx (REG, SImode, 1), insn) || ! 2377: reg_mentioned_p (gen_rtx (REG, SImode, 25), insn) || ! 2378: reg_mentioned_p (gen_rtx (REG, SImode, 26), insn) || ! 2379: reg_mentioned_p (gen_rtx (REG, SImode, 29), insn) || ! 2380: reg_mentioned_p (gen_rtx (REG, SImode, 31), insn)); ! 2381: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.