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