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