|
|
1.1 root 1: /* Subroutines for insn-output.c for Intel 80386.
2: Copyright (C) 1988 Free Software Foundation, Inc.
3:
4: This file is part of GNU CC.
5:
6: GNU CC is free software; you can redistribute it and/or modify
7: it under the terms of the GNU General Public License as published by
8: the Free Software Foundation; either version 1, or (at your option)
9: any later version.
10:
11: GNU CC is distributed in the hope that it will be useful,
12: but WITHOUT ANY WARRANTY; without even the implied warranty of
13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14: GNU General Public License for more details.
15:
16: You should have received a copy of the GNU General Public License
17: along with GNU CC; see the file COPYING. If not, write to
18: the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
19:
20: #ifndef FILE
21: #include <stdio.h>
22: #endif
23:
24: #define FP_TOP (gen_rtx(REG, DFmode, FIRST_FLOAT_REG))
25:
26: #define AT_SP(mode) (gen_rtx (MEM, (mode), stack_pointer_rtx))
27: #define AT_BP(mode) (gen_rtx (MEM, (mode), frame_pointer_rtx))
28:
29: #define RET return ""
30:
31: /* #define RETCOM(X) fprintf (asm_out_file, "%sX fp_pop_level=%d\n", \
32: COMMENT_BEGIN, fp_pop_level); RET */
33: #define RETCOM(X) return ""
34:
35: #define POP_ONE_FP \
36: { /* fp_pop_level--; */ \
37: fprintf (asm_out_file, "\tfstp %sst (0)\n", RP); }
38:
39: extern FILE *asm_out_file;
40: static char *singlemove_string ();
41: static void output_movf ();
42: static void replace_float_constant ();
43: static int mentions_fp_top ();
44: static int call_top_dead_p ();
45: static int fp_top_dead_p1 ();
46: static rtx via_memory ();
47: static void output_asm_insn_double_reg_op ();
48:
49: /* All output functions must increment or decrement this to indicate
50: the net number of pops or pushes which they perform. Note that it won't
51: necessarily balance with the optimize running, since we might have
52: two different calls with the same pop shared by cross jumping.
53: However on optimize the reg dead heuristic seems to work. */
54:
55: int fp_pop_level = 0;
56:
57: static char *hi_reg_name[] = HI_REGISTER_NAMES;
58: static char *qi_reg_name[] = QI_REGISTER_NAMES;
59:
60: /* for fabs, fch, .. where the argument operand[1] must first be moved to
61: constraints "=fm" "0" */
62:
63: #define FP_CALL1(op) \
64: { if (FP_REG_P (operands[0])) \
65: return op; \
66: output_movf (FP_TOP, operands[1]); \
67: output_asm_insn (op, operands); \
68: /* fp_pop_level--; */ \
69: return "fstp%z0 %0"; }
70:
71: /* handle case of call where op0/op1 is "=mf" and opn is "mrf"
72: eg. fadd */
73: #define FP_CALL(op, rev, n) \
74: return fp_call_internal (op, rev, n, operands, insn);
75:
76: static char *
77: fp_call_internal (op, rev, n, operands, insn)
78: char *op;
79: char *rev;
80: int n;
81: rtx *operands;
82: rtx insn;
83: {
84: if (!FP_REG_P (operands[0]))
85: {
86: /* Here destination is in memory
87: and source is in the fp stack. */
88: output_movf (FP_TOP, operands[0]);
89: output_asm_insn_double_reg_op (op, rev, insn);
90: return "fstp%z0 %0";
91: }
92:
93: if (FP_REG_P (operands[n]))
94: {
95: rtx temp = operands[1];
96: char *tem1 = op;
97: operands[1] = operands[n];
98: op = rev;
99: operands[n] = temp;
100: rev = tem1;
101: }
102:
103: if (REG_P (operands[n]))
104: {
105: rtx xops[2];
106: via_memory (operands[n]);
107: operands[n] = AT_SP (GET_MODE (operands[n]));
108: xops[0] = stack_pointer_rtx;
109: xops[1] = gen_rtx (CONST_INT, VOIDmode,
110: GET_MODE_SIZE (GET_MODE (operands[n])));
111: output_asm_insn (op, operands + n);
112: output_asm_insn (AS2 (add%L0,%1,%0), xops);
113: }
114: else
115: output_asm_insn (op, operands + n);
116:
1.1.1.4 root 117: if (FP_REG_P (operands[0]))
118: {
119: /* It turns out not to work to use top_dead_p because
120: the death notes are not accurate enough.
121: But this ought to work, because the only thing that can
122: live across basic blocks is reg 8, and these insns
123: never involve reg 8 directly. */
124: if (fp_top_dead_p1 (insn))
125: POP_ONE_FP;
126: }
127:
1.1 root 128: RET;
129: }
130:
131: /* Output assembler code to perform insn OP
132: with two stack operands, and output on the stack.
133:
134: REV is the assembler insn that does the same thing but
135: effectively interchanges the meanings of the two arguments.
136:
137: Somewhat counterintuitively, the "first" operand was pushed last.
138:
139: The output replaces either the top-of-stack or both of the arguments,
140: depending on whether the other argument is wanted after this insn. */
141:
142: static void
143: output_asm_insn_double_reg_op (op, rev, insn)
144: char *op;
145: char *rev;
146: rtx insn;
147: {
148: fputc ('\t', asm_out_file);
149: if (top_dead_p (insn))
150: {
151: /* Here we want the "reversed" insn, fsubr or fdivr.
152: But there is an assembler bug in all 80386 assemblers
153: which exchanges the meanings of fsubr and fsub, and of fdivr and fdiv!
154: So use the "unreversed" opcode (which will assemble into
155: the "reversed" insn). */
156: rev = op;
157:
158: while (*rev && *rev != '%')
159: fputc (*rev++, asm_out_file);
160: /* fp_pop_level--; */
161:
162: fprintf (asm_out_file, AS2 (p,%sst,%sst(1)), RP, RP);
163: }
164: else
165: {
166: while (*op && *op != '%')
167: fputc (*op++, asm_out_file);
168: fprintf (asm_out_file,AS2 ( ,%sst(1),%sst), RP, RP);
169: }
170: putc ('\n', asm_out_file);
171: }
172:
173: /* Moves X to memory location 8 below stack pointer
174: and returns an RTX for that memory location.
175: X should be a register, in DFmode or SFmode. */
176:
177: static rtx
178: via_memory (x)
179: rtx x;
180: {
181: if (!REG_P (x))
182: abort ();
183: if (GET_MODE (x) == DFmode)
184: {
185: rtx xops[1];
186: xops[0] = gen_rtx (REG, SImode, REGNO (x) + 1);
187: output_asm_insn ("push%L0 %0", xops);
188: }
189: output_asm_insn ("push%L0 %0", &x);
190: }
191:
192: /* Output an insn to copy the SFmode value in fp0 to OPERAND
193: without clobbering fp0. */
194:
195: void
196: fp_store_sf (target)
197: rtx target;
198: {
199: if (REG_P (target))
200: {
201: rtx xoperands[3];
202: xoperands[0] = stack_pointer_rtx;
203: xoperands[1] = AT_SP (Pmode);
204: xoperands[2] = gen_rtx (CONST_INT, VOIDmode, -4);
205: output_asm_insn (AS2 (add%L0,%2,%0), xoperands);
206: output_asm_insn ("fst%S0 %1", xoperands);
207: output_asm_insn ("pop%L0 %0", &target);
208: }
209: else if (GET_CODE (target) == MEM)
210: output_asm_insn ("fst%S0 %0", &target);
211: }
212:
213: /* Output an insn to pop an SF value from fp0 into TARGET.
214: This destroys the value of fp0. */
215:
216: void
217: fp_pop_sf (target)
218: rtx target;
219: {
220: if (REG_P (target))
221: {
222: rtx xoperands[3];
223: xoperands[0] = stack_pointer_rtx;
224: xoperands[1] = AT_SP (Pmode);
225: xoperands[2] = gen_rtx (CONST_INT, VOIDmode, -4);
226: output_asm_insn (AS2 (add%L0,%2,%0), xoperands);
227: output_asm_insn ("fstp%S0 %1", xoperands);
228: output_asm_insn ("pop%L0 %0", &target);
229: /* fp_pop_level--; */
230: }
231: else if (GET_CODE (target) == MEM)
232: {
233: /* fp_pop_level--; */
234: output_asm_insn ("fstp%S0 %0", &target);
235: }
236: else abort ();
237: }
238:
239: /* Copy the top of the fpu stack into TARGET, without popping. */
240:
241: void
242: fp_store_df (target)
243: rtx target;
244: {
245: if (REG_P (target))
246: {
247: rtx xoperands[4];
248: xoperands[0] = stack_pointer_rtx;
249: xoperands[1] = gen_rtx (REG, SImode, REGNO (target) + 1);
250: xoperands[2] = AT_SP (Pmode);
251: xoperands[3] = gen_rtx (CONST_INT, VOIDmode, -8);
252: output_asm_insn (AS2 (add%L0,%3,%0), xoperands);
253: output_asm_insn ("fst%Q0 %2", xoperands);
254: output_asm_insn ("pop%L0 %0", &target);
255: output_asm_insn ("pop%L0 %1", xoperands);
256: }
257: else if (GET_CODE (target) == MEM)
258: output_asm_insn ("fst%Q0 %0", &target);
259: }
260:
261: /* Copy the top of the fpu stack into TARGET, with popping. */
262:
263: void
264: fp_pop_df (target)
265: rtx target;
266: {
267: if (REG_P (target))
268: {
269: rtx xoperands[4];
270: xoperands[0] = stack_pointer_rtx;
271: xoperands[1] = gen_rtx (REG, SImode, REGNO (target) + 1);
272: xoperands[2] = AT_SP (Pmode);
273: xoperands[3] = gen_rtx (CONST_INT, VOIDmode, -8);
274: output_asm_insn (AS2 (add%L0,%3,%0), xoperands);
275: /* fp_pop_level--; */
276: output_asm_insn ("fstp%Q0 %2", xoperands);
277: output_asm_insn ("pop%L0 %0", &target);
278: output_asm_insn ("pop%L0 %1", xoperands);
279: }
280: else if (GET_CODE (target) == MEM)
281: {
282: /* fp_pop_level--; */
283: output_asm_insn ("fstp%z0 %0", &target);
284: }
285: }
286:
287: #if 0
288: /* Pop the fp stack, convert value to integer and store in TARGET.
289: TARGET may be memory or register, and may have QI, HI or SImode. */
290:
291: void
292: fp_pop_int (target)
293: rtx target;
294: {
295: if (REG_P (target) || GET_MODE (target) != SImode)
296: {
297: rtx xxops[2];
298: xxops[0] = stack_pointer_rtx;
299: xxops[1] = gen_rtx (CONST_INT, VOIDmode, 4);
300: output_asm_insn (AS2 (sub%L0,%1,%0), xxops);
301: xxops[0] = AT_SP (Pmode);
302: /* fp_pop_level--; */
1.1.1.2 root 303: output_asm_insn ("fistp%L0 %0", xxops);
1.1 root 304: output_asm_insn ("pop%L0 %0", &target);
305: }
306: else if (GET_CODE (target) == MEM)
307: {
308: /* fp_pop_level--; */
1.1.1.2 root 309: output_asm_insn ("fistp%L0 %0", &target);
1.1 root 310: }
311: else abort ();
312: }
313: #endif
314:
315: /* Push the SFmode value X onto the fpu stack. */
316:
317: void
318: fp_push_sf (x)
319: rtx x;
320: {
321: /* fp_pop_level++; */
322: if (REG_P (x))
323: {
324: rtx xoperands[2];
325: rtx xfops[3];
326: output_asm_insn ("push%L0 %0", &x);
327: xfops[0] = AT_SP (Pmode);
328: xfops[2] = gen_rtx (CONST_INT, VOIDmode, 4);
329: xfops[1] = stack_pointer_rtx;
330: output_asm_insn ("fld%S0 %0 \n\tadd%L0 %2,%1", xfops);
331: }
332: else
333: output_asm_insn ("fld%S0 %0", &x);
334: }
335:
336: /* Push the DFmode value X onto the fpu stack. */
337:
338: void
339: fp_push_df (x)
340: rtx x;
341: {
342: /* fp_pop_level++; */
343:
344: if (REG_P (x))
345: {
346: rtx xoperands[2];
347: rtx xfops[3];
348: xoperands[0] = x;
349: xoperands[1] = gen_rtx (REG, SImode, REGNO (x) + 1);
350: output_asm_insn ("push%L0 %1", xoperands);
351: output_asm_insn ("push%L0 %0", xoperands);
352: xfops[0] = AT_SP (Pmode);
353: xfops[2] = gen_rtx (CONST_INT, VOIDmode, 8);
354: xfops[1] = stack_pointer_rtx;
355: output_asm_insn ("fld%Q0 %0 \n\tadd%L0 %2,%1", xfops);
356: }
357: else if (GET_CODE (x) == MEM)
358: output_asm_insn ("fld%Q0 %0", &x);
359: }
360:
361: static char *output_move_const_single ();
362:
363: static char *
364: singlemove_string (operands)
365: rtx *operands;
366: {
367: rtx x;
368: if (GET_CODE (operands[0]) == MEM
369: && GET_CODE (x = XEXP (operands[0], 0)) == PRE_DEC)
370: {
371: if (XEXP (x, 0) != stack_pointer_rtx)
372: abort ();
373: return "push%L0 %1";
374: }
375: else if (GET_CODE (operands[1]) == CONST_DOUBLE)
376: {
377: return output_move_const_single (operands);
378: }
379: else if (GET_CODE (operands[0]) == REG || GET_CODE (operands[1]) == REG)
380: return AS2 (mov%L0,%1,%0);
1.1.1.2 root 381: else if (CONSTANT_P (operands[1]))
382: return AS2 (mov%L0,%1,%0);
1.1 root 383: else
384: {
385: output_asm_insn ("push%L0 %1", operands);
386: return "pop%L0 %0";
387: }
388: }
389:
390: /* Return a REG that occurs in ADDR with coefficient 1.
391: ADDR can be effectively incremented by incrementing REG. */
392:
393: static rtx
394: find_addr_reg (addr)
395: rtx addr;
396: {
397: while (GET_CODE (addr) == PLUS)
398: {
399: if (GET_CODE (XEXP (addr, 0)) == REG)
400: addr = XEXP (addr, 0);
401: else if (GET_CODE (XEXP (addr, 1)) == REG)
402: addr = XEXP (addr, 1);
403: else if (CONSTANT_P (XEXP (addr, 0)))
404: addr = XEXP (addr, 1);
405: else if (CONSTANT_P (XEXP (addr, 1)))
406: addr = XEXP (addr, 0);
407: else
408: abort ();
409: }
410: if (GET_CODE (addr) == REG)
411: return addr;
412: abort ();
413: }
414:
415: /* Output an insn to add the constant N to the register X. */
416:
417: static void
418: asm_add (n, x)
419: int n;
420: rtx x;
421: {
422: rtx xops[2];
423: xops[1] = x;
424: if (n < 0)
425: {
426: xops[0] = gen_rtx (CONST_INT, VOIDmode, -n);
427: output_asm_insn (AS2 (sub%L0,%0,%1), xops);
428: }
429: else if (n > 0)
430: {
431: xops[0] = gen_rtx (CONST_INT, VOIDmode, n);
432: output_asm_insn (AS2 (add%L0,%0,%1), xops);
433: }
434: }
435:
436: /* Output assembler code to perform a doubleword move insn
437: with operands OPERANDS. */
438:
439: char *
440: output_move_double (operands)
441: rtx *operands;
442: {
443: enum {REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP } optype0, optype1;
444: rtx latehalf[2];
445: rtx addreg0 = 0, addreg1 = 0;
446:
447: /* First classify both operands. */
448:
449: if (REG_P (operands[0]))
450: optype0 = REGOP;
451: else if (offsettable_memref_p (operands[0]))
452: optype0 = OFFSOP;
453: else if (GET_CODE (XEXP (operands[0], 0)) == POST_INC)
454: optype0 = POPOP;
455: else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC)
456: optype0 = PUSHOP;
457: else if (GET_CODE (operands[0]) == MEM)
458: optype0 = MEMOP;
459: else
460: optype0 = RNDOP;
461:
462: if (REG_P (operands[1]))
463: optype1 = REGOP;
464: else if (CONSTANT_P (operands[1])
465: || GET_CODE (operands[1]) == CONST_DOUBLE)
466: optype1 = CNSTOP;
467: else if (offsettable_memref_p (operands[1]))
468: optype1 = OFFSOP;
469: else if (GET_CODE (XEXP (operands[1], 0)) == POST_INC)
470: optype1 = POPOP;
471: else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC)
472: optype1 = PUSHOP;
473: else if (GET_CODE (operands[1]) == MEM)
474: optype1 = MEMOP;
475: else
476: optype1 = RNDOP;
477:
478: /* Check for the cases that the operand constraints are not
479: supposed to allow to happen. Abort if we get one,
480: because generating code for these cases is painful. */
481:
482: if (optype0 == RNDOP || optype1 == RNDOP)
483: abort ();
484:
485: /* If one operand is decrementing and one is incrementing
486: decrement the former register explicitly
487: and change that operand into ordinary indexing. */
488:
489: if (optype0 == PUSHOP && optype1 == POPOP)
490: {
491: operands[0] = XEXP (XEXP (operands[0], 0), 0);
492: asm_add (-8, operands[0]);
493: operands[0] = gen_rtx (MEM, DImode, operands[0]);
494: optype0 = OFFSOP;
495: }
496: if (optype0 == POPOP && optype1 == PUSHOP)
497: {
498: operands[1] = XEXP (XEXP (operands[1], 0), 0);
499: asm_add (-8, operands[1]);
500: operands[1] = gen_rtx (MEM, DImode, operands[1]);
501: optype1 = OFFSOP;
502: }
503:
504: /* If an operand is an unoffsettable memory ref, find a register
505: we can increment temporarily to make it refer to the second word. */
506:
507: if (optype0 == MEMOP)
508: addreg0 = find_addr_reg (XEXP (operands[0], 0));
509:
510: if (optype1 == MEMOP)
511: addreg1 = find_addr_reg (XEXP (operands[1], 0));
512:
513: /* Ok, we can do one word at a time.
514: Normally we do the low-numbered word first,
515: but if either operand is autodecrementing then we
516: do the high-numbered word first.
517:
518: In either case, set up in LATEHALF the operands to use
519: for the high-numbered word and in some cases alter the
520: operands in OPERANDS to be suitable for the low-numbered word. */
521:
522: if (optype0 == REGOP)
523: latehalf[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
524: else if (optype0 == OFFSOP)
525: latehalf[0] = adj_offsettable_operand (operands[0], 4);
526: else
527: latehalf[0] = operands[0];
528:
529: if (optype1 == REGOP)
530: latehalf[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1);
531: else if (optype1 == OFFSOP)
532: latehalf[1] = adj_offsettable_operand (operands[1], 4);
533: else if (optype1 == CNSTOP)
534: {
535: if (CONSTANT_P (operands[1]))
536: latehalf[1] = const0_rtx;
537: else if (GET_CODE (operands[1]) == CONST_DOUBLE)
538: {
539: latehalf[1] = gen_rtx (CONST_INT, VOIDmode,
540: CONST_DOUBLE_HIGH (operands[1]));
541: operands[1] = gen_rtx (CONST_INT, VOIDmode,
542: CONST_DOUBLE_LOW (operands[1]));
543: }
544: }
545: else
546: latehalf[1] = operands[1];
547:
548: /* If insn is effectively movd N (sp),-(sp) then we will do the
549: high word first. We should use the adjusted operand 1 (which is N+4 (sp))
550: for the low word as well, to compensate for the first decrement of sp. */
551: if (optype0 == PUSHOP
552: && REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM
553: && reg_overlap_mentioned_p (stack_pointer_rtx, operands[1]))
554: operands[1] = latehalf[1];
555:
556: /* If one or both operands autodecrementing,
557: do the two words, high-numbered first. */
558:
559: /* Likewise, the first move would clobber the source of the second one,
560: do them in the other order. This happens only for registers;
561: such overlap can't happen in memory unless the user explicitly
562: sets it up, and that is an undefined circumstance. */
563:
564: if (optype0 == PUSHOP || optype1 == PUSHOP
565: || (optype0 == REGOP && optype1 == REGOP
566: && REGNO (operands[0]) == REGNO (latehalf[1])))
567: {
568: /* Make any unoffsettable addresses point at high-numbered word. */
569: if (addreg0)
570: asm_add (4, addreg0);
571: if (addreg1)
572: asm_add (4, addreg1);
573:
574: /* Do that word. */
575: output_asm_insn (singlemove_string (latehalf), latehalf);
576:
577: /* Undo the adds we just did. */
578: if (addreg0)
579: asm_add (-4, addreg0);
580: if (addreg1)
581: asm_add (-4, addreg1);
582:
583: /* Do low-numbered word. */
584: return singlemove_string (operands);
585: }
586:
587: /* Normal case: do the two words, low-numbered first. */
588:
589: output_asm_insn (singlemove_string (operands), operands);
590:
591: /* Make any unoffsettable addresses point at high-numbered word. */
592: if (addreg0)
593: asm_add (4, addreg0);
594: if (addreg1)
595: asm_add (4, addreg1);
596:
597: /* Do that word. */
598: output_asm_insn (singlemove_string (latehalf), latehalf);
599:
600: /* Undo the adds we just did. */
601: if (addreg0)
602: asm_add (-4, addreg0);
603: if (addreg1)
604: asm_add (-4, addreg1);
605:
606: return "";
607: }
608:
609: int
610: standard_80387_constant_p (x)
611: rtx x;
612: {
613: union { double d; int i[2];} u;
614: register double d;
615: u.i[0] = XINT (x, 0);
616: u.i[1] = XINT (x, 1);
617: d = u.d;
618:
619: if (d == 0)
620: return 1;
621: if (d == 1)
622: return 2;
623: /* Note that on the 80387, other constants, such as pi,
624: are much slower to load as standard constants
625: than to load from doubles in memory! */
626:
627: return 0;
628: }
629:
630: static char *
631: output_move_const_double (operands)
632: rtx *operands;
633: {
634: if (FP_REG_P (operands[0]))
635: {
636: int conval = standard_80387_constant_p (operands[1]);
637:
638: /* fp_pop_level++; */
639: if (conval == 1)
640: return "fldz";
641: if (conval == 2)
642: return "fld1";
643: /* fp_pop_level--; */
644: }
645:
646: output_move_double (operands);
647: }
648:
649:
650: static char *
651: output_move_const_single (operands)
652: rtx *operands;
653: {
654: if (FP_REG_P (operands[0]))
655: {
656: int conval = standard_80387_constant_p (operands[1]);
657:
658: /* fp_pop_level++; */
659: if (conval == 1)
660: return "fldz";
661: if (conval == 2)
662: return "fld1";
663: /* fp_pop_level--; */
664: }
665: if (GET_CODE (operands[1]) == CONST_DOUBLE)
666: {
667: union { int i[2]; double d;} u1;
668: union { int i; float f;} u2;
669: u1.i[0] = CONST_DOUBLE_LOW (operands[1]);
670: u1.i[1] = CONST_DOUBLE_HIGH (operands[1]);
671: u2.f = u1.d;
672: operands[1] = gen_rtx (CONST_INT, VOIDmode, u2.i);
673: }
674: return singlemove_string (operands);
675: }
676:
677: /* Output an insn to move an SF value from FROM to TO.
678: The kinds of operands are not restricted
679: except that they may not both be in memory. */
680:
681: void
682: output_movsf (to, from)
683: rtx from, to;
684: {
685: rtx xops[2];
686: xops[0] = to;
687: xops[1] = from;
688: if (FP_REG_P (from) || FP_REG_P (to))
689: {
690: from = xops[1];
691: }
692:
693: if (FP_REG_P (from))
694: {
695: #if 0
696: {
697: if (REGNO (from) != REGNO (to))
698: {
699: output_asm_insn ("fld%S0 %1 \n\tfstp%S0 %0", xops);
700: }
701: }
702: else
703: #endif
704:
705: if (! FP_REG_P (to))
706: fp_pop_sf (to);
707: }
708: else if (FP_REG_P (to))
709: fp_push_sf (from);
710: else
711: output_asm_insn (singlemove_string (xops), xops);
712: }
713:
714: /* Output an insn to move a DF value from FROM to TO.
715: The kinds of operands are not restricted
716: except that they may not both be in memory. */
717:
718: void
719: output_movdf (to, from)
720: rtx from, to;
721: {
722: rtx xops[2];
723: xops[0] = to;
724: xops[1] = from;
725: if (FP_REG_P (from) || FP_REG_P (to))
726: {
727: from = xops[1];
728: to = xops[0];
729: }
730: if (FP_REG_P (from))
731: {
732: #if 0
733: {
734: if (REGNO (from) != REGNO (to))
735: abort ();
736: /* output_asm_insn ("fld%Q0 %1 \n\t fstp%Q0 %0", xops);*/
737: }
738: else
739: {
740: #endif
741: if (! FP_REG_P (to))
742: fp_pop_df (to);
743: }
744: else if (FP_REG_P (to))
745: fp_push_df (from);
746: else
747: output_asm_insn (output_move_double (xops), xops);
748: }
749:
750: /* does move of FROM to TO where the mode is the minimum of the
751: two */
752:
753: static void
754: output_movf (to, from)
755: rtx to, from;
756: {
757: if (GET_MODE (from) == SFmode || GET_MODE (to) == SFmode)
758: output_movsf (to, from);
759: else
760: output_movdf (to, from);
761: }
762:
763: /* Return the best assembler insn template
764: for moving operands[1] into operands[0] as a fullword. */
765:
766: void
767: function_prologue (file, size)
768: FILE *file;
769: int size;
770: {
771: register int regno;
772: int nregs, limit;
773: rtx xops[4];
774: extern int frame_pointer_needed;
775:
776: /* fp_pop_level = 0; */
777: xops[0] = stack_pointer_rtx;
778: xops[1] = frame_pointer_rtx;
779: xops[2] = gen_rtx (CONST_INT, VOIDmode, size);
780: if (frame_pointer_needed)
781: {
782: output_asm_insn ("push%L0 %1", xops);
783: output_asm_insn (AS2 (mov%L0,%0,%1), xops);
784: if (size)
785: output_asm_insn (AS2 (sub%L0,%2,%0), xops);
786: }
787:
788: /* Note If use enter it is NOT reversed args.
789: This one is not reversed from intel!!
790: I think enter is slower. Also sdb doesn't like it.
791: But if you want it the code is:
792: {
793: xops[3] = const0_rtx;
794: output_asm_insn ("enter %2,%3", xops);
795: }
796: */
797: nregs = 0;
798: limit = (frame_pointer_needed ? FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM);
799: for (regno = limit - 1; regno >= 0; regno--)
800: if (regs_ever_live[regno] && ! call_used_regs[regno])
801: {
802: fprintf (file, "\tpush%s %se%s\n", L_SIZE, RP, hi_reg_name[regno]);
803: }
804: }
805:
806: void
807: function_epilogue (file, size)
808: FILE *file;
809: int size;
810: {
811: register int regno;
812: register int nregs, limit;
813: int assure_sp_pos;
1.1.1.4 root 814: int return_struct_adjust;
1.1 root 815: extern int frame_pointer_needed;
816: extern int current_function_pops_args;
817: extern int current_function_args_size;
1.1.1.4 root 818: extern int flag_pcc_struct_return;
819:
1.1 root 820: limit = (frame_pointer_needed ? FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM);
821: nregs = 0;
822:
1.1.1.4 root 823: return_struct_adjust =
824: (current_function_returns_struct
825: #ifdef STRUCT_RETURN_CALLER_POP
826: && !flag_pcc_struct_return
827: #endif
828: ? 4 : 0);
1.1 root 829:
830: for (regno = (limit -1); regno >= 0; regno--)
831: if (regs_ever_live[regno] && ! call_used_regs[regno])
832: nregs++;
833:
834: /* sp is often unreliable so we must go off the frame pointer,
835: */
836:
837: if (nregs && frame_pointer_needed)
838: {
839: rtx xops[2];
840: xops[0] = adj_offsettable_operand (AT_BP (Pmode),
841: -size -(nregs*(UNITS_PER_WORD)));
842: xops[1] = stack_pointer_rtx;
843: output_asm_insn (AS2 (lea%L0,%0,%1), xops);
844: }
845: for (regno = 0; regno < limit; regno++)
846: {
847: if (regs_ever_live[regno] && ! call_used_regs[regno])
848: {
849: fprintf (file, "\tpop%s ", L_SIZE);
850: fprintf (file, "%se%s\n", RP, hi_reg_name[regno]);
851: }
852: }
853:
854: if (frame_pointer_needed)
855: fprintf (file, "\tleave\n");
856: if (current_function_pops_args && current_function_args_size)
857: fprintf (file, "\tret %s%d\n", IP,
1.1.1.4 root 858: (current_function_args_size + return_struct_adjust));
859: else if (return_struct_adjust)
860: fprintf (file, "\tret %s%d\n", IP, return_struct_adjust);
1.1 root 861: else
862: fprintf (file, "\tret\n");
863: }
864:
865: int
866: hard_regno_mode_ok (regno, mode)
867: int regno;
868: enum machine_mode mode;
869: {
870: return
871: (regno < 2 ? 1
872: /* Used to reject floating modes here */
873: : regno < 4 ? 1
874: : regno >= 8 ? mode == DFmode || mode == SFmode
875: : mode != QImode);
876: }
877:
878: /* Print the name of a register based on its machine mode and number.
1.1.1.2 root 879: If CODE is 'w', pretend the mode is HImode.
1.1.1.3 root 880: If CODE is 'b', pretend the mode is QImode.
881: If CODE is 'k', pretend the mode is SImode. */
1.1 root 882:
883: #define PRINT_REG(X, CODE, FILE) \
884: do { fprintf (FILE, "%s", RP); \
1.1.1.2 root 885: switch ((CODE == 'w' ? 2 \
886: : CODE == 'b' ? 1 \
1.1.1.3 root 887: : CODE == 'k' ? 4 \
1.1.1.2 root 888: : GET_MODE_SIZE (GET_MODE (X)))) \
1.1 root 889: { \
890: case 4: \
891: case 8: \
892: if (!FP_REG_P (X)) fputs ("e", FILE); \
893: case 2: \
894: fputs (hi_reg_name[REGNO (X)], FILE); \
895: break; \
896: case 1: \
897: fputs (qi_reg_name[REGNO (X)], FILE); \
1.1.1.3 root 898: break; \
1.1 root 899: } \
900: } while (0)
901:
902: /* Meaning of CODE:
903: f -- float insn (print a CONST_DOUBLE as a float rather than in hex).
904: L,W,B,Q,S -- print the opcode suffix for specified size of operand.
905: R -- print the prefix for register names.
906: z -- print the opcode suffix for the size of the current operand.
907: * -- print a star (in certain assembler syntax)
908: w -- print the operand as if it's a "word" (HImode) even if it isn't.
909: c -- don't print special prefixes before constant operands.
910: */
911:
912: void
913: print_operand (file, x, code)
914: FILE *file;
915: rtx x;
916: int code;
917: {
918: if (code)
919: {
920: switch (code)
921: {
922: case '*':
923: if (USE_STAR)
924: putc ('*', file);
925: return;
926:
927: case 'L':
928: PUT_OP_SIZE (code, 'l', file);
929: return;
930:
931: case 'W':
932: PUT_OP_SIZE (code, 'w', file);
933: return;
934:
935: case 'B':
936: PUT_OP_SIZE (code, 'b', file);
937: return;
938:
939: case 'Q':
940: PUT_OP_SIZE (code, 'l', file);
941: return;
942:
943: case 'S':
944: PUT_OP_SIZE (code, 's', file);
945: return;
946:
947: case 'R':
948: fprintf (file, "%s", RP);
949: return;
950:
951: case 'z':
952: /* this is the size of op from size of operand */
953: switch (GET_MODE_SIZE (GET_MODE (x)))
954: {
955: case 2:
956: PUT_OP_SIZE ('W', 'w', file);
957: return;
958: case 4:
959: if (GET_MODE (x) == SFmode)
960: {
961: PUT_OP_SIZE ('S', 's', file);
962: return;
963: }
964: else
965: PUT_OP_SIZE ('L', 'l', file);
966: return;
967: case 8:
968: if (!FP_REG_P (x)) PUT_OP_SIZE ('Q', 'l', file);
969: return;
970: case 1:
971: PUT_OP_SIZE ('B', 'b', file);
972: return;
973: }
974: }
975: }
976: if (GET_CODE (x) == REG)
977: {
978: PRINT_REG (x, code, file);
979: }
980: else if (GET_CODE (x) == MEM)
981: {
982: PRINT_PTR (x, file);
983: if (CONSTANT_ADDRESS_P (XEXP (x, 0)))
984: output_addr_const (file, XEXP (x, 0));
985: else
986: output_address (XEXP (x, 0));
987: }
988: else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) == SFmode)
989: {
990: union { double d; int i[2]; } u;
991: union { float f; int i; } u1;
992: u.i[0] = CONST_DOUBLE_LOW (x);
993: u.i[1] = CONST_DOUBLE_HIGH (x);
994: u1.f = u.d;
995: if (code == 'f')
996: fprintf (file, "%.22e", u1.f);
997: else
998: {
999: PRINT_IMMED_PREFIX (file);
1000: fprintf (file, "0x%x", u1.i);
1001: }
1002: }
1003: else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) == DFmode)
1004: {
1005: union { double d; int i[2]; } u;
1006: u.i[0] = CONST_DOUBLE_LOW (x);
1007: u.i[1] = CONST_DOUBLE_HIGH (x);
1008: fprintf (file, "%.22e", u.d);
1009: }
1010: else
1011: {
1012: if (code != 'c')
1013: {
1014: if (GET_CODE (x) == CONST_INT)
1015: PRINT_IMMED_PREFIX (file);
1.1.1.5 ! root 1016: else if (GET_CODE (x) == CONST || GET_CODE (x) == SYMBOL_REF
! 1017: || GET_CODE (x) == LABEL_REF)
1.1 root 1018: PRINT_OFFSET_PREFIX (file);
1019: }
1020: output_addr_const (file, x);
1021: }
1022: }
1023:
1024: /* Print a memory operand whose address is ADDR. */
1025:
1026: void
1027: print_operand_address (file, addr)
1028: FILE *file;
1029: register rtx addr;
1030: {
1031: register rtx reg1, reg2, breg, ireg;
1032: rtx offset;
1033:
1034: switch (GET_CODE (addr))
1035: {
1036: case REG:
1037: ADDR_BEG (file);
1038: fprintf (file, "%se", RP);
1039: fputs (hi_reg_name[REGNO (addr)], file);
1040: ADDR_END (file);
1041: break;
1042:
1043: case PLUS:
1044: reg1 = 0;
1045: reg2 = 0;
1046: ireg = 0;
1047: breg = 0;
1048: offset = 0;
1049: if (CONSTANT_ADDRESS_P (XEXP (addr, 0)))
1050: {
1051: offset = XEXP (addr, 0);
1052: addr = XEXP (addr, 1);
1053: }
1054: else if (CONSTANT_ADDRESS_P (XEXP (addr, 1)))
1055: {
1056: offset = XEXP (addr, 1);
1057: addr = XEXP (addr, 0);
1058: }
1059: if (GET_CODE (addr) != PLUS) ;
1060: else if (GET_CODE (XEXP (addr, 0)) == MULT)
1061: {
1062: reg1 = XEXP (addr, 0);
1063: addr = XEXP (addr, 1);
1064: }
1065: else if (GET_CODE (XEXP (addr, 1)) == MULT)
1066: {
1067: reg1 = XEXP (addr, 1);
1068: addr = XEXP (addr, 0);
1069: }
1070: else if (GET_CODE (XEXP (addr, 0)) == REG)
1071: {
1072: reg1 = XEXP (addr, 0);
1073: addr = XEXP (addr, 1);
1074: }
1075: else if (GET_CODE (XEXP (addr, 1)) == REG)
1076: {
1077: reg1 = XEXP (addr, 1);
1078: addr = XEXP (addr, 0);
1079: }
1080: if (GET_CODE (addr) == REG || GET_CODE (addr) == MULT)
1081: {
1082: if (reg1 == 0) reg1 = addr;
1083: else reg2 = addr;
1084: addr = 0;
1085: }
1086: if (offset != 0)
1087: {
1088: if (addr != 0) abort ();
1089: addr = offset;
1090: }
1091: if ((reg1 && GET_CODE (reg1) == MULT)
1092: || (reg2 != 0 && REGNO_OK_FOR_BASE_P (REGNO (reg2))))
1093: {
1094: breg = reg2;
1095: ireg = reg1;
1096: }
1097: else if (reg1 != 0 && REGNO_OK_FOR_BASE_P (REGNO (reg1)))
1098: {
1099: breg = reg1;
1100: ireg = reg2;
1101: }
1102:
1103: if (ireg != 0 || breg != 0)
1104: {
1105: int scale = 1;
1106:
1107: if (addr != 0)
1108: {
1109: if (GET_CODE (addr) == LABEL_REF)
1110: output_asm_label (addr);
1111: else
1112: output_addr_const (file, addr);
1113: }
1114:
1115: if (ireg != 0 && GET_CODE (ireg) == MULT)
1116: {
1117: scale = INTVAL (XEXP (ireg, 1));
1118: ireg = XEXP (ireg, 0);
1119: }
1120: /* output breg+ireg*scale */
1121: PRINT_B_I_S (breg, ireg, scale, file);
1122: break;
1123: }
1124:
1.1.1.2 root 1125: case MULT:
1126: {
1127: int scale;
1128: if (GET_CODE (XEXP (addr, 0)) == CONST_INT)
1129: {
1130: scale = INTVAL (XEXP (addr, 0));
1131: ireg = XEXP (addr, 1);
1132: }
1133: else
1134: {
1135: scale = INTVAL (XEXP (addr, 1));
1136: ireg = XEXP (addr, 0);
1137: }
1138: output_addr_const (file, const0_rtx);
1139: PRINT_B_I_S ((rtx) 0, ireg, scale, file);
1140: }
1141: break;
1142:
1.1 root 1143: default:
1144: if (GET_CODE (addr) == CONST_INT
1145: && INTVAL (addr) < 0x8000
1146: && INTVAL (addr) >= -0x8000)
1147: fprintf (file, "%d", INTVAL (addr));
1148: else
1149: output_addr_const (file, addr);
1150: }
1151: }
1152:
1153: /* Set the cc_status for the results of an insn whose pattern is EXP.
1154: On the 80386, we assume that only test and compare insns, as well
1155: as SI, HI, & DI mode ADD, SUB, NEG, AND, IOR, XOR, ASHIFT, LSHIFT,
1156: ASHIFTRT, and LSHIFTRT instructions set the condition codes usefully.
1157: Also, we assume that jumps and moves don't affect the condition codes.
1158: All else, clobbers the condition codes, by assumption.
1159:
1160: We assume that ALL add, minus, etc. instructions effect the condition
1161: codes. This MUST be consistent with i386.md. */
1162:
1163: notice_update_cc (exp)
1164: rtx exp;
1165: {
1166: if (GET_CODE (exp) == SET)
1167: {
1168: /* Jumps do not alter the cc's. */
1169: if (SET_DEST (exp) == pc_rtx)
1170: return;
1171: /* Moving register or memory into a register:
1172: it doesn't alter the cc's, but it might invalidate
1173: the RTX's which we remember the cc's came from.
1174: (Note that moving a constant 0 or 1 MAY set the cc's). */
1175: if (REG_P (SET_DEST (exp))
1176: && (REG_P (SET_SRC (exp)) || GET_CODE (SET_SRC (exp)) == MEM))
1177: {
1178: if (cc_status.value1
1179: && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value1))
1180: cc_status.value1 = 0;
1181: if (cc_status.value2
1182: && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value2))
1183: cc_status.value2 = 0;
1184: return;
1185: }
1186: /* Moving register into memory doesn't alter the cc's.
1187: It may invalidate the RTX's which we remember the cc's came from. */
1188: if (GET_CODE (SET_DEST (exp)) == MEM && REG_P (SET_SRC (exp)))
1189: {
1190: if (cc_status.value1 && GET_CODE (cc_status.value1) == MEM)
1191: cc_status.value1 = 0;
1192: if (cc_status.value2 && GET_CODE (cc_status.value2) == MEM)
1193: cc_status.value2 = 0;
1194: return;
1195: }
1196: /* Function calls clobber the cc's. */
1197: else if (GET_CODE (SET_SRC (exp)) == CALL)
1198: {
1199: CC_STATUS_INIT;
1200: return;
1201: }
1202: /* Tests and compares set the cc's in predictable ways. */
1203: else if (SET_DEST (exp) == cc0_rtx)
1204: {
1205: CC_STATUS_INIT;
1206: cc_status.value1 = SET_SRC (exp);
1207: return;
1208: }
1209: /* Certain instructions effect the condition codes. */
1210: else if (GET_MODE (SET_SRC (exp)) == SImode
1211: || GET_MODE (SET_SRC (exp)) == HImode
1212: || GET_MODE (SET_SRC (exp)) == QImode)
1213: switch (GET_CODE (SET_SRC (exp)))
1214: {
1215: case ASHIFTRT: case LSHIFTRT:
1216: case ASHIFT: case LSHIFT:
1217: /* Shifts on the 386 don't set the condition codes if the
1218: shift count is zero. */
1219: if (GET_CODE (XEXP (SET_SRC (exp), 1)) != CONST_INT)
1220: {
1221: CC_STATUS_INIT;
1222: break;
1223: }
1224: /* We assume that the CONST_INT is non-zero (this rtx would
1225: have been deleted if it were zero. */
1226:
1227: case PLUS: case MINUS: case NEG:
1228: case AND: case IOR: case XOR:
1229: cc_status.flags = CC_NO_OVERFLOW;
1230: cc_status.value1 = SET_SRC (exp);
1231: cc_status.value2 = SET_DEST (exp);
1232: break;
1233:
1234: default:
1235: CC_STATUS_INIT;
1236: }
1237: else
1238: {
1239: CC_STATUS_INIT;
1240: }
1241: }
1242: else if (GET_CODE (exp) == PARALLEL
1243: && GET_CODE (XVECEXP (exp, 0, 0)) == SET)
1244: {
1245: if (SET_DEST (XVECEXP (exp, 0, 0)) == pc_rtx)
1246: return;
1247: if (SET_DEST (XVECEXP (exp, 0, 0)) == cc0_rtx)
1248: {
1249: CC_STATUS_INIT;
1250: cc_status.value1 = SET_SRC (XVECEXP (exp, 0, 0));
1251: return;
1252: }
1253: CC_STATUS_INIT;
1254: }
1255: else
1256: {
1257: CC_STATUS_INIT;
1258: }
1259: }
1260:
1261: /* Nonzero if the top of the fpu stack dies in this insn. */
1262:
1263: int
1264: top_dead_p (insn)
1265: rtx insn;
1266: {
1267: extern int optimize;
1268: if (optimize)
1269: return (find_regno_note (insn, REG_DEAD, FIRST_FLOAT_REG)
1270: || find_regno_note (insn, REG_DEAD, FIRST_FLOAT_REG + 1));
1271:
1272: if (GET_CODE (insn) == CALL_INSN)
1273: return call_top_dead_p (insn);
1274:
1275: return fp_top_dead_p1 (insn);
1276: }
1277:
1278: /* Following is used after a call_value insn
1279: if obey_regdecls there will not be the REG_DEAD notes
1280: to go by (there won't be any cross jumping to worry about
1281: either), and we depend on seeing if the FP_TOP is used
1282: in the next two insn's. Otherwise we depend on the
1283: REG_DEAD notes.
1284: */
1285:
1286: static int
1287: call_top_dead_p (insn)
1288: rtx insn;
1289: {
1290: int i;
1291: for (i = 0; i < 3; i++)
1292: {
1293: insn = NEXT_INSN (insn);
1294: if (insn == 0)
1295: return 1;
1296: if (GET_CODE (insn) == NOTE || GET_CODE (insn) == CODE_LABEL)
1297: continue;
1298: if (GET_CODE (insn) == BARRIER)
1299: abort ();
1300: if (GET_CODE (PATTERN (insn)) == SET
1301: && SET_DEST (PATTERN (insn)) != stack_pointer_rtx)
1302: return (!(mentions_fp_top (SET_SRC (PATTERN (insn)))));
1303: if (GET_CODE (PATTERN (insn)) == CALL)
1304: return 1;
1305: if (GET_CODE (PATTERN (insn)) == USE)
1306: return (! FP_REG_P (XEXP (PATTERN (insn), 0)));
1307: }
1308: return 1;
1309: }
1310:
1311: /* Return 1 if current val of fpu top-of-stack appears unused
1.1.1.5 ! root 1312: in rest of this basic block and also through a jump insn.
! 1313: This is called from top_dead_p and from fp_call_internal. */
1.1 root 1314:
1315: static int
1316: fp_top_dead_p1 (insn)
1317: rtx insn;
1318: {
1.1.1.4 root 1319: extern int optimize;
1320:
1.1.1.5 ! root 1321: int past_jump = 0;
1.1.1.4 root 1322:
1.1 root 1323: for (insn = NEXT_INSN (insn); insn; insn = NEXT_INSN (insn))
1324: {
1325: switch (GET_CODE (insn))
1326: {
1327: case CALL_INSN:
1328: /* Function calls clobber this value, so it's dead. */
1.1.1.4 root 1329: return 1;
1330:
1.1 root 1331: case JUMP_INSN:
1.1.1.5 ! root 1332: /* Follow one jump in case of cross-jumping,
! 1333: which could insert such a jump into one basic block. */
1.1.1.4 root 1334: if (! optimize)
1335: /* Can't use JUMP_LABEL, but there's no cross-jumping either. */
1336: return 1;
1337: if (JUMP_LABEL (insn) == 0)
1338: return 1;
1.1.1.5 ! root 1339: /* Don't scan past a jump and another jump. */
! 1340: if (past_jump)
! 1341: return 1;
! 1342: past_jump = 1;
1.1.1.4 root 1343: insn = JUMP_LABEL (insn);
1.1 root 1344: case CODE_LABEL:
1.1.1.4 root 1345: if (! optimize)
1346: return 1;
1347: break;
1.1 root 1348:
1349: case INSN:
1350: if (GET_CODE (PATTERN (insn)) == SET)
1351: {
1352: if ((mentions_fp_top (SET_SRC (PATTERN (insn)))))
1353: return 0;
1.1.1.5 ! root 1354: else if (mentions_fp_top (SET_DEST (PATTERN (insn))))
1.1 root 1355: return 1;
1356: }
1357: else if (mentions_fp_top (PATTERN (insn)))
1358: return 0;
1359: break;
1360: }
1361: }
1362: return 1;
1363: }
1364:
1365: /* Return 1 if X involves an FPU register. */
1366:
1367: static int
1368: mentions_fp_top (x)
1369: rtx x;
1370: {
1371: register RTX_CODE code;
1372:
1373: code = GET_CODE (x);
1374: switch (code)
1375: {
1376: case LABEL_REF:
1377: case SYMBOL_REF:
1378: case CONST_INT:
1379: case CONST:
1380: case CC0:
1381: case PC:
1382: case CLOBBER:
1383: case MEM:
1384: return 0;
1385:
1386: case REG:
1387: return FP_REGNO_P (REGNO (x));
1388: }
1389:
1390: /* Recursively scan the operands of this expression. */
1391: {
1392: register char *fmt = GET_RTX_FORMAT (code);
1393: register int i;
1394:
1395: for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
1396: {
1397: if (fmt[i] == 'e')
1398: {
1399: if (mentions_fp_top (XEXP (x, i)))
1400: return 1;
1401: }
1402: if (fmt[i] == 'E')
1403: {
1404: register int j;
1405: for (j = 0; j < XVECLEN (x, i); j++)
1406: if (mentions_fp_top (XVECEXP (x, i, j)))
1407: return 1;
1408: }
1409: }
1410: }
1411: return 0;
1412: }
1413:
1414: /* Some asm-dependent functions. */
1415:
1416: #ifdef MASM
1417: #include "masm386.c"
1418: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.