|
|
1.1 root 1: /* Subroutines for insn-output.c for Sun SPARC. 1.1.1.4 root 2: Copyright (C) 1987, 1988 Free Software Foundation, Inc. 1.1 root 3: Contributed by Michael Tiemann ([email protected]) 4: 5: This file is part of GNU CC. 6: 7: GNU CC is distributed in the hope that it will be useful, 8: but WITHOUT ANY WARRANTY. No author or distributor 9: accepts responsibility to anyone for the consequences of using it 10: or for whether it serves any particular purpose or works at all, 11: unless he says so in writing. Refer to the GNU CC General Public 12: License for full details. 13: 14: Everyone is granted permission to copy, modify and redistribute 15: GNU CC, but only under the conditions described in the 16: GNU CC General Public License. A copy of this license is 17: supposed to have been given to you along with GNU CC so you 18: can know your rights and responsibilities. It should be in a 19: file named COPYING. Among other things, the copyright notice 20: and this notice must be preserved on all copies. */ 21: 22: /* Global variables for machine-dependend things. */ 23: 24: /* This should go away if we pass floats to regs via 1.1.1.4 root 25: the stack instead of the frame, and if we learn how 26: to renumber all the registers when we don't do a save (hard!). */ 1.1 root 27: extern int frame_pointer_needed; 28: 29: static rtx find_addr_reg (); 30: 1.1.1.8 ! root 31: /* Return non-zero only if OP is a register of mode MODE, ! 32: or const0_rtx. */ ! 33: int ! 34: reg_or_0_operand (op, mode) 1.1.1.7 root 35: rtx op; 36: enum machine_mode mode; 37: { 1.1.1.8 ! root 38: return (op == const0_rtx || register_operand (op, mode)); 1.1.1.7 root 39: } 40: 1.1.1.4 root 41: /* Return non-zero if this pattern, as a source to a "SET", 42: is known to yield an instruction of unit size. */ 43: int 44: single_insn_src_p (op, mode) 45: rtx op; 46: enum machine_mode mode; 1.1 root 47: { 1.1.1.4 root 48: switch (GET_CODE (op)) 49: { 50: case CONST_INT: 51: if (SMALL_INT (op)) 52: return 1; 53: return 0; 54: 55: case REG: 1.1.1.7 root 56: return 1; 57: 1.1.1.4 root 58: case MEM: 1.1.1.8 ! root 59: #if 0 ! 60: /* This is not a single insn src, technically, ! 61: but output_delay_insn knows how to deal with it. */ 1.1.1.7 root 62: if (GET_CODE (XEXP (op, 0)) == SYMBOL_REF) 63: return 0; 1.1.1.8 ! root 64: #endif 1.1.1.4 root 65: return 1; 66: 67: /* We never need to negate or complement constants. */ 68: case NOT: 69: case NEG: 70: return 1; 71: 72: case PLUS: 73: case MINUS: 74: case AND: 75: case IOR: 76: case XOR: 77: case LSHIFT: 78: case ASHIFT: 79: case ASHIFTRT: 80: case LSHIFTRT: 81: if ((GET_CODE (XEXP (op, 0)) == CONST_INT && ! SMALL_INT (XEXP (op, 0))) 82: || (GET_CODE (XEXP (op, 1)) == CONST_INT && ! SMALL_INT (XEXP (op, 1)))) 83: return 0; 84: return 1; 85: 86: case SUBREG: 87: if (SUBREG_WORD (op) != 0) 88: return 0; 89: return single_insn_src_p (SUBREG_REG (op), mode); 90: 91: case SIGN_EXTEND: 92: case ZERO_EXTEND: 93: /* Lazy... could check for these. */ 94: return 0; 95: 96: /* Not doing floating point, since they probably 97: take longer than the branch slot they might fill. */ 98: case FLOAT_EXTEND: 99: case FLOAT_TRUNCATE: 100: case FLOAT: 101: case FIX: 102: case UNSIGNED_FLOAT: 103: case UNSIGNED_FIX: 104: return 0; 105: 106: default: 107: return 0; 108: } 1.1 root 109: } 1.1.1.4 root 110: 1.1.1.5 root 111: /* Return truth value of whether OP can be used as an operands in a three 112: address arithmetic insn (such as add %o1,7,%l2) of mode MODE. */ 1.1 root 113: 114: int 115: arith_operand (op, mode) 116: rtx op; 117: enum machine_mode mode; 118: { 119: return (register_operand (op, mode) 120: || (GET_CODE (op) == CONST_INT && SMALL_INT (op))); 121: } 122: 1.1.1.5 root 123: /* Return truth value of whether OP can be used as an operand in a two 124: address arithmetic insn (such as set 123456,%o4) of mode MODE. */ 1.1.1.4 root 125: 1.1 root 126: int 127: arith32_operand (op, mode) 128: rtx op; 129: enum machine_mode mode; 130: { 131: return (register_operand (op, mode) || GET_CODE (op) == CONST_INT); 132: } 133: 1.1.1.5 root 134: /* Return truth value of whether OP is a integer which fits the 135: range constraining immediate operands in three-address insns. */ 136: 1.1.1.3 root 137: int 138: small_int (op, mode) 139: rtx op; 140: enum machine_mode mode; 141: { 142: return (GET_CODE (op) == CONST_INT && SMALL_INT (op)); 143: } 144: 1.1 root 145: /* Return the best assembler insn template 146: for moving operands[1] into operands[0] as a fullword. */ 147: 148: static char * 149: singlemove_string (operands) 150: rtx *operands; 151: { 152: if (GET_CODE (operands[0]) == MEM) 1.1.1.8 ! root 153: { ! 154: if (GET_CODE (operands[1]) != MEM) ! 155: if (CONSTANT_ADDRESS_P (XEXP (operands[0], 0))) ! 156: { ! 157: if (! ((cc_prev_status.flags & CC_KNOW_HI_G1) ! 158: && cc_prev_status.mdep == XEXP (operands[0], 0))) ! 159: output_asm_insn ("sethi %%hi(%m0),%%g1", operands); ! 160: cc_status.flags |= CC_KNOW_HI_G1; ! 161: cc_status.mdep = XEXP (operands[0], 0); ! 162: return "st %1,[%%lo(%m0)+%%g1]"; ! 163: } ! 164: else ! 165: return "st %r1,%0"; ! 166: else ! 167: { ! 168: rtx xoperands[2]; ! 169: ! 170: cc_status.flags &= ~CC_F0_IS_0; ! 171: xoperands[0] = gen_rtx (REG, SFmode, 32); ! 172: xoperands[1] = operands[1]; ! 173: output_asm_insn (singlemove_string (xoperands), xoperands); ! 174: xoperands[1] = xoperands[0]; ! 175: xoperands[0] = operands[0]; ! 176: output_asm_insn (singlemove_string (xoperands), xoperands); ! 177: return ""; ! 178: } ! 179: } 1.1 root 180: if (GET_CODE (operands[1]) == MEM) 1.1.1.8 ! root 181: { ! 182: if (CONSTANT_ADDRESS_P (XEXP (operands[1], 0))) ! 183: { ! 184: if (! ((cc_prev_status.flags & CC_KNOW_HI_G1) ! 185: && cc_prev_status.mdep == XEXP (operands[1], 0))) ! 186: output_asm_insn ("sethi %%hi(%m1),%%g1", operands); ! 187: cc_status.flags |= CC_KNOW_HI_G1; ! 188: cc_status.mdep = XEXP (operands[1], 0); ! 189: return "ld [%%lo(%m1)+%%g1],%0"; ! 190: } ! 191: return "ld %1,%0"; ! 192: } 1.1.1.3 root 193: return "mov %1,%0"; 1.1 root 194: } 195: 196: /* Output assembler code to perform a doubleword move insn 197: with operands OPERANDS. */ 198: 199: char * 200: output_move_double (operands) 201: rtx *operands; 202: { 203: enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype0, optype1; 204: rtx latehalf[2]; 205: rtx addreg0 = 0, addreg1 = 0; 206: 207: /* First classify both operands. */ 208: 209: if (REG_P (operands[0])) 210: optype0 = REGOP; 211: else if (offsetable_memref_p (operands[0])) 212: optype0 = OFFSOP; 213: else if (GET_CODE (operands[0]) == MEM) 214: optype0 = MEMOP; 215: else 216: optype0 = RNDOP; 217: 218: if (REG_P (operands[1])) 219: optype1 = REGOP; 220: else if (CONSTANT_P (operands[1]) 221: || GET_CODE (operands[1]) == CONST_DOUBLE) 222: optype1 = CNSTOP; 223: else if (offsetable_memref_p (operands[1])) 224: optype1 = OFFSOP; 225: else if (GET_CODE (operands[1]) == MEM) 1.1.1.7 root 226: optype1 = MEMOP; 1.1 root 227: else 228: optype1 = RNDOP; 229: 230: /* Check for the cases that the operand constraints are not 231: supposed to allow to happen. Abort if we get one, 232: because generating code for these cases is painful. */ 233: 234: if (optype0 == RNDOP || optype1 == RNDOP) 235: abort (); 236: 237: /* If an operand is an unoffsettable memory ref, find a register 238: we can increment temporarily to make it refer to the second word. */ 239: 240: if (optype0 == MEMOP) 241: addreg0 = find_addr_reg (operands[0]); 242: 243: if (optype1 == MEMOP) 244: addreg1 = find_addr_reg (operands[1]); 245: 246: /* Ok, we can do one word at a time. 247: Normally we do the low-numbered word first, 248: but if either operand is autodecrementing then we 249: do the high-numbered word first. 250: 251: In either case, set up in LATEHALF the operands to use 252: for the high-numbered word and in some cases alter the 253: operands in OPERANDS to be suitable for the low-numbered word. */ 254: 255: if (optype0 == REGOP) 256: latehalf[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1); 257: else if (optype0 == OFFSOP) 258: latehalf[0] = adj_offsetable_operand (operands[0], 4); 259: else 260: latehalf[0] = operands[0]; 261: 262: if (optype1 == REGOP) 263: latehalf[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1); 264: else if (optype1 == OFFSOP) 265: latehalf[1] = adj_offsetable_operand (operands[1], 4); 266: else if (optype1 == CNSTOP) 267: { 268: if (CONSTANT_P (operands[1])) 269: latehalf[1] = const0_rtx; 270: else if (GET_CODE (operands[1]) == CONST_DOUBLE) 271: { 272: latehalf[1] = gen_rtx (CONST_INT, VOIDmode, XINT (operands[1], 1)); 273: operands[1] = gen_rtx (CONST_INT, VOIDmode, XINT (operands[1], 0)); 274: } 275: } 276: else 277: latehalf[1] = operands[1]; 278: 279: /* If the first move would clobber the source of the second one, 1.1.1.3 root 280: do them in the other order. 281: 282: RMS says "This happens only for registers; 1.1 root 283: such overlap can't happen in memory unless the user explicitly 1.1.1.3 root 284: sets it up, and that is an undefined circumstance." 285: 286: but it happens on the sparc when loading parameter registers, 287: so I am going to define that circumstance, and make it work 288: as expected. */ 289: 290: /* Easy case: try moving both words at once. */ 1.1.1.8 ! root 291: /* First check for moving between an even/odd register pair ! 292: and a memory location. */ ! 293: if ((optype0 == REGOP && optype1 != REGOP && optype1 != CNSTOP 1.1.1.3 root 294: && (REGNO (operands[0]) & 1) == 0) 1.1.1.8 ! root 295: || (optype0 != REGOP && optype1 != CNSTOP && optype1 == REGOP 1.1.1.3 root 296: && (REGNO (operands[1]) & 1) == 0)) 297: { 298: rtx op1, op2; 299: rtx base = 0, offset = const0_rtx; 1.1 root 300: 1.1.1.8 ! root 301: /* OP1 gets the register pair, and OP2 gets the memory address. */ 1.1.1.3 root 302: if (optype0 == REGOP) 303: op1 = operands[0], op2 = XEXP (operands[1], 0); 304: else 305: op1 = operands[1], op2 = XEXP (operands[0], 0); 306: 1.1.1.8 ! root 307: /* Now see if we can trust the address to be 8-byte aligned. */ 1.1.1.4 root 308: /* Trust global variables. */ 1.1.1.8 ! root 309: if (CONSTANT_ADDRESS_P (op2)) 1.1.1.4 root 310: { 1.1.1.8 ! root 311: operands[0] = op1; ! 312: operands[1] = op2; ! 313: if (! ((cc_prev_status.flags & CC_KNOW_HI_G1) ! 314: && cc_prev_status.mdep == op2)) ! 315: output_asm_insn ("sethi %%hi(%m1),%%g1", operands); ! 316: cc_status.flags |= CC_KNOW_HI_G1; ! 317: cc_status.mdep = op2; 1.1.1.4 root 318: if (op1 == operands[0]) 1.1.1.8 ! root 319: return "ldd [%%lo(%m1)+%%g1],%0"; 1.1.1.4 root 320: else 1.1.1.8 ! root 321: return "std [%%lo(%m1)+%%g1],%0"; 1.1.1.4 root 322: } 1.1.1.3 root 323: 1.1.1.4 root 324: if (GET_CODE (op2) == PLUS) 325: { 326: if (GET_CODE (XEXP (op2, 0)) == REG) 327: base = XEXP (op2, 0), offset = XEXP (op2, 1); 328: else if (GET_CODE (XEXP (op2, 1)) == REG) 329: base = XEXP (op2, 1), offset = XEXP (op2, 0); 330: } 331: 332: /* Trust round enough offsets from the stack or frame pointer. */ 1.1.1.3 root 333: if (base 1.1.1.4 root 334: && (REGNO (base) == FRAME_POINTER_REGNUM 335: || REGNO (base) == STACK_POINTER_REGNUM)) 336: { 337: if (GET_CODE (offset) == CONST_INT 338: && (INTVAL (offset) & 0x7) == 0) 339: { 340: if (op1 == operands[0]) 341: return "ldd %1,%0"; 342: else 343: return "std %1,%0"; 344: } 345: } 346: else 347: { 1.1.1.8 ! root 348: /* We know structs not on the stack are properly aligned. ! 349: Since a double asks for 8-byte alignment, ! 350: we know it must have got that if it is in a struct. ! 351: But a DImode need not be 8-byte aligned, because it could be a ! 352: struct containing two ints or pointers. */ ! 353: if (GET_CODE (operands[1]) == MEM && GET_MODE (operands[1]) == DFmode ! 354: && MEM_IN_STRUCT_P (operands[1])) 1.1.1.4 root 355: return "ldd %1,%0"; 1.1.1.8 ! root 356: else if (GET_CODE (operands[0]) == MEM ! 357: && GET_MODE (operands[0]) == DFmode ! 358: && MEM_IN_STRUCT_P (operands[0])) 1.1.1.4 root 359: return "std %1,%0"; 360: } 1.1.1.3 root 361: } 1.1.1.4 root 362: 1.1 root 363: if (optype0 == REGOP && optype1 == REGOP 364: && REGNO (operands[0]) == REGNO (latehalf[1])) 365: { 366: /* Make any unoffsetable addresses point at high-numbered word. */ 367: if (addreg0) 368: output_asm_insn ("add %0,0x4,%0", &addreg0); 369: if (addreg1) 370: output_asm_insn ("add %0,0x4,%0", &addreg1); 371: 372: /* Do that word. */ 373: output_asm_insn (singlemove_string (latehalf), latehalf); 374: 375: /* Undo the adds we just did. */ 376: if (addreg0) 377: output_asm_insn ("add %0,-0x4,%0", &addreg0); 378: if (addreg1) 379: output_asm_insn ("add %0,-0x4,%0", &addreg0); 380: 381: /* Do low-numbered word. */ 382: return singlemove_string (operands); 383: } 1.1.1.3 root 384: else if (optype0 == REGOP && optype1 != REGOP 1.1.1.8 ! root 385: && reg_overlap_mentioned_p (operands[0], operands[1])) 1.1.1.3 root 386: { 387: /* Do the late half first. */ 388: output_asm_insn (singlemove_string (latehalf), latehalf); 389: /* Then clobber. */ 390: return singlemove_string (operands); 391: } 1.1 root 392: 393: /* Normal case: do the two words, low-numbered first. */ 394: 395: output_asm_insn (singlemove_string (operands), operands); 396: 397: /* Make any unoffsetable addresses point at high-numbered word. */ 398: if (addreg0) 399: output_asm_insn ("add %0,0x4,%0", &addreg0); 400: if (addreg1) 401: output_asm_insn ("add %0,0x4,%0", &addreg1); 402: 403: /* Do that word. */ 404: output_asm_insn (singlemove_string (latehalf), latehalf); 405: 406: /* Undo the adds we just did. */ 407: if (addreg0) 408: output_asm_insn ("add %0,-0x4,%0", &addreg0); 409: if (addreg1) 410: output_asm_insn ("add %0,-0x4,%0", &addreg1); 411: 412: return ""; 413: } 414: 415: static char * 416: output_fp_move_double (operands) 417: rtx *operands; 418: { 419: if (FP_REG_P (operands[0])) 420: { 421: if (FP_REG_P (operands[1])) 422: { 423: output_asm_insn ("fmovs %1,%0", operands); 424: operands[0] = gen_rtx (REG, VOIDmode, REGNO (operands[0]) + 1); 425: operands[1] = gen_rtx (REG, VOIDmode, REGNO (operands[1]) + 1); 426: return "fmovs %1,%0"; 427: } 428: if (GET_CODE (operands[1]) == REG) 429: { 1.1.1.3 root 430: if ((REGNO (operands[1]) & 1) == 0) 431: return "std %1,[%%fp-8]\n\tldd [%%fp-8],%0"; 432: else 433: { 434: rtx xoperands[3]; 435: xoperands[0] = operands[0]; 436: xoperands[1] = operands[1]; 437: xoperands[2] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1); 438: output_asm_insn ("st %2,[%%fp-4]\n\tst %1,[%%fp-8]\n\tldd [%%fp-8],%0", xoperands); 439: return ""; 440: } 1.1 root 441: } 1.1.1.4 root 442: if (GET_CODE (XEXP (operands[1], 0)) == PLUS 443: && (XEXP (XEXP (operands[1], 0), 0) == frame_pointer_rtx 444: || XEXP (XEXP (operands[1], 0), 0) == stack_pointer_rtx) 445: && GET_CODE (XEXP (XEXP (operands[1], 0), 1)) == CONST_INT 446: && (INTVAL (XEXP (XEXP (operands[1], 0), 1)) & 0x7) != 0) 447: { 448: rtx xoperands[2]; 449: output_asm_insn ("ld %1,%0", operands); 450: xoperands[0] = gen_rtx (REG, GET_MODE (operands[0]), 451: REGNO (operands[0]) + 1); 452: xoperands[1] = gen_rtx (MEM, GET_MODE (operands[1]), 453: plus_constant (XEXP (operands[1], 0), 4)); 454: output_asm_insn ("ld %1,%0", xoperands); 455: return ""; 456: } 1.1.1.8 ! root 457: if (CONSTANT_ADDRESS_P (XEXP (operands[1], 0))) ! 458: { ! 459: if (! ((cc_prev_status.flags & CC_KNOW_HI_G1) ! 460: && cc_prev_status.mdep == XEXP (operands[1], 0))) ! 461: output_asm_insn ("sethi %%hi(%m1),%%g1", operands); ! 462: cc_status.flags |= CC_KNOW_HI_G1; ! 463: cc_status.mdep = XEXP (operands[1], 0); ! 464: return "ldd [%%lo(%m1)+%%g1],%0"; ! 465: } 1.1 root 466: return "ldd %1,%0"; 467: } 468: else if (FP_REG_P (operands[1])) 469: { 470: if (GET_CODE (operands[0]) == REG) 471: { 1.1.1.3 root 472: if ((REGNO (operands[0]) & 1) == 0) 473: return "std %1,[%%fp-8]\n\tldd [%%fp-8],%0"; 474: else 475: { 476: rtx xoperands[3]; 477: xoperands[2] = operands[1]; 478: xoperands[1] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1); 479: xoperands[0] = operands[0]; 480: output_asm_insn ("std %2,[%%fp-8]\n\tld [%%fp-4],%1\n\tld [%%fp-8],%0", xoperands); 481: return ""; 482: } 1.1 root 483: } 1.1.1.4 root 484: if (GET_CODE (XEXP (operands[0], 0)) == PLUS 485: && (XEXP (XEXP (operands[0], 0), 0) == frame_pointer_rtx 486: || XEXP (XEXP (operands[0], 0), 0) == stack_pointer_rtx) 487: && GET_CODE (XEXP (XEXP (operands[0], 0), 1)) == CONST_INT 488: && (INTVAL (XEXP (XEXP (operands[0], 0), 1)) & 0x7) != 0) 489: { 490: rtx xoperands[2]; 1.1.1.8 ! root 491: output_asm_insn ("st %r1,%0", operands); 1.1.1.4 root 492: xoperands[1] = gen_rtx (REG, GET_MODE (operands[1]), 493: REGNO (operands[1]) + 1); 494: xoperands[0] = gen_rtx (MEM, GET_MODE (operands[0]), 495: plus_constant (XEXP (operands[0], 0), 4)); 1.1.1.8 ! root 496: output_asm_insn ("st %r1,%0", xoperands); 1.1.1.4 root 497: return ""; 498: } 1.1.1.8 ! root 499: if (CONSTANT_ADDRESS_P (XEXP (operands[0], 0))) ! 500: { ! 501: if (! ((cc_prev_status.flags & CC_KNOW_HI_G1) ! 502: && cc_prev_status.mdep == XEXP (operands[0], 0))) ! 503: output_asm_insn ("sethi %%hi(%m0),%%g1", operands); ! 504: cc_status.flags |= CC_KNOW_HI_G1; ! 505: cc_status.mdep = XEXP (operands[0], 0); ! 506: return "std %1,[%%lo(%m0)+%%g1]"; ! 507: } 1.1 root 508: return "std %1,%0"; 509: } 1.1.1.3 root 510: else abort (); 1.1 root 511: } 512: 513: /* Return a REG that occurs in ADDR with coefficient 1. 514: ADDR can be effectively incremented by incrementing REG. */ 515: 516: static rtx 517: find_addr_reg (addr) 518: rtx addr; 519: { 520: while (GET_CODE (addr) == PLUS) 521: { 522: if (GET_CODE (XEXP (addr, 0)) == REG) 523: addr = XEXP (addr, 0); 524: if (GET_CODE (XEXP (addr, 1)) == REG) 525: addr = XEXP (addr, 1); 526: if (CONSTANT_P (XEXP (addr, 0))) 527: addr = XEXP (addr, 1); 528: if (CONSTANT_P (XEXP (addr, 1))) 529: addr = XEXP (addr, 0); 530: } 531: if (GET_CODE (addr) == REG) 532: return addr; 533: return 0; 534: } 535: 1.1.1.8 ! root 536: void ! 537: output_sized_memop (opname, mode) ! 538: char *opname; ! 539: enum machine_mode mode; ! 540: { ! 541: extern struct _iobuf *asm_out_file; ! 542: ! 543: static char *ld_size_suffix[] = { "ub", "uh", "", "?", "d" }; ! 544: static char *st_size_suffix[] = { "b", "h", "", "?", "d" }; ! 545: char *modename ! 546: = (opname[0] == 'l' ? ld_size_suffix : st_size_suffix)[GET_MODE_SIZE (mode) >> 1]; ! 547: ! 548: fprintf (asm_out_file, "\t%s%s", opname, modename); ! 549: } ! 550: 1.1 root 551: /* Load the address specified by OPERANDS[3] into the register 552: specified by OPERANDS[0]. 553: 554: OPERANDS[3] may be the result of a sum, hence it could either be: 555: 556: (1) CONST 557: (2) REG 558: (2) REG + CONST_INT 559: (3) REG + REG + CONST_INT 1.1.1.8 ! root 560: (4) REG + REG (special case of 3). 1.1 root 561: 1.1.1.5 root 562: Note that (3) is not a legitimate address. 1.1 root 563: All cases are handled here. */ 564: 565: void 566: output_load_address (operands) 567: rtx *operands; 568: { 569: rtx base, offset; 570: 571: if (CONSTANT_P (operands[3])) 572: { 573: output_asm_insn ("set %3,%0", operands); 574: return; 575: } 576: 577: if (REG_P (operands[3])) 578: { 579: if (REGNO (operands[0]) != REGNO (operands[3])) 580: output_asm_insn ("mov %3,%0", operands); 581: return; 582: } 583: 1.1.1.8 ! root 584: if (GET_CODE (operands[3]) != PLUS) ! 585: abort (); ! 586: 1.1 root 587: base = XEXP (operands[3], 0); 588: offset = XEXP (operands[3], 1); 589: 590: if (GET_CODE (base) == CONST_INT) 591: { 592: rtx tmp = base; 593: base = offset; 594: offset = tmp; 595: } 596: 597: if (GET_CODE (offset) != CONST_INT) 1.1.1.8 ! root 598: { ! 599: /* Operand is (PLUS (REG) (REG)). */ ! 600: base = operands[3]; ! 601: offset = const0_rtx; ! 602: } 1.1 root 603: 604: if (REG_P (base)) 605: { 606: operands[6] = base; 607: operands[7] = offset; 608: if (SMALL_INT (offset)) 609: output_asm_insn ("add %6,%7,%0", operands); 610: else 611: output_asm_insn ("set %7,%0\n\tadd %0,%6,%0", operands); 612: } 1.1.1.8 ! root 613: else if (GET_CODE (base) == PLUS) 1.1 root 614: { 615: operands[6] = XEXP (base, 0); 616: operands[7] = XEXP (base, 1); 617: operands[8] = offset; 618: 619: if (SMALL_INT (offset)) 620: output_asm_insn ("add %6,%7,%0\n\tadd %0,%8,%0", operands); 621: else 622: output_asm_insn ("set %8,%0\n\tadd %0,%6,%0\n\tadd %0,%7,%0", operands); 623: } 1.1.1.8 ! root 624: else ! 625: abort (); 1.1 root 626: } 627: 1.1.1.5 root 628: /* Output code to place a size count SIZE in register REG. 629: If SIZE is round, then assume that we can use alignment 630: based on that roundness, and return an integer saying 631: what alignment (roundness, transfer size) we will be using. 632: 633: Because block moves are pipelined, we don't include the 634: first element in the transfer of SIZE to REG. */ 635: 636: static int 637: output_size_for_block_move (size, reg) 638: rtx size, reg; 639: { 640: int align; 641: rtx xoperands[2]; 642: xoperands[0] = reg; 643: 644: /* First, figure out best alignment we may assume. */ 645: if (REG_P (size)) 646: { 647: xoperands[1] = size; 648: output_asm_insn ("sub %1,1,%0", xoperands); 649: align = 1; 650: } 651: else 652: { 653: int i = INTVAL (size); 654: 655: if (i & 1) 656: align = 1; 657: else if (i & 3) 658: align = 2; 659: else 660: align = 4; 661: 662: /* predecrement count. */ 663: i -= align; 664: if (i < 0) abort (); 665: 666: xoperands[1] = gen_rtx (CONST_INT, VOIDmode, i); 667: 668: output_asm_insn ("set %1,%0", xoperands); 669: } 670: return align; 671: } 672: 673: /* Emit code to perform a block move. 674: 675: OPERANDS[0] is the destination. 676: OPERANDS[1] is the source. 677: OPERANDS[2] is the size. 1.1.1.8 ! root 678: OPERANDS[3..5] are pseudos we can safely clobber as temps. */ 1.1.1.5 root 679: 1.1 root 680: char * 681: output_block_move (operands) 682: rtx *operands; 683: { 1.1.1.5 root 684: /* A vector for our computed operands. Note that load_output_address 1.1.1.8 ! root 685: makes use of (and can clobber) up to the 8th element of this vector. */ 1.1.1.5 root 686: rtx xoperands[10]; 1.1.1.8 ! root 687: rtx zoperands[10]; 1.1 root 688: static int movstrsi_label = 0; 1.1.1.5 root 689: int align = -1; /* not yet known */ 1.1 root 690: int i, j; 691: 1.1.1.8 ! root 692: xoperands[0] = operands[0]; ! 693: xoperands[1] = operands[1]; ! 694: xoperands[2] = operands[3]; 1.1.1.5 root 695: 1.1 root 696: /* Since we clobber untold things, nix the condition codes. */ 697: CC_STATUS_INIT; 698: 1.1.1.5 root 699: /* Recognize special cases of block moves. These occur 700: when GNU C++ is forced to treat something as BLKmode 701: to keep it in memory, when its mode could be represented 1.1.1.8 ! root 702: with something smaller. ! 703: ! 704: We cannot do this for global variables, since we don't know ! 705: what pages they don't cross. Sigh. */ 1.1.1.5 root 706: if (GET_CODE (operands[2]) == CONST_INT 1.1.1.8 ! root 707: && INTVAL (operands[2]) <= 16 ! 708: && ! CONSTANT_ADDRESS_P (operands[0]) ! 709: && ! CONSTANT_ADDRESS_P (operands[1])) 1.1.1.5 root 710: { 711: int size = INTVAL (operands[2]); 712: 1.1.1.8 ! root 713: cc_status.flags &= ~CC_KNOW_HI_G1; 1.1.1.5 root 714: if (size & 1) 715: { 716: if (memory_address_p (QImode, plus_constant (xoperands[0], size)) 717: && memory_address_p (QImode, plus_constant (xoperands[1], size))) 718: { 1.1.1.8 ! root 719: /* We will store different integers into this particular RTX. */ ! 720: xoperands[2] = gen_rtx (CONST_INT, VOIDmode, 13); 1.1.1.5 root 721: for (i = size-1; i >= 0; i--) 722: { 723: INTVAL (xoperands[2]) = i; 724: output_asm_insn ("ldub [%a1+%2],%%g1\n\tstb %%g1,[%a0+%2]", 725: xoperands); 726: } 727: return ""; 728: } 729: } 730: else if (size & 2) 731: { 732: if (memory_address_p (HImode, plus_constant (xoperands[0], size)) 733: && memory_address_p (HImode, plus_constant (xoperands[1], size))) 734: { 1.1.1.8 ! root 735: /* We will store different integers into this particular RTX. */ ! 736: xoperands[2] = gen_rtx (CONST_INT, VOIDmode, 13); 1.1.1.5 root 737: for (i = (size>>1)-1; i >= 0; i--) 738: { 739: INTVAL (xoperands[2]) = i<<1; 740: output_asm_insn ("lduh [%a1+%2],%%g1\n\tsth %%g1,[%a0+%2]", 741: xoperands); 742: } 743: return ""; 744: } 745: } 746: else 747: { 748: if (memory_address_p (SImode, plus_constant (xoperands[0], size)) 749: && memory_address_p (SImode, plus_constant (xoperands[1], size))) 750: { 1.1.1.8 ! root 751: /* We will store different integers into this particular RTX. */ ! 752: xoperands[2] = gen_rtx (CONST_INT, VOIDmode, 13); 1.1.1.5 root 753: for (i = (size>>2)-1; i >= 0; i--) 754: { 755: INTVAL (xoperands[2]) = i<<2; 756: output_asm_insn ("ld [%a1+%2],%%g1\n\tst %%g1,[%a0+%2]", 757: xoperands); 758: } 759: return ""; 760: } 761: } 762: } 763: 1.1.1.8 ! root 764: /* This is the size of the transfer. ! 765: Either use the register which already contains the size, ! 766: or use a free register (used by no operands). ! 767: Also emit code to decrement the size value by ALIGN. */ ! 768: align = output_size_for_block_move (operands[2], operands[3]); 1.1.1.5 root 769: 1.1.1.8 ! root 770: zoperands[0] = operands[0]; ! 771: zoperands[3] = plus_constant (operands[0], align); ! 772: output_load_address (zoperands); 1.1 root 773: 774: xoperands[3] = gen_rtx (CONST_INT, VOIDmode, movstrsi_label++); 775: xoperands[4] = gen_rtx (CONST_INT, VOIDmode, align); 776: 1.1.1.5 root 777: if (align == 1) 778: output_asm_insn ("\nLm%3:\n\tldub [%1+%2],%%g1\n\tsubcc %2,%4,%2\n\tbge Lm%3\n\tstb %%g1,[%0+%2]", xoperands); 779: else if (align == 2) 780: output_asm_insn ("\nLm%3:\n\tlduh [%1+%2],%%g1\n\tsubcc %2,%4,%2\n\tbge Lm%3\n\tsth %%g1,[%0+%2]", xoperands); 781: else 782: output_asm_insn ("\nLm%3:\n\tld [%1+%2],%%g1\n\tsubcc %2,%4,%2\n\tbge Lm%3\n\tst %%g1,[%0+%2]", xoperands); 1.1 root 783: return ""; 784: } 1.1.1.5 root 785: 786: /* What the sparc lacks in hardware, make up for in software. 787: Compute a fairly good sequence of shift and add insns 788: to make a multiply happen. */ 1.1 root 789: 790: #define ABS(x) ((x) < 0 ? -(x) : x) 791: 792: char * 793: output_mul_by_constant (insn, operands, unsignedp) 794: rtx insn; 795: rtx *operands; 796: int unsignedp; 797: { 798: int c; /* Size of constant */ 799: int shifts[BITS_PER_WORD]; /* Table of shifts */ 1.1.1.4 root 800: unsigned int p, log; /* A power of two, and its log */ 1.1 root 801: int d1, d2; /* Differences of c and p */ 802: int first = 1; /* True if dst has unknown data in it */ 803: int i; 804: 805: c = INTVAL (operands[2]); 806: if (c == 0) 807: { 808: /* should not happen. */ 809: abort (); 810: if (GET_CODE (operands[0]) == MEM) 811: return "st %%g0,%0"; 812: return "mov %%g0,%0"; 813: } 814: 815: output_asm_insn ("! start open coded multiply"); 816: 817: /* Clear out the table of shifts. */ 818: for (i = 0; i < BITS_PER_WORD; ++i) 819: shifts[i] = 0; 820: 821: while (c) 822: { 823: /* Find the power of two nearest ABS(c) */ 824: p = 1, log = 0; 825: do 826: { 827: d1 = ABS(c) - p; 828: p *= 2; 829: ++log; 830: } 831: while (p < ABS(c)); 832: d2 = p - ABS(c); 833: 834: /* Make an appropriate entry in shifts for p. */ 835: if (d2 < d1) 836: { 837: shifts[log] = c < 0 ? -1 : 1; 838: c = c < 0 ? d2 : -d2; 839: } 840: else 841: { 842: shifts[log - 1] = c < 0 ? -1 : 1; 843: c = c < 0 ? -d1 : d1; 844: } 845: } 846: 847: /* Take care of the first insn in sequence. 848: We know we have at least one. */ 849: 850: /* A value of -1 in shifts says to subtract that power of two, and a value 851: of 1 says to add that power of two. */ 852: for (i = 0; ; i++) 853: if (shifts[i]) 854: { 855: if (i) 856: { 857: operands[2] = gen_rtx (CONST_INT, VOIDmode, i); 1.1.1.5 root 858: output_asm_insn ("sll %1,%2,%%g1", operands); 1.1 root 859: } 1.1.1.5 root 860: else output_asm_insn ("mov %1,%%g1", operands); 1.1 root 861: 862: log = i; 863: if (shifts[i] < 0) 1.1.1.5 root 864: output_asm_insn ("sub %%g0,%%g1,%0", operands); 1.1 root 865: else 1.1.1.5 root 866: output_asm_insn ("mov %%g1,%0", operands); 1.1 root 867: break; 868: } 869: 870: /* A value of -1 in shifts says to subtract that power of two, and a value 871: of 1 says to add that power of two--continued. */ 872: for (i += 1; i < BITS_PER_WORD; ++i) 873: if (shifts[i]) 874: { 875: if (i - log > 0) 876: { 877: operands[2] = gen_rtx (CONST_INT, VOIDmode, i - log); 1.1.1.5 root 878: output_asm_insn ("sll %%g1,%2,%%g1", operands); 1.1 root 879: } 880: else 881: { 882: operands[2] = gen_rtx (CONST_INT, VOIDmode, log - i); 1.1.1.5 root 883: output_asm_insn ("sra %%g1,%2,%%g1", operands); 1.1 root 884: } 885: log = i; 886: if (shifts[i] < 0) 1.1.1.5 root 887: output_asm_insn ("sub %0,%%g1,%0", operands); 1.1 root 888: else 1.1.1.5 root 889: output_asm_insn ("add %0,%%g1,%0", operands); 1.1 root 890: } 891: 892: output_asm_insn ("! end open coded multiply"); 893: 894: return ""; 895: } 896: 897: char * 898: output_mul_insn (operands, unsignedp) 899: rtx *operands; 900: int unsignedp; 901: { 902: int lucky1 = ((unsigned)REGNO (operands[1]) - 8) <= 1; 903: int lucky2 = ((unsigned)REGNO (operands[2]) - 8) <= 1; 904: 905: if (lucky1) 906: if (lucky2) 907: output_asm_insn ("call .mul,2\n\tnop", operands); 908: else 909: { 910: rtx xoperands[2]; 911: xoperands[0] = gen_rtx (REG, SImode, 912: 8 ^ (REGNO (operands[1]) == 8)); 913: xoperands[1] = operands[2]; 914: output_asm_insn ("call .mul,2\n\tmov %1,%0", xoperands); 915: } 916: else if (lucky2) 917: { 918: rtx xoperands[2]; 919: xoperands[0] = gen_rtx (REG, SImode, 920: 8 ^ (REGNO (operands[2]) == 8)); 921: xoperands[1] = operands[1]; 922: output_asm_insn ("call .mul,2\n\tmov %1,%0", xoperands); 923: } 924: else 925: { 926: output_asm_insn ("mov %1,%%o0\n\tcall .mul,2\n\tmov %2,%%o1", 927: operands); 928: } 929: 930: if (REGNO (operands[0]) == 8) 931: return ""; 932: return "mov %%o0,%0"; 933: } 934: 935: /* Make floating point register f0 contain 0. 936: SIZE is the number of registers (including f0) 937: which should contain 0. */ 938: 939: void 940: make_f0_contain_0 (size) 941: int size; 942: { 943: if (size == 1) 1.1.1.8 ! root 944: { ! 945: if ((cc_status.flags & (CC_F0_IS_0)) == 0) ! 946: output_asm_insn ("ld [%%fp-16],%%f0", 0); ! 947: cc_status.flags |= CC_F0_IS_0; ! 948: } 1.1 root 949: else if (size == 2) 1.1.1.8 ! root 950: { ! 951: if ((cc_status.flags & CC_F0_IS_0) == 0) ! 952: output_asm_insn ("ld [%%fp-16],%%f0", 0); ! 953: if ((cc_status.flags & (CC_F1_IS_0)) == 0) ! 954: output_asm_insn ("ld [%%fp-12],%%f1", 0); ! 955: cc_status.flags |= CC_F0_IS_0 | CC_F1_IS_0; ! 956: } ! 957: } ! 958: ! 959: /* Since condition codes don't have logical links, we need to keep ! 960: their setting and use together for set-cc insns. */ ! 961: void ! 962: gen_scc_insn (code, mode, operands) ! 963: enum rtx_code code; ! 964: enum machine_mode mode; ! 965: rtx *operands; ! 966: { ! 967: extern rtx sequence_stack; ! 968: rtx last_insn = XEXP (XEXP (sequence_stack, 1), 0); ! 969: rtx last_pat = PATTERN (last_insn); ! 970: if (GET_CODE (last_pat) != SET ! 971: || GET_CODE (SET_DEST (last_pat)) != CC0) ! 972: abort (); ! 973: SET_DEST (last_pat) = operands[0]; ! 974: SET_SRC (last_pat) = gen_rtx (code, mode, SET_SRC (last_pat), const0_rtx); 1.1 root 975: } 1.1.1.4 root 976: 977: /* Output reasonable peephole for set-on-condition-code insns. 978: Note that these insns assume a particular way of defining 979: labels. Therefore, *both* tm-sparc.h and this function must 980: be changed if a new syntax is needed. */ 981: 982: char * 1.1.1.8 ! root 983: output_scc_insn (code, operand) 1.1.1.4 root 984: enum rtx_code code; 1.1.1.8 ! root 985: rtx operand; 1.1.1.4 root 986: { 987: rtx xoperands[2]; 988: rtx label = gen_label_rtx (); 1.1.1.8 ! root 989: int cc_in_fccr = cc_status.flags & CC_IN_FCCR; 1.1.1.4 root 990: 1.1.1.8 ! root 991: xoperands[0] = operand; 1.1.1.4 root 992: xoperands[1] = label; 993: 994: switch (code) 995: { 996: case NE: 1.1.1.8 ! root 997: if (cc_in_fccr) 1.1.1.5 root 998: output_asm_insn ("fbne,a %l0", &label); 1.1.1.4 root 999: else 1.1.1.5 root 1000: output_asm_insn ("bne,a %l0", &label); 1.1.1.4 root 1001: break; 1002: case EQ: 1.1.1.8 ! root 1003: if (cc_in_fccr) 1.1.1.5 root 1004: output_asm_insn ("fbe,a %l0", &label); 1.1.1.4 root 1005: else 1.1.1.5 root 1006: output_asm_insn ("be,a %l0", &label); 1.1.1.4 root 1007: break; 1008: case GE: 1.1.1.8 ! root 1009: if (cc_in_fccr) 1.1.1.5 root 1010: output_asm_insn ("fbge,a %l0", &label); 1.1.1.4 root 1011: else 1.1.1.5 root 1012: output_asm_insn ("bge,a %l0", &label); 1.1.1.4 root 1013: break; 1014: case GT: 1.1.1.8 ! root 1015: if (cc_in_fccr) 1.1.1.5 root 1016: output_asm_insn ("fbg,a %l0", &label); 1.1.1.4 root 1017: else 1.1.1.5 root 1018: output_asm_insn ("bg,a %l0", &label); 1.1.1.4 root 1019: break; 1020: case LE: 1.1.1.8 ! root 1021: if (cc_in_fccr) 1.1.1.5 root 1022: output_asm_insn ("fble,a %l0", &label); 1.1.1.4 root 1023: else 1.1.1.5 root 1024: output_asm_insn ("ble,a %l0", &label); 1.1.1.4 root 1025: break; 1026: case LT: 1.1.1.8 ! root 1027: if (cc_in_fccr) 1.1.1.5 root 1028: output_asm_insn ("fbl,a %l0", &label); 1.1.1.4 root 1029: else 1.1.1.5 root 1030: output_asm_insn ("bl,a %l0", &label); 1.1.1.4 root 1031: break; 1032: case GEU: 1.1.1.8 ! root 1033: if (cc_in_fccr) 1.1.1.4 root 1034: abort (); 1035: else 1.1.1.5 root 1036: output_asm_insn ("bgeu,a %l0", &label); 1.1.1.4 root 1037: break; 1038: case GTU: 1.1.1.8 ! root 1039: if (cc_in_fccr) 1.1.1.4 root 1040: abort (); 1041: else 1.1.1.5 root 1042: output_asm_insn ("bgu,a %l0", &label); 1.1.1.4 root 1043: break; 1044: case LEU: 1.1.1.8 ! root 1045: if (cc_in_fccr) 1.1.1.4 root 1046: abort (); 1047: else 1.1.1.5 root 1048: output_asm_insn ("bleu,a %l0", &label); 1.1.1.4 root 1049: break; 1050: case LTU: 1.1.1.8 ! root 1051: if (cc_in_fccr) 1.1.1.4 root 1052: abort (); 1053: else 1.1.1.5 root 1054: output_asm_insn ("blu,a %l0", &label); 1.1.1.4 root 1055: break; 1056: default: 1057: abort (); 1058: } 1.1.1.8 ! root 1059: output_asm_insn ("mov 1,%0\n\tmov 0,%0\n%l1:", xoperands); 1.1.1.4 root 1060: return ""; 1061: } 1062: 1.1.1.5 root 1063: /* Output a delayed branch insn with the delay insn in its 1064: branch slot. The delayed branch insn template is in TEMPLATE, 1.1.1.8 ! root 1065: with operands OPERANDS. The insn in its delay slot is INSN. ! 1066: ! 1067: As a special case, since we know that all memory transfers are via ! 1068: ld/st insns, if we see a (MEM (SYMBOL_REF ...)) we divide the memory ! 1069: reference around the branch as ! 1070: ! 1071: sethi %hi(x),%%g1 ! 1072: b ... ! 1073: ld/st [%g1+%lo(x)],... ! 1074: ! 1075: */ 1.1.1.4 root 1076: 1077: char * 1078: output_delay_insn (template, operands, insn) 1079: char *template; 1080: rtx *operands; 1081: rtx insn; 1082: { 1.1.1.8 ! root 1083: rtx src = XVECEXP (PATTERN (insn), 0, 1); ! 1084: rtx dest = XVECEXP (PATTERN (insn), 0, 0); ! 1085: ! 1086: if (GET_CODE (src) == MEM ! 1087: && CONSTANT_ADDRESS_P (XEXP (src, 0)) ! 1088: || GET_CODE (dest) == MEM ! 1089: && CONSTANT_ADDRESS_P (XEXP (dest, 0))) ! 1090: { ! 1091: rtx xoperands[2]; ! 1092: char *split_template; ! 1093: xoperands[0] = dest; ! 1094: xoperands[1] = src; ! 1095: ! 1096: /* Output the `sethi' insn. */ ! 1097: if (GET_CODE (src) == MEM) ! 1098: { ! 1099: output_asm_insn ("sethi %%hi(%m1),%%g1", xoperands); ! 1100: split_template = "ld [%%g1+%%lo(%m1)],%0"; ! 1101: } ! 1102: else ! 1103: { ! 1104: output_asm_insn ("sethi %%hi(%m0),%%g1", xoperands); ! 1105: split_template = "st %r1,[%%g1+%%lo(%m0)]"; ! 1106: } 1.1.1.4 root 1107: 1.1.1.8 ! root 1108: /* Output the branch instruction next. */ ! 1109: output_asm_insn (template, operands); ! 1110: ! 1111: /* Now output the load or store. ! 1112: No need to do a CC_STATUS_INIT, because we are branching anyway. */ ! 1113: output_asm_insn (split_template, xoperands); ! 1114: } ! 1115: else ! 1116: { ! 1117: extern char *insn_template[]; ! 1118: extern char *(*insn_outfun[])(); ! 1119: int insn_code_number; ! 1120: rtx pat = gen_rtx (SET, VOIDmode, dest, src); ! 1121: rtx delay_insn = gen_rtx (INSN, VOIDmode, 0, 0, 0, pat, -1, 0, 0); ! 1122: ! 1123: /* Output the branch instruction first. */ ! 1124: output_asm_insn (template, operands); ! 1125: ! 1126: /* Now recognize the insn which we put in its delay slot. ! 1127: We must do this after outputing the branch insn, ! 1128: since operands may just be a pointer to `recog_operands'. */ ! 1129: insn_code_number = recog (pat, delay_insn); ! 1130: if (insn_code_number == -1) ! 1131: abort (); ! 1132: ! 1133: /* Now get the template for what this insn would ! 1134: have been, without the branch. Its operands are ! 1135: exactly the same as they would be, so we don't ! 1136: need to do an insn_extract. */ ! 1137: template = insn_template[insn_code_number]; ! 1138: if (template == 0) ! 1139: template = (*insn_outfun[insn_code_number]) (operands, delay_insn); ! 1140: output_asm_insn (template, operands); ! 1141: } 1.1.1.4 root 1142: return ""; 1143: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.