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