|
|
1.1 ! root 1: /* Subroutines for gcc2 for pdp11. ! 2: Copyright (C) 1994 Free Software Foundation, Inc. ! 3: Contributed by Michael K. Gschwind ([email protected]). ! 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 1, 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: #ifndef FILE ! 22: #include <stdio.h> ! 23: #endif ! 24: #include "config.h" ! 25: #include "rtl.h" ! 26: #include "regs.h" ! 27: #include "hard-reg-set.h" ! 28: #include "real.h" ! 29: #include "insn-config.h" ! 30: #include "conditions.h" ! 31: #include "insn-flags.h" ! 32: #include "output.h" ! 33: #include "insn-attr.h" ! 34: ! 35: /* ! 36: #define FPU_REG_P(X) ((X)>=8 && (X)<14) ! 37: #define CPU_REG_P(X) ((X)>=0 && (X)<8) ! 38: */ ! 39: ! 40: /* this is the current value returned by the macro FIRST_PARM_OFFSET ! 41: defined in tm.h */ ! 42: int current_first_parm_offset; ! 43: ! 44: /* This is where the condition code register lives. */ ! 45: /* rtx cc0_reg_rtx; - no longer needed? */ ! 46: ! 47: static rtx find_addr_reg (); ! 48: ! 49: /* Nonzero if OP is a valid second operand for an arithmetic insn. */ ! 50: ! 51: int ! 52: arith_operand (op, mode) ! 53: rtx op; ! 54: enum machine_mode mode; ! 55: { ! 56: return (register_operand (op, mode) || GET_CODE (op) == CONST_INT); ! 57: } ! 58: ! 59: int ! 60: const_immediate_operand (op, mode) ! 61: rtx op; ! 62: enum machine_mode mode; ! 63: { ! 64: return (GET_CODE (op) == CONST_INT); ! 65: } ! 66: ! 67: int ! 68: immediate15_operand (op, mode) ! 69: rtx op; ! 70: enum machine_mode mode; ! 71: { ! 72: return (GET_CODE (op) == CONST_INT && ((INTVAL (op) & 0x8000) == 0x0000)); ! 73: } ! 74: ! 75: int ! 76: expand_shift_operand (op, mode) ! 77: rtx op; ! 78: enum machine_mode mode; ! 79: { ! 80: return (GET_CODE (op) == CONST_INT ! 81: && abs (INTVAL(op)) > 1 ! 82: && abs (INTVAL(op)) <= 4); ! 83: } ! 84: ! 85: /* ! 86: stream is a stdio stream to output the code to. ! 87: size is an int: how many units of temporary storage to allocate. ! 88: Refer to the array `regs_ever_live' to determine which registers ! 89: to save; `regs_ever_live[I]' is nonzero if register number I ! 90: is ever used in the function. This macro is responsible for ! 91: knowing which registers should not be saved even if used. ! 92: */ ! 93: ! 94: void ! 95: output_function_prologue(stream, size) ! 96: FILE *stream; ! 97: int size; ! 98: { ! 99: extern char call_used_regs[]; ! 100: extern int frame_pointer_needed; ! 101: ! 102: int fsize = ((size) + 1) & ~1; ! 103: int regno, nregs, i; ! 104: int offset = 0; ! 105: ! 106: int via_ac = -1; ! 107: ! 108: fprintf (stream, "\n\t; /* function prologue %s*/\n", current_function_name); ! 109: ! 110: /* if we are outputting code for main, ! 111: the switch FPU to right mode if TARGET_FPU */ ! 112: if ( (strcmp ("main", current_function_name) == 0) ! 113: && TARGET_FPU) ! 114: { ! 115: fprintf(stream, "\t;/* switch cpu to double float, single integer */\n"); ! 116: fprintf(stream, "\tsetd\n"); ! 117: fprintf(stream, "\tseti\n\n"); ! 118: } ! 119: ! 120: if (frame_pointer_needed) ! 121: { ! 122: fprintf(stream, "\tmov fp, -(sp)\n"); ! 123: fprintf(stream, "\tmov sp, fp\n"); ! 124: } ! 125: else ! 126: { ! 127: /* DON'T SAVE FP */ ! 128: } ! 129: ! 130: /* make frame */ ! 131: if (fsize) ! 132: fprintf (stream, "\tsub $%d, sp\n", fsize); ! 133: ! 134: /* save CPU registers */ ! 135: for (regno = 0; regno < 8; regno++) ! 136: if (regs_ever_live[regno] && ! call_used_regs[regno]) ! 137: if (! ((regno == FRAME_POINTER_REGNUM) ! 138: && frame_pointer_needed)) ! 139: fprintf (stream, "\tmov %s, -(sp)\n", reg_names[regno]); ! 140: /* fpu regs saving */ ! 141: ! 142: /* via_ac specifies the ac to use for saving ac4, ac5 */ ! 143: via_ac = -1; ! 144: ! 145: for (regno = 8; regno < FIRST_PSEUDO_REGISTER ; regno++) ! 146: { ! 147: /* ac0 - ac3 */ ! 148: if (LOAD_FPU_REG_P(regno) ! 149: && regs_ever_live[regno] ! 150: && ! call_used_regs[regno]) ! 151: { ! 152: fprintf (stream, "\tfstd %s, -(sp)\n", reg_names[regno]); ! 153: via_ac = regno; ! 154: } ! 155: ! 156: /* maybe make ac4, ac5 call used regs?? */ ! 157: /* ac4 - ac5 */ ! 158: if (NO_LOAD_FPU_REG_P(regno) ! 159: && regs_ever_live[regno] ! 160: && ! call_used_regs[regno]) ! 161: { ! 162: if (via_ac == -1) ! 163: abort(); ! 164: ! 165: fprintf (stream, "\tfldd %s, %s\n", reg_names[regno], reg_names[via_ac]); ! 166: fprintf (stream, "\tfstd %s, -(sp)\n", reg_names[via_ac]); ! 167: } ! 168: } ! 169: ! 170: fprintf (stream, "\t;/* end of prologue */\n\n"); ! 171: } ! 172: ! 173: /* ! 174: The function epilogue should not depend on the current stack pointer! ! 175: It should use the frame pointer only. This is mandatory because ! 176: of alloca; we also take advantage of it to omit stack adjustments ! 177: before returning. */ ! 178: ! 179: /* maybe we can make leaf functions faster by switching to the ! 180: second register file - this way we don't have to save regs! ! 181: leaf functions are ~ 50% of all functions (dynamically!) ! 182: ! 183: set/clear bit 11 (dec. 2048) of status word for switching register files - ! 184: but how can we do this? the pdp11/45 manual says bit may only ! 185: be set (p.24), but not cleared! ! 186: ! 187: switching to kernel is probably more expensive, so we'll leave it ! 188: like this and not use the second set of registers... ! 189: ! 190: maybe as option if you want to generate code for kernel mode? */ ! 191: ! 192: ! 193: void ! 194: output_function_epilogue(stream, size) ! 195: FILE *stream; ! 196: int size; ! 197: { ! 198: extern char call_used_regs[]; ! 199: extern int may_call_alloca; ! 200: ! 201: int fsize = ((size) + 1) & ~1; ! 202: int nregs, regno, i, j, k, adjust_fp; ! 203: ! 204: int via_ac; ! 205: ! 206: fprintf (stream, "\n\t; /*function epilogue */\n"); ! 207: ! 208: if (frame_pointer_needed) ! 209: { ! 210: /* hope this is safe - m68k does it also .... */ ! 211: regs_ever_live[FRAME_POINTER_REGNUM] = 0; ! 212: ! 213: for (i =7, j = 0 ; i >= 0 ; i--) ! 214: if (regs_ever_live[i] && ! call_used_regs[i]) ! 215: j++; ! 216: ! 217: /* remember # of pushed bytes for CPU regs */ ! 218: k = 2*j; ! 219: ! 220: for (i =7 ; i >= 0 ; i--) ! 221: if (regs_ever_live[i] && ! call_used_regs[i]) ! 222: fprintf(stream, "\tmov %d(fp), %s\n",-fsize-2*j--, reg_names[i]); ! 223: ! 224: /* get ACs */ ! 225: via_ac = FIRST_PSEUDO_REGISTER -1; ! 226: ! 227: for (i = FIRST_PSEUDO_REGISTER; i > 7; i--) ! 228: if (regs_ever_live[i] && ! call_used_regs[i]) ! 229: { ! 230: via_ac = i; ! 231: k += 8; ! 232: } ! 233: ! 234: for (i = FIRST_PSEUDO_REGISTER; i > 7; i--) ! 235: { ! 236: if (LOAD_FPU_REG_P(i) ! 237: && regs_ever_live[i] ! 238: && ! call_used_regs[i]) ! 239: { ! 240: fprintf(stream, "\tfldd %d(fp), %s\n", -fsize-k, reg_names[i]); ! 241: k -= 8; ! 242: } ! 243: ! 244: if (NO_LOAD_FPU_REG_P(i) ! 245: && regs_ever_live[i] ! 246: && ! call_used_regs[i]) ! 247: { ! 248: if (! LOAD_FPU_REG_P(via_ac)) ! 249: abort(); ! 250: ! 251: fprintf(stream, "\tfldd %d(fp), %s\n", -fsize-k, reg_names[via_ac]); ! 252: fprintf(stream, "\tfstd %s, %s\n", reg_names[via_ac], reg_names[i]); ! 253: k -= 8; ! 254: } ! 255: } ! 256: ! 257: fprintf(stream, "\tmov fp, sp\n"); ! 258: fprintf (stream, "\tmov (sp)+, fp\n"); ! 259: } ! 260: else ! 261: { ! 262: via_ac = FIRST_PSEUDO_REGISTER -1; ! 263: ! 264: /* get ACs */ ! 265: for (i = FIRST_PSEUDO_REGISTER; i > 7; i--) ! 266: if (regs_ever_live[i] && call_used_regs[i]) ! 267: via_ac = i; ! 268: ! 269: for (i = FIRST_PSEUDO_REGISTER; i > 7; i--) ! 270: { ! 271: if (LOAD_FPU_REG_P(i) ! 272: && regs_ever_live[i] ! 273: && ! call_used_regs[i]) ! 274: fprintf(stream, "\tfldd (sp)+, %s\n", reg_names[i]); ! 275: ! 276: if (NO_LOAD_FPU_REG_P(i) ! 277: && regs_ever_live[i] ! 278: && ! call_used_regs[i]) ! 279: { ! 280: if (! LOAD_FPU_REG_P(via_ac)) ! 281: abort(); ! 282: ! 283: fprintf(stream, "\tfldd (sp)+, %s\n", reg_names[via_ac]); ! 284: fprintf(stream, "\tfstd %s, %s\n", reg_names[via_ac], reg_names[i]); ! 285: } ! 286: } ! 287: ! 288: for (i=7; i >= 0; i--) ! 289: if (regs_ever_live[i] && !call_used_regs[i]) ! 290: fprintf(stream, "\tmov (sp)+, %s\n", reg_names[i]); ! 291: ! 292: if (fsize) ! 293: fprintf((stream), "\tadd $%d, sp\n", fsize); ! 294: } ! 295: ! 296: fprintf (stream, "\trts pc\n"); ! 297: fprintf (stream, "\t;/* end of epilogue*/\n\n\n"); ! 298: } ! 299: ! 300: /* Return the best assembler insn template ! 301: for moving operands[1] into operands[0] as a fullword. */ ! 302: static char * ! 303: singlemove_string (operands) ! 304: rtx *operands; ! 305: { ! 306: if (operands[1] != const0_rtx) ! 307: return "mov %1,%0"; ! 308: ! 309: return "clr %0"; ! 310: } ! 311: ! 312: ! 313: /* Output assembler code to perform a doubleword move insn ! 314: with operands OPERANDS. */ ! 315: ! 316: char * ! 317: output_move_double (operands) ! 318: rtx *operands; ! 319: { ! 320: enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype0, optype1; ! 321: rtx latehalf[2]; ! 322: rtx addreg0 = 0, addreg1 = 0; ! 323: ! 324: /* First classify both operands. */ ! 325: ! 326: if (REG_P (operands[0])) ! 327: optype0 = REGOP; ! 328: else if (offsettable_memref_p (operands[0])) ! 329: optype0 = OFFSOP; ! 330: else if (GET_CODE (XEXP (operands[0], 0)) == POST_INC) ! 331: optype0 = POPOP; ! 332: else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC) ! 333: optype0 = PUSHOP; ! 334: else if (GET_CODE (operands[0]) == MEM) ! 335: optype0 = MEMOP; ! 336: else ! 337: optype0 = RNDOP; ! 338: ! 339: if (REG_P (operands[1])) ! 340: optype1 = REGOP; ! 341: else if (CONSTANT_P (operands[1])) ! 342: #if 0 ! 343: || GET_CODE (operands[1]) == CONST_DOUBLE) ! 344: #endif ! 345: optype1 = CNSTOP; ! 346: else if (offsettable_memref_p (operands[1])) ! 347: optype1 = OFFSOP; ! 348: else if (GET_CODE (XEXP (operands[1], 0)) == POST_INC) ! 349: optype1 = POPOP; ! 350: else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC) ! 351: optype1 = PUSHOP; ! 352: else if (GET_CODE (operands[1]) == MEM) ! 353: optype1 = MEMOP; ! 354: else ! 355: optype1 = RNDOP; ! 356: ! 357: /* Check for the cases that the operand constraints are not ! 358: supposed to allow to happen. Abort if we get one, ! 359: because generating code for these cases is painful. */ ! 360: ! 361: if (optype0 == RNDOP || optype1 == RNDOP) ! 362: abort (); ! 363: ! 364: /* If one operand is decrementing and one is incrementing ! 365: decrement the former register explicitly ! 366: and change that operand into ordinary indexing. */ ! 367: ! 368: if (optype0 == PUSHOP && optype1 == POPOP) ! 369: { ! 370: operands[0] = XEXP (XEXP (operands[0], 0), 0); ! 371: output_asm_insn ("sub $4,%0", operands); ! 372: operands[0] = gen_rtx (MEM, SImode, operands[0]); ! 373: optype0 = OFFSOP; ! 374: } ! 375: if (optype0 == POPOP && optype1 == PUSHOP) ! 376: { ! 377: operands[1] = XEXP (XEXP (operands[1], 0), 0); ! 378: output_asm_insn ("sub $4,%1", operands); ! 379: operands[1] = gen_rtx (MEM, SImode, operands[1]); ! 380: optype1 = OFFSOP; ! 381: } ! 382: ! 383: /* If an operand is an unoffsettable memory ref, find a register ! 384: we can increment temporarily to make it refer to the second word. */ ! 385: ! 386: if (optype0 == MEMOP) ! 387: addreg0 = find_addr_reg (XEXP (operands[0], 0)); ! 388: ! 389: if (optype1 == MEMOP) ! 390: addreg1 = find_addr_reg (XEXP (operands[1], 0)); ! 391: ! 392: /* Ok, we can do one word at a time. ! 393: Normally we do the low-numbered word first, ! 394: but if either operand is autodecrementing then we ! 395: do the high-numbered word first. ! 396: ! 397: In either case, set up in LATEHALF the operands to use ! 398: for the high-numbered word and in some cases alter the ! 399: operands in OPERANDS to be suitable for the low-numbered word. */ ! 400: ! 401: if (optype0 == REGOP) ! 402: latehalf[0] = gen_rtx (REG, HImode, REGNO (operands[0]) + 1); ! 403: else if (optype0 == OFFSOP) ! 404: latehalf[0] = adj_offsettable_operand (operands[0], 2); ! 405: else ! 406: latehalf[0] = operands[0]; ! 407: ! 408: if (optype1 == REGOP) ! 409: latehalf[1] = gen_rtx (REG, HImode, REGNO (operands[1]) + 1); ! 410: else if (optype1 == OFFSOP) ! 411: latehalf[1] = adj_offsettable_operand (operands[1], 2); ! 412: else if (optype1 == CNSTOP) ! 413: { ! 414: if (CONSTANT_P (operands[1])) ! 415: { ! 416: /* now the mess begins, high word is in lower word??? ! 417: ! 418: that's what ashc makes me think, but I don't remember :-( */ ! 419: latehalf[1] = gen_rtx(CONST_INT, VOIDmode, ! 420: INTVAL(operands[1])>>16); ! 421: operands[1] = gen_rtx(CONST_INT, VOIDmode, ! 422: INTVAL(operands[1])&0xff); ! 423: } ! 424: else if (GET_CODE (operands[1]) == CONST_DOUBLE) ! 425: { ! 426: /* immediate 32 bit values not allowed */ ! 427: abort(); ! 428: } ! 429: } ! 430: else ! 431: latehalf[1] = operands[1]; ! 432: ! 433: /* If insn is effectively movd N(sp),-(sp) then we will do the ! 434: high word first. We should use the adjusted operand 1 (which is N+4(sp)) ! 435: for the low word as well, to compensate for the first decrement of sp. */ ! 436: if (optype0 == PUSHOP ! 437: && REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM ! 438: && reg_overlap_mentioned_p (stack_pointer_rtx, operands[1])) ! 439: operands[1] = latehalf[1]; ! 440: ! 441: /* If one or both operands autodecrementing, ! 442: do the two words, high-numbered first. */ ! 443: ! 444: /* Likewise, the first move would clobber the source of the second one, ! 445: do them in the other order. This happens only for registers; ! 446: such overlap can't happen in memory unless the user explicitly ! 447: sets it up, and that is an undefined circumstance. */ ! 448: ! 449: if (optype0 == PUSHOP || optype1 == PUSHOP ! 450: || (optype0 == REGOP && optype1 == REGOP ! 451: && REGNO (operands[0]) == REGNO (latehalf[1]))) ! 452: { ! 453: /* Make any unoffsettable addresses point at high-numbered word. */ ! 454: if (addreg0) ! 455: output_asm_insn ("add $2,%0", &addreg0); ! 456: if (addreg1) ! 457: output_asm_insn ("add $2,%0", &addreg1); ! 458: ! 459: /* Do that word. */ ! 460: output_asm_insn (singlemove_string (latehalf), latehalf); ! 461: ! 462: /* Undo the adds we just did. */ ! 463: if (addreg0) ! 464: output_asm_insn ("sub $2,%0", &addreg0); ! 465: if (addreg1) ! 466: output_asm_insn ("sub $2,%0", &addreg1); ! 467: ! 468: /* Do low-numbered word. */ ! 469: return singlemove_string (operands); ! 470: } ! 471: ! 472: /* Normal case: do the two words, low-numbered first. */ ! 473: ! 474: output_asm_insn (singlemove_string (operands), operands); ! 475: ! 476: /* Make any unoffsettable addresses point at high-numbered word. */ ! 477: if (addreg0) ! 478: output_asm_insn ("add $2,%0", &addreg0); ! 479: if (addreg1) ! 480: output_asm_insn ("add $2,%0", &addreg1); ! 481: ! 482: /* Do that word. */ ! 483: output_asm_insn (singlemove_string (latehalf), latehalf); ! 484: ! 485: /* Undo the adds we just did. */ ! 486: if (addreg0) ! 487: output_asm_insn ("sub $2,%0", &addreg0); ! 488: if (addreg1) ! 489: output_asm_insn ("sub $2,%0", &addreg1); ! 490: ! 491: return ""; ! 492: } ! 493: /* Output assembler code to perform a quadword move insn ! 494: with operands OPERANDS. */ ! 495: ! 496: char * ! 497: output_move_quad (operands) ! 498: rtx *operands; ! 499: { ! 500: enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype0, optype1; ! 501: rtx latehalf[2]; ! 502: rtx addreg0 = 0, addreg1 = 0; ! 503: ! 504: output_asm_insn(";; movdi/df: %1 -> %0", operands); ! 505: ! 506: if (REG_P (operands[0])) ! 507: optype0 = REGOP; ! 508: else if (offsettable_memref_p (operands[0])) ! 509: optype0 = OFFSOP; ! 510: else if (GET_CODE (XEXP (operands[0], 0)) == POST_INC) ! 511: optype0 = POPOP; ! 512: else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC) ! 513: optype0 = PUSHOP; ! 514: else if (GET_CODE (operands[0]) == MEM) ! 515: optype0 = MEMOP; ! 516: else ! 517: optype0 = RNDOP; ! 518: ! 519: if (REG_P (operands[1])) ! 520: optype1 = REGOP; ! 521: else if (CONSTANT_P (operands[1]) ! 522: || GET_CODE (operands[1]) == CONST_DOUBLE) ! 523: optype1 = CNSTOP; ! 524: else if (offsettable_memref_p (operands[1])) ! 525: optype1 = OFFSOP; ! 526: else if (GET_CODE (XEXP (operands[1], 0)) == POST_INC) ! 527: optype1 = POPOP; ! 528: else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC) ! 529: optype1 = PUSHOP; ! 530: else if (GET_CODE (operands[1]) == MEM) ! 531: optype1 = MEMOP; ! 532: else ! 533: optype1 = RNDOP; ! 534: ! 535: /* Check for the cases that the operand constraints are not ! 536: supposed to allow to happen. Abort if we get one, ! 537: because generating code for these cases is painful. */ ! 538: ! 539: if (optype0 == RNDOP || optype1 == RNDOP) ! 540: abort (); ! 541: ! 542: /* check if we move a CPU reg to an FPU reg, or vice versa! */ ! 543: if (optype0 == REGOP && optype1 == REGOP) ! 544: /* bogus - 64 bit cannot reside in CPU! */ ! 545: if (CPU_REG_P(REGNO(operands[0])) ! 546: || CPU_REG_P (REGNO(operands[1]))) ! 547: abort(); ! 548: ! 549: if (optype0 == REGOP || optype1 == REGOP) ! 550: { ! 551: /* check for use of clrd???? ! 552: if you ever allow ac4 and ac5 (now we require secondary load) ! 553: you must check whether ! 554: you want to load into them or store from them - ! 555: then dump ac0 into $help$ movce ac4/5 to ac0, do the ! 556: store from ac0, and restore ac0 - if you can find ! 557: an unused ac[0-3], use that and you save a store and a load!*/ ! 558: ! 559: if (FPU_REG_P(REGNO(operands[0]))) ! 560: { ! 561: if (GET_CODE(operands[1]) == CONST_DOUBLE) ! 562: { ! 563: union { double d; int i[2]; } u; ! 564: u.i[0] = CONST_DOUBLE_LOW (operands[1]); ! 565: u.i[1] = CONST_DOUBLE_HIGH (operands[1]); ! 566: ! 567: if (u.d == 0.0) ! 568: return "clrd %0"; ! 569: } ! 570: ! 571: return "ldd %1, %0"; ! 572: } ! 573: ! 574: if (FPU_REG_P(REGNO(operands[1]))) ! 575: return "std %1, %0"; ! 576: } ! 577: ! 578: /* If one operand is decrementing and one is incrementing ! 579: decrement the former register explicitly ! 580: and change that operand into ordinary indexing. */ ! 581: ! 582: if (optype0 == PUSHOP && optype1 == POPOP) ! 583: { ! 584: operands[0] = XEXP (XEXP (operands[0], 0), 0); ! 585: output_asm_insn ("sub $8,%0", operands); ! 586: operands[0] = gen_rtx (MEM, DImode, operands[0]); ! 587: optype0 = OFFSOP; ! 588: } ! 589: if (optype0 == POPOP && optype1 == PUSHOP) ! 590: { ! 591: operands[1] = XEXP (XEXP (operands[1], 0), 0); ! 592: output_asm_insn ("sub $8,%1", operands); ! 593: operands[1] = gen_rtx (MEM, SImode, operands[1]); ! 594: optype1 = OFFSOP; ! 595: } ! 596: ! 597: /* If an operand is an unoffsettable memory ref, find a register ! 598: we can increment temporarily to make it refer to the second word. */ ! 599: ! 600: if (optype0 == MEMOP) ! 601: addreg0 = find_addr_reg (XEXP (operands[0], 0)); ! 602: ! 603: if (optype1 == MEMOP) ! 604: addreg1 = find_addr_reg (XEXP (operands[1], 0)); ! 605: ! 606: /* Ok, we can do one word at a time. ! 607: Normally we do the low-numbered word first, ! 608: but if either operand is autodecrementing then we ! 609: do the high-numbered word first. ! 610: ! 611: In either case, set up in LATEHALF the operands to use ! 612: for the high-numbered word and in some cases alter the ! 613: operands in OPERANDS to be suitable for the low-numbered word. */ ! 614: ! 615: if (optype0 == REGOP) ! 616: latehalf[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 2); ! 617: else if (optype0 == OFFSOP) ! 618: latehalf[0] = adj_offsettable_operand (operands[0], 4); ! 619: else ! 620: latehalf[0] = operands[0]; ! 621: ! 622: if (optype1 == REGOP) ! 623: latehalf[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 2); ! 624: else if (optype1 == OFFSOP) ! 625: latehalf[1] = adj_offsettable_operand (operands[1], 4); ! 626: else if (optype1 == CNSTOP) ! 627: { ! 628: if (GET_CODE (operands[1]) == CONST_DOUBLE) ! 629: { ! 630: /* floats only. not yet supported! ! 631: ! 632: -- compute it into PDP float format, - internally, ! 633: just use IEEE and ignore possible problems ;-) ! 634: ! 635: we might get away with it !!!! */ ! 636: ! 637: abort(); ! 638: ! 639: #ifndef HOST_WORDS_BIG_ENDIAN ! 640: latehalf[1] = gen_rtx (CONST_INT, VOIDmode, ! 641: CONST_DOUBLE_LOW (operands[1])); ! 642: operands[1] = gen_rtx (CONST_INT, VOIDmode, ! 643: CONST_DOUBLE_HIGH (operands[1])); ! 644: #else /* HOST_WORDS_BIG_ENDIAN */ ! 645: latehalf[1] = gen_rtx (CONST_INT, VOIDmode, ! 646: CONST_DOUBLE_HIGH (operands[1])); ! 647: operands[1] = gen_rtx (CONST_INT, VOIDmode, ! 648: CONST_DOUBLE_LOW (operands[1])); ! 649: #endif /* HOST_WORDS_BIG_ENDIAN */ ! 650: } ! 651: } ! 652: else ! 653: latehalf[1] = operands[1]; ! 654: ! 655: /* If insn is effectively movd N(sp),-(sp) then we will do the ! 656: high word first. We should use the adjusted operand 1 (which is N+4(sp)) ! 657: for the low word as well, to compensate for the first decrement of sp. */ ! 658: if (optype0 == PUSHOP ! 659: && REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM ! 660: && reg_overlap_mentioned_p (stack_pointer_rtx, operands[1])) ! 661: operands[1] = latehalf[1]; ! 662: ! 663: /* If one or both operands autodecrementing, ! 664: do the two words, high-numbered first. */ ! 665: ! 666: /* Likewise, the first move would clobber the source of the second one, ! 667: do them in the other order. This happens only for registers; ! 668: such overlap can't happen in memory unless the user explicitly ! 669: sets it up, and that is an undefined circumstance. */ ! 670: ! 671: if (optype0 == PUSHOP || optype1 == PUSHOP ! 672: || (optype0 == REGOP && optype1 == REGOP ! 673: && REGNO (operands[0]) == REGNO (latehalf[1]))) ! 674: { ! 675: /* Make any unoffsettable addresses point at high-numbered word. */ ! 676: if (addreg0) ! 677: output_asm_insn ("add $4,%0", &addreg0); ! 678: if (addreg1) ! 679: output_asm_insn ("add $4,%0", &addreg1); ! 680: ! 681: /* Do that word. */ ! 682: output_asm_insn(output_move_double(latehalf), latehalf); ! 683: ! 684: /* Undo the adds we just did. */ ! 685: if (addreg0) ! 686: output_asm_insn ("sub $4,%0", &addreg0); ! 687: if (addreg1) ! 688: output_asm_insn ("sub $4,%0", &addreg1); ! 689: ! 690: /* Do low-numbered word. */ ! 691: return output_move_double (operands); ! 692: } ! 693: ! 694: /* Normal case: do the two words, low-numbered first. */ ! 695: ! 696: output_asm_insn (output_move_double (operands), operands); ! 697: ! 698: /* Make any unoffsettable addresses point at high-numbered word. */ ! 699: if (addreg0) ! 700: output_asm_insn ("add $4,%0", &addreg0); ! 701: if (addreg1) ! 702: output_asm_insn ("add $4,%0", &addreg1); ! 703: ! 704: /* Do that word. */ ! 705: output_asm_insn (output_move_double (latehalf), latehalf); ! 706: ! 707: /* Undo the adds we just did. */ ! 708: if (addreg0) ! 709: output_asm_insn ("sub $4,%0", &addreg0); ! 710: if (addreg1) ! 711: output_asm_insn ("sub $4,%0", &addreg1); ! 712: ! 713: return ""; ! 714: } ! 715: ! 716: ! 717: /* Return a REG that occurs in ADDR with coefficient 1. ! 718: ADDR can be effectively incremented by incrementing REG. */ ! 719: ! 720: static rtx ! 721: find_addr_reg (addr) ! 722: rtx addr; ! 723: { ! 724: while (GET_CODE (addr) == PLUS) ! 725: { ! 726: if (GET_CODE (XEXP (addr, 0)) == REG) ! 727: addr = XEXP (addr, 0); ! 728: if (GET_CODE (XEXP (addr, 1)) == REG) ! 729: addr = XEXP (addr, 1); ! 730: if (CONSTANT_P (XEXP (addr, 0))) ! 731: addr = XEXP (addr, 1); ! 732: if (CONSTANT_P (XEXP (addr, 1))) ! 733: addr = XEXP (addr, 0); ! 734: } ! 735: if (GET_CODE (addr) == REG) ! 736: return addr; ! 737: return 0; ! 738: } ! 739: ! 740: /* Output an ascii string. */ ! 741: output_ascii (file, p, size) ! 742: FILE *file; ! 743: char *p; ! 744: int size; ! 745: { ! 746: int i; ! 747: ! 748: fprintf (file, "\t.byte \""); ! 749: ! 750: for (i = 0; i < size; i++) ! 751: { ! 752: register int c = p[i]; ! 753: if (c == '\"' || c == '\\') ! 754: putc ('\\', file); ! 755: if (c >= ' ' && c < 0177) ! 756: putc (c, file); ! 757: else ! 758: { ! 759: fprintf (file, "\\%03o", c); ! 760: /* After an octal-escape, if a digit follows, ! 761: terminate one string constant and start another. ! 762: The Vax assembler fails to stop reading the escape ! 763: after three digits, so this is the only way we ! 764: can get it to parse the data properly. */ ! 765: if (i < size - 1 && p[i + 1] >= '0' && p[i + 1] <= '9') ! 766: fprintf (file, "\"\n\tstring \""); ! 767: } ! 768: } ! 769: fprintf (file, "\"\n"); ! 770: } ! 771: ! 772: ! 773: /* --- stole from out-vax, needs changes */ ! 774: ! 775: print_operand_address (file, addr) ! 776: FILE *file; ! 777: register rtx addr; ! 778: { ! 779: register rtx reg1, reg2, breg, ireg; ! 780: rtx offset; ! 781: ! 782: retry: ! 783: ! 784: switch (GET_CODE (addr)) ! 785: { ! 786: case MEM: ! 787: fprintf (file, "@"); ! 788: addr = XEXP (addr, 0); ! 789: goto retry; ! 790: ! 791: case REG: ! 792: fprintf (file, "(%s)", reg_names[REGNO (addr)]); ! 793: break; ! 794: ! 795: case PRE_DEC: ! 796: fprintf (file, "-(%s)", reg_names[REGNO (XEXP (addr, 0))]); ! 797: break; ! 798: ! 799: case POST_INC: ! 800: fprintf (file, "(%s)+", reg_names[REGNO (XEXP (addr, 0))]); ! 801: break; ! 802: ! 803: case PLUS: ! 804: reg1 = 0; reg2 = 0; ! 805: ireg = 0; breg = 0; ! 806: offset = 0; ! 807: if (CONSTANT_ADDRESS_P (XEXP (addr, 0)) ! 808: || GET_CODE (XEXP (addr, 0)) == MEM) ! 809: { ! 810: offset = XEXP (addr, 0); ! 811: addr = XEXP (addr, 1); ! 812: } ! 813: else if (CONSTANT_ADDRESS_P (XEXP (addr, 1)) ! 814: || GET_CODE (XEXP (addr, 1)) == MEM) ! 815: { ! 816: offset = XEXP (addr, 1); ! 817: addr = XEXP (addr, 0); ! 818: } ! 819: if (GET_CODE (addr) != PLUS) ! 820: ; ! 821: else if (GET_CODE (XEXP (addr, 0)) == MULT) ! 822: { ! 823: reg1 = XEXP (addr, 0); ! 824: addr = XEXP (addr, 1); ! 825: } ! 826: else if (GET_CODE (XEXP (addr, 1)) == MULT) ! 827: { ! 828: reg1 = XEXP (addr, 1); ! 829: addr = XEXP (addr, 0); ! 830: } ! 831: else if (GET_CODE (XEXP (addr, 0)) == REG) ! 832: { ! 833: reg1 = XEXP (addr, 0); ! 834: addr = XEXP (addr, 1); ! 835: } ! 836: else if (GET_CODE (XEXP (addr, 1)) == REG) ! 837: { ! 838: reg1 = XEXP (addr, 1); ! 839: addr = XEXP (addr, 0); ! 840: } ! 841: if (GET_CODE (addr) == REG || GET_CODE (addr) == MULT) ! 842: { ! 843: if (reg1 == 0) ! 844: reg1 = addr; ! 845: else ! 846: reg2 = addr; ! 847: addr = 0; ! 848: } ! 849: if (offset != 0) ! 850: { ! 851: if (addr != 0) abort (); ! 852: addr = offset; ! 853: } ! 854: if (reg1 != 0 && GET_CODE (reg1) == MULT) ! 855: { ! 856: breg = reg2; ! 857: ireg = reg1; ! 858: } ! 859: else if (reg2 != 0 && GET_CODE (reg2) == MULT) ! 860: { ! 861: breg = reg1; ! 862: ireg = reg2; ! 863: } ! 864: else if (reg2 != 0 || GET_CODE (addr) == MEM) ! 865: { ! 866: breg = reg2; ! 867: ireg = reg1; ! 868: } ! 869: else ! 870: { ! 871: breg = reg1; ! 872: ireg = reg2; ! 873: } ! 874: if (addr != 0) ! 875: output_address (addr); ! 876: if (breg != 0) ! 877: { ! 878: if (GET_CODE (breg) != REG) ! 879: abort (); ! 880: fprintf (file, "(%s)", reg_names[REGNO (breg)]); ! 881: } ! 882: if (ireg != 0) ! 883: { ! 884: if (GET_CODE (ireg) == MULT) ! 885: ireg = XEXP (ireg, 0); ! 886: if (GET_CODE (ireg) != REG) ! 887: abort (); ! 888: abort(); ! 889: fprintf (file, "[%s]", reg_names[REGNO (ireg)]); ! 890: } ! 891: break; ! 892: ! 893: default: ! 894: output_addr_const (file, addr); ! 895: } ! 896: } ! 897: ! 898: /* register move costs, indexed by regs */ ! 899: ! 900: static int move_costs[N_REG_CLASSES][N_REG_CLASSES] = ! 901: { ! 902: /* NO MUL GEN LFPU NLFPU FPU ALL */ ! 903: ! 904: /* NO */ { 0, 0, 0, 0, 0, 0, 0}, ! 905: /* MUL */ { 0, 2, 2, 10, 22, 22, 22}, ! 906: /* GEN */ { 0, 2, 2, 10, 22, 22, 22}, ! 907: /* LFPU */ { 0, 10, 10, 2, 2, 2, 10}, ! 908: /* NLFPU */ { 0, 22, 22, 2, 2, 2, 22}, ! 909: /* FPU */ { 0, 22, 22, 2, 2, 2, 22}, ! 910: /* ALL */ { 0, 22, 22, 10, 22, 22, 22} ! 911: } ; ! 912: ! 913: ! 914: /* -- note that some moves are tremendously expensive, ! 915: because they require lots of tricks! do we have to ! 916: charge the costs incurred by secondary reload class ! 917: -- as we do here with 22 -- or not ? */ ! 918: ! 919: int ! 920: register_move_cost(c1, c2) ! 921: enum reg_class c1, c2; ! 922: { ! 923: return move_costs[(int)c1][(int)c2]; ! 924: } ! 925: ! 926: char * ! 927: output_jump(pos, neg, length) ! 928: int length; ! 929: char *pos, *neg; ! 930: { ! 931: static int x = 0; ! 932: ! 933: static char buf[1000]; ! 934: ! 935: #if 0 ! 936: /* currently we don't need this, because the tstdf and cmpdf ! 937: copy the condition code immediately, and other float operations are not ! 938: yet recognized as changing the FCC - if so, then the length-cost of all ! 939: jump insns increases by one, because we have to potentially copy the ! 940: FCC! */ ! 941: if (cc_status.flags & CC_IN_FPU) ! 942: output_asm_insn("cfcc", NULL); ! 943: #endif ! 944: ! 945: switch (length) ! 946: { ! 947: case 1: ! 948: ! 949: strcpy(buf, pos); ! 950: strcat(buf, " %l0"); ! 951: ! 952: return buf; ! 953: ! 954: case 3: ! 955: ! 956: sprintf(buf, "%s JMP_%d\n\tjmp %%l0\nJMP_%d:", neg, x, x); ! 957: ! 958: x++; ! 959: ! 960: return buf; ! 961: ! 962: default: ! 963: ! 964: abort(); ! 965: } ! 966: ! 967: } ! 968: ! 969: void ! 970: notice_update_cc_on_set(exp, insn) ! 971: rtx exp; ! 972: rtx insn; ! 973: { ! 974: if (GET_CODE (SET_DEST (exp)) == CC0) ! 975: { ! 976: cc_status.flags = 0; ! 977: cc_status.value1 = SET_DEST (exp); ! 978: cc_status.value2 = SET_SRC (exp); ! 979: ! 980: /* ! 981: if (GET_MODE(SET_SRC(exp)) == DFmode) ! 982: cc_status.flags |= CC_IN_FPU; ! 983: */ ! 984: } ! 985: else if ((GET_CODE (SET_DEST (exp)) == REG ! 986: || GET_CODE (SET_DEST (exp)) == MEM) ! 987: && GET_CODE (SET_SRC (exp)) != PC ! 988: && (GET_MODE (SET_DEST(exp)) == HImode ! 989: || GET_MODE (SET_DEST(exp)) == QImode) ! 990: && (GET_CODE (SET_SRC(exp)) == PLUS ! 991: || GET_CODE (SET_SRC(exp)) == MINUS ! 992: || GET_CODE (SET_SRC(exp)) == AND ! 993: || GET_CODE (SET_SRC(exp)) == IOR ! 994: || GET_CODE (SET_SRC(exp)) == XOR ! 995: || GET_CODE (SET_SRC(exp)) == NOT ! 996: || GET_CODE (SET_SRC(exp)) == NEG ! 997: || GET_CODE (SET_SRC(exp)) == REG ! 998: || GET_CODE (SET_SRC(exp)) == MEM)) ! 999: { ! 1000: cc_status.flags = 0; ! 1001: cc_status.value1 = SET_SRC (exp); ! 1002: cc_status.value2 = SET_DEST (exp); ! 1003: ! 1004: if (cc_status.value1 && GET_CODE (cc_status.value1) == REG ! 1005: && cc_status.value2 ! 1006: && reg_overlap_mentioned_p (cc_status.value1, cc_status.value2)) ! 1007: cc_status.value2 = 0; ! 1008: if (cc_status.value1 && GET_CODE (cc_status.value1) == MEM ! 1009: && cc_status.value2 ! 1010: && GET_CODE (cc_status.value2) == MEM) ! 1011: cc_status.value2 = 0; ! 1012: } ! 1013: else if (GET_CODE (SET_SRC (exp)) == CALL) ! 1014: { ! 1015: CC_STATUS_INIT; ! 1016: } ! 1017: else if (GET_CODE (SET_DEST (exp)) == REG) ! 1018: /* what's this ? */ ! 1019: { ! 1020: if ((cc_status.value1 ! 1021: && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value1))) ! 1022: cc_status.value1 = 0; ! 1023: if ((cc_status.value2 ! 1024: && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value2))) ! 1025: cc_status.value2 = 0; ! 1026: } ! 1027: else if (SET_DEST(exp) == pc_rtx) ! 1028: { ! 1029: /* jump */ ! 1030: } ! 1031: else /* if (GET_CODE (SET_DEST (exp)) == MEM) */ ! 1032: { ! 1033: /* the last else is a bit paranoic, but since nearly all instructions ! 1034: play with condition codes, it's reasonable! */ ! 1035: ! 1036: CC_STATUS_INIT; /* paranoia*/ ! 1037: } ! 1038: } ! 1039: ! 1040: ! 1041: int simple_memory_operand(op, mode) ! 1042: rtx op; ! 1043: enum machine_mode mode; ! 1044: { ! 1045: rtx addr, plus0, plus1; ! 1046: int offset = 0; ! 1047: ! 1048: /* Eliminate non-memory operations */ ! 1049: if (GET_CODE (op) != MEM) ! 1050: return FALSE; ! 1051: ! 1052: #if 0 ! 1053: /* dword operations really put out 2 instructions, so eliminate them. */ ! 1054: if (GET_MODE_SIZE (GET_MODE (op)) > (HAVE_64BIT_P () ? 8 : 4)) ! 1055: return FALSE; ! 1056: #endif ! 1057: ! 1058: /* Decode the address now. */ ! 1059: ! 1060: indirection: ! 1061: ! 1062: addr = XEXP (op, 0); ! 1063: ! 1064: switch (GET_CODE (addr)) ! 1065: { ! 1066: case REG: ! 1067: /* (R0) - no extra cost */ ! 1068: return 1; ! 1069: ! 1070: case PRE_DEC: ! 1071: case POST_INC: ! 1072: /* -(R0), (R0)+ - cheap! */ ! 1073: return 0; ! 1074: ! 1075: case MEM: ! 1076: /* cheap - is encoded in addressing mode info! ! 1077: ! 1078: -- except for @(R0), which has to be @0(R0) !!! */ ! 1079: ! 1080: if (GET_CODE (XEXP (addr, 0)) == REG) ! 1081: return 0; ! 1082: ! 1083: op=addr; ! 1084: goto indirection; ! 1085: ! 1086: case CONST_INT: ! 1087: case LABEL_REF: ! 1088: case CONST: ! 1089: case SYMBOL_REF: ! 1090: /* @#address - extra cost */ ! 1091: return 0; ! 1092: ! 1093: case PLUS: ! 1094: /* X(R0) - extra cost */ ! 1095: return 0; ! 1096: } ! 1097: ! 1098: return FALSE; ! 1099: } ! 1100: ! 1101: ! 1102: /* ! 1103: * output a block move: ! 1104: * ! 1105: * operands[0] ... to ! 1106: * operands[1] ... from ! 1107: * operands[2] ... length ! 1108: * operands[3] ... alignment ! 1109: * operands[4] ... scratch register ! 1110: */ ! 1111: ! 1112: ! 1113: char * ! 1114: output_block_move(operands) ! 1115: rtx *operands; ! 1116: { ! 1117: static int count = 0; ! 1118: char buf[200]; ! 1119: ! 1120: if (GET_CODE(operands[2]) == CONST_INT ! 1121: && TARGET_TIME) ! 1122: { ! 1123: if (INTVAL(operands[2]) < 16 ! 1124: && INTVAL(operands[3]) == 1) ! 1125: { ! 1126: register int i; ! 1127: ! 1128: for (i = 1; i <= INTVAL(operands[2]); i++) ! 1129: output_asm_insn("movb (%1)+, (%0)+", operands); ! 1130: ! 1131: return ""; ! 1132: } ! 1133: else if (INTVAL(operands[2]) < 32) ! 1134: { ! 1135: register int i; ! 1136: ! 1137: for (i = 1; i <= INTVAL(operands[2])/2; i++) ! 1138: output_asm_insn("mov (%1)+, (%0)+", operands); ! 1139: ! 1140: /* may I assume that moved quantity is ! 1141: multiple of alignment ??? ! 1142: ! 1143: I HOPE SO ! ! 1144: */ ! 1145: ! 1146: return ""; ! 1147: } ! 1148: ! 1149: ! 1150: /* can do other clever things, maybe... */ ! 1151: } ! 1152: ! 1153: if (CONSTANT_P(operands[2]) ) ! 1154: { ! 1155: /* just move count to scratch */ ! 1156: output_asm_insn("mov %2, %4", operands); ! 1157: } ! 1158: else ! 1159: { ! 1160: /* just clobber the register */ ! 1161: operands[4] = operands[2]; ! 1162: } ! 1163: ! 1164: ! 1165: /* switch over alignment */ ! 1166: switch (INTVAL(operands[3])) ! 1167: { ! 1168: case 1: ! 1169: ! 1170: /* ! 1171: x: ! 1172: movb (%1)+, (%0)+ ! 1173: ! 1174: if (TARGET_45) ! 1175: sob %4,x ! 1176: else ! 1177: dec %4 ! 1178: bgt x ! 1179: ! 1180: */ ! 1181: ! 1182: sprintf(buf, "\nmovestrhi%d:", count); ! 1183: output_asm_insn(buf, NULL); ! 1184: ! 1185: output_asm_insn("movb (%1)+, (%0)+", operands); ! 1186: ! 1187: if (TARGET_45) ! 1188: { ! 1189: sprintf(buf, "sob %%4, movestrhi%d", count); ! 1190: output_asm_insn(buf, operands); ! 1191: } ! 1192: else ! 1193: { ! 1194: output_asm_insn("dec %4", operands); ! 1195: ! 1196: sprintf(buf, "bgt movestrhi%d", count); ! 1197: output_asm_insn(buf, NULL); ! 1198: } ! 1199: ! 1200: count ++; ! 1201: break; ! 1202: ! 1203: case 2: ! 1204: ! 1205: /* ! 1206: asr %4 ! 1207: ! 1208: x: ! 1209: ! 1210: mov (%1)+, (%0)+ ! 1211: ! 1212: if (TARGET_45) ! 1213: sob %4, x ! 1214: else ! 1215: dec %4 ! 1216: bgt x ! 1217: */ ! 1218: ! 1219: generate_compact_code: ! 1220: ! 1221: output_asm_insn("asr %4", operands); ! 1222: ! 1223: sprintf(buf, "\nmovestrhi%d:", count); ! 1224: output_asm_insn(buf, NULL); ! 1225: ! 1226: output_asm_insn("mov (%1)+, (%0)+", operands); ! 1227: ! 1228: if (TARGET_45) ! 1229: { ! 1230: sprintf(buf, "sob %%4, movestrhi%d", count); ! 1231: output_asm_insn(buf, operands); ! 1232: } ! 1233: else ! 1234: { ! 1235: output_asm_insn("dec %4", operands); ! 1236: ! 1237: sprintf(buf, "bgt movestrhi%d", count); ! 1238: output_asm_insn(buf, NULL); ! 1239: } ! 1240: ! 1241: count ++; ! 1242: break; ! 1243: ! 1244: case 4: ! 1245: ! 1246: /* ! 1247: ! 1248: asr %4 ! 1249: asr %4 ! 1250: ! 1251: x: ! 1252: ! 1253: mov (%1)+, (%0)+ ! 1254: mov (%1)+, (%0)+ ! 1255: ! 1256: if (TARGET_45) ! 1257: sob %4, x ! 1258: else ! 1259: dec %4 ! 1260: bgt x ! 1261: */ ! 1262: ! 1263: if (TARGET_SPACE) ! 1264: goto generate_compact_code; ! 1265: ! 1266: output_asm_insn("asr %4", operands); ! 1267: output_asm_insn("asr %4", operands); ! 1268: ! 1269: sprintf(buf, "\nmovestrhi%d:", count); ! 1270: output_asm_insn(buf, NULL); ! 1271: ! 1272: output_asm_insn("mov (%1)+, (%0)+", operands); ! 1273: output_asm_insn("mov (%1)+, (%0)+", operands); ! 1274: ! 1275: if (TARGET_45) ! 1276: { ! 1277: sprintf(buf, "sob %%4, movestrhi%d", count); ! 1278: output_asm_insn(buf, operands); ! 1279: } ! 1280: else ! 1281: { ! 1282: output_asm_insn("dec %4", operands); ! 1283: ! 1284: sprintf(buf, "bgt movestrhi%d", count); ! 1285: output_asm_insn(buf, NULL); ! 1286: } ! 1287: ! 1288: count ++; ! 1289: break; ! 1290: ! 1291: default: ! 1292: ! 1293: /* ! 1294: ! 1295: asr %4 ! 1296: asr %4 ! 1297: asr %4 ! 1298: ! 1299: x: ! 1300: ! 1301: mov (%1)+, (%0)+ ! 1302: mov (%1)+, (%0)+ ! 1303: mov (%1)+, (%0)+ ! 1304: mov (%1)+, (%0)+ ! 1305: ! 1306: if (TARGET_45) ! 1307: sob %4, x ! 1308: else ! 1309: dec %4 ! 1310: bgt x ! 1311: */ ! 1312: ! 1313: ! 1314: if (TARGET_SPACE) ! 1315: goto generate_compact_code; ! 1316: ! 1317: output_asm_insn("asr %4", operands); ! 1318: output_asm_insn("asr %4", operands); ! 1319: output_asm_insn("asr %4", operands); ! 1320: ! 1321: sprintf(buf, "\nmovestrhi%d:", count); ! 1322: output_asm_insn(buf, NULL); ! 1323: ! 1324: output_asm_insn("mov (%1)+, (%0)+", operands); ! 1325: output_asm_insn("mov (%1)+, (%0)+", operands); ! 1326: output_asm_insn("mov (%1)+, (%0)+", operands); ! 1327: output_asm_insn("mov (%1)+, (%0)+", operands); ! 1328: ! 1329: if (TARGET_45) ! 1330: { ! 1331: sprintf(buf, "sob %%4, movestrhi%d", count); ! 1332: output_asm_insn(buf, operands); ! 1333: } ! 1334: else ! 1335: { ! 1336: output_asm_insn("dec %4", operands); ! 1337: ! 1338: sprintf(buf, "bgt movestrhi%d", count); ! 1339: output_asm_insn(buf, NULL); ! 1340: } ! 1341: ! 1342: count ++; ! 1343: break; ! 1344: ! 1345: ; ! 1346: ! 1347: } ! 1348: ! 1349: return ""; ! 1350: } ! 1351: ! 1352: /* for future use */ ! 1353: int ! 1354: comparison_operator_index(op) ! 1355: rtx op; ! 1356: { ! 1357: switch (GET_CODE(op)) ! 1358: { ! 1359: case NE: ! 1360: return 0; ! 1361: ! 1362: case EQ: ! 1363: return 1; ! 1364: ! 1365: case GE: ! 1366: return 2; ! 1367: ! 1368: case GT: ! 1369: return 3; ! 1370: ! 1371: case LE: ! 1372: return 4; ! 1373: ! 1374: case LT: ! 1375: return 5; ! 1376: ! 1377: case GEU: ! 1378: return 6; ! 1379: ! 1380: case GTU: ! 1381: return 7; ! 1382: ! 1383: case LEU: ! 1384: return 8; ! 1385: ! 1386: case LTU: ! 1387: return 9; ! 1388: ! 1389: default: ! 1390: return -1; ! 1391: } ! 1392: } ! 1393: ! 1394: /* tests whether the rtx is a comparison operator */ ! 1395: int ! 1396: comp_operator (op, mode) ! 1397: rtx op; ! 1398: enum machine_mode mode; ! 1399: { ! 1400: return comparison_operator_index(op) >= 0; ! 1401: } ! 1402: ! 1403: ! 1404: int ! 1405: legitimate_address_p (mode, address) ! 1406: enum machine_mode mode; ! 1407: rtx address; ! 1408: { ! 1409: /* #define REG_OK_STRICT */ ! 1410: GO_IF_LEGITIMATE_ADDRESS(mode, address, win); ! 1411: ! 1412: return 0; ! 1413: ! 1414: win: ! 1415: return 1; ! 1416: ! 1417: /* #undef REG_OK_STRICT */ ! 1418: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.