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