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