PR target/16201
[official-gcc.git] / gcc / config / ns32k / ns32k.c
blobbad908d49d5675401b708ec8a21b87fea2b84e92
1 /* Subroutines for assembler code output on the NS32000.
2 Copyright (C) 1988, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2004
3 Free Software Foundation, Inc.
5 This file is part of GCC.
7 GCC is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
12 GCC is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING. If not, write to
19 the Free Software Foundation, 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA. */
22 #include "config.h"
23 #include "system.h"
24 #include "coretypes.h"
25 #include "tm.h"
26 #include "rtl.h"
27 #include "regs.h"
28 #include "hard-reg-set.h"
29 #include "real.h"
30 #include "insn-config.h"
31 #include "conditions.h"
32 #include "output.h"
33 #include "insn-attr.h"
34 #include "tree.h"
35 #include "function.h"
36 #include "expr.h"
37 #include "flags.h"
38 #include "recog.h"
39 #include "tm_p.h"
40 #include "target.h"
41 #include "target-def.h"
42 #include "toplev.h"
44 #ifdef OSF_OS
45 int ns32k_num_files = 0;
46 #endif
48 /* This duplicates reg_class_contents in reg_class.c, but maybe that isn't
49 initialized in time. Also this is more convenient as an array of ints.
50 We know that HARD_REG_SET fits in an unsigned int */
52 const unsigned int ns32k_reg_class_contents[N_REG_CLASSES][1] = REG_CLASS_CONTENTS;
54 const enum reg_class regclass_map[FIRST_PSEUDO_REGISTER] =
56 GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
57 GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
58 FLOAT_REG0, LONG_FLOAT_REG0, FLOAT_REGS, FLOAT_REGS,
59 FLOAT_REGS, FLOAT_REGS, FLOAT_REGS, FLOAT_REGS,
60 LONG_REGS, LONG_REGS, LONG_REGS, LONG_REGS,
61 LONG_REGS, LONG_REGS, LONG_REGS, LONG_REGS,
62 FRAME_POINTER_REG, STACK_POINTER_REG
65 static const char *const ns32k_out_reg_names[] = OUTPUT_REGISTER_NAMES;
67 static rtx gen_indexed_expr (rtx, rtx, rtx);
68 static const char *singlemove_string (rtx *);
69 static void move_tail (rtx[], int, int);
70 static tree ns32k_handle_fntype_attribute (tree *, tree, tree, int, bool *);
71 const struct attribute_spec ns32k_attribute_table[];
72 static void ns32k_output_function_prologue (FILE *, HOST_WIDE_INT);
73 static void ns32k_output_function_epilogue (FILE *, HOST_WIDE_INT);
74 static bool ns32k_rtx_costs (rtx, int, int, int *);
75 static int ns32k_address_cost (rtx);
76 static rtx ns32k_struct_value_rtx (tree, int);
77 static int ns32k_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode,
78 tree, bool);
80 /* Initialize the GCC target structure. */
81 #undef TARGET_ATTRIBUTE_TABLE
82 #define TARGET_ATTRIBUTE_TABLE ns32k_attribute_table
84 #undef TARGET_ASM_ALIGNED_HI_OP
85 #define TARGET_ASM_ALIGNED_HI_OP "\t.word\t"
87 #ifdef ENCORE_ASM
88 #undef TARGET_ASM_ALIGNED_SI_OP
89 #define TARGET_ASM_ALIGNED_SI_OP "\t.double\t"
90 #endif
92 #undef TARGET_ASM_FUNCTION_PROLOGUE
93 #define TARGET_ASM_FUNCTION_PROLOGUE ns32k_output_function_prologue
94 #undef TARGET_ASM_FUNCTION_EPILOGUE
95 #define TARGET_ASM_FUNCTION_EPILOGUE ns32k_output_function_epilogue
97 #undef TARGET_RTX_COSTS
98 #define TARGET_RTX_COSTS ns32k_rtx_costs
99 #undef TARGET_ADDRESS_COST
100 #define TARGET_ADDRESS_COST ns32k_address_cost
102 #undef TARGET_STRUCT_VALUE_RTX
103 #define TARGET_STRUCT_VALUE_RTX ns32k_struct_value_rtx
105 #undef TARGET_ARG_PARTIAL_BYTES
106 #define TARGET_ARG_PARTIAL_BYTES ns32k_arg_partial_bytes
108 #undef TARGET_ASM_FILE_START_APP_OFF
109 #define TARGET_ASM_FILE_START_APP_OFF true
111 struct gcc_target targetm = TARGET_INITIALIZER;
113 /* Generate the assembly code for function entry. FILE is a stdio
114 stream to output the code to. SIZE is an int: how many units of
115 temporary storage to allocate.
117 Refer to the array `regs_ever_live' to determine which registers to
118 save; `regs_ever_live[I]' is nonzero if register number I is ever
119 used in the function. This function is responsible for knowing
120 which registers should not be saved even if used. */
123 * The function prologue for the ns32k is fairly simple.
124 * If a frame pointer is needed (decided in reload.c ?) then
125 * we need assembler of the form
127 * # Save the oldframe pointer, set the new frame pointer, make space
128 * # on the stack and save any general purpose registers necessary
130 * enter [<general purpose regs to save>], <local stack space>
132 * movf fn, tos # Save any floating point registers necessary
136 * If a frame pointer is not needed we need assembler of the form
138 * # Make space on the stack
140 * adjspd <local stack space + 4>
142 * # Save any general purpose registers necessary
144 * save [<general purpose regs to save>]
146 * movf fn, tos # Save any floating point registers necessary
151 #if !defined (MERLIN_TARGET) && !defined (UTEK_ASM)
153 #if defined(IMMEDIATE_PREFIX) && IMMEDIATE_PREFIX
154 #define ADJSP(FILE, N) \
155 fprintf (FILE, "\tadjspd %c" HOST_WIDE_INT_PRINT_DEC "\n", IMMEDIATE_PREFIX, (N))
156 #else
157 #define ADJSP(FILE, N) \
158 fprintf (FILE, "\tadjspd " HOST_WIDE_INT_PRINT_DEC "\n", (N))
159 #endif
161 static void
162 ns32k_output_function_prologue (FILE *file, HOST_WIDE_INT size)
164 register int regno, g_regs_used = 0;
165 int used_regs_buf[8], *bufp = used_regs_buf;
166 int used_fregs_buf[17], *fbufp = used_fregs_buf;
168 for (regno = R0_REGNUM; regno < F0_REGNUM; regno++)
169 if (regs_ever_live[regno]
170 && ! call_used_regs[regno])
172 *bufp++ = regno; g_regs_used++;
174 *bufp = -1;
176 for (; regno < FRAME_POINTER_REGNUM; regno++)
177 if (regs_ever_live[regno] && !call_used_regs[regno])
179 *fbufp++ = regno;
181 *fbufp = -1;
183 bufp = used_regs_buf;
184 if (frame_pointer_needed)
185 fprintf (file, "\tenter [");
186 else
188 if (size)
189 ADJSP (file, size + 4);
190 if (g_regs_used && g_regs_used > 4)
191 fprintf (file, "\tsave [");
192 else
194 while (*bufp >= 0)
195 fprintf (file, "\tmovd r%d,tos\n", *bufp++);
196 g_regs_used = 0;
200 while (*bufp >= 0)
202 fprintf (file, "r%d", *bufp++);
203 if (*bufp >= 0)
204 fputc (',', file);
207 if (frame_pointer_needed)
208 fprintf (file, "]," HOST_WIDE_INT_PRINT_DEC "\n", size);
209 else if (g_regs_used)
210 fprintf (file, "]\n");
212 fbufp = used_fregs_buf;
213 while (*fbufp >= 0)
215 if ((*fbufp & 1) || (fbufp[0] != fbufp[1] - 1))
216 fprintf (file, "\tmovf %s,tos\n", ns32k_out_reg_names[*fbufp++]);
217 else
219 fprintf (file, "\tmovl %s,tos\n",
220 ns32k_out_reg_names[fbufp[0]]);
221 fbufp += 2;
225 if (flag_pic && current_function_uses_pic_offset_table)
227 fprintf (file, "\tsprd sb,tos\n");
228 if (TARGET_REGPARM)
230 fprintf (file, "\taddr __GLOBAL_OFFSET_TABLE_(pc),tos\n");
231 fprintf (file, "\tlprd sb,tos\n");
233 else
235 fprintf (file, "\taddr __GLOBAL_OFFSET_TABLE_(pc),r0\n");
236 fprintf (file, "\tlprd sb,r0\n");
241 #else /* MERLIN_TARGET || UTEK_ASM */
243 /* This differs from the standard one above in printing a bitmask
244 rather than a register list in the enter or save instruction. */
246 static void
247 ns32k_output_function_prologue (file, size)
248 FILE *file;
249 HOST_WIDE_INT size;
251 register int regno, g_regs_used = 0;
252 int used_regs_buf[8], *bufp = used_regs_buf;
253 int used_fregs_buf[8], *fbufp = used_fregs_buf;
255 for (regno = 0; regno < 8; regno++)
256 if (regs_ever_live[regno]
257 && ! call_used_regs[regno])
259 *bufp++ = regno; g_regs_used++;
261 *bufp = -1;
263 for (; regno < 16; regno++)
264 if (regs_ever_live[regno] && !call_used_regs[regno]) {
265 *fbufp++ = regno;
267 *fbufp = -1;
269 bufp = used_regs_buf;
270 if (frame_pointer_needed)
271 fprintf (file, "\tenter ");
272 else if (g_regs_used)
273 fprintf (file, "\tsave ");
275 if (frame_pointer_needed || g_regs_used)
277 char mask = 0;
278 while (*bufp >= 0)
279 mask |= 1 << *bufp++;
280 fprintf (file, "$0x%x", (int) mask & 0xff);
283 if (frame_pointer_needed)
284 #ifdef UTEK_ASM
285 fprintf (file, ",$%d\n", size);
286 #else
287 fprintf (file, ",%d\n", size);
288 #endif
289 else if (g_regs_used)
290 fprintf (file, "\n");
292 fbufp = used_fregs_buf;
293 while (*fbufp >= 0)
295 if ((*fbufp & 1) || (fbufp[0] != fbufp[1] - 1))
296 fprintf (file, "\tmovf f%d,tos\n", *fbufp++ - 8);
297 else
299 fprintf (file, "\tmovl f%d,tos\n", fbufp[0] - 8);
300 fbufp += 2;
305 #endif /* MERLIN_TARGET || UTEK_ASM */
307 /* This function generates the assembly code for function exit,
308 on machines that need it.
310 The function epilogue should not depend on the current stack pointer,
311 if EXIT_IGNORE_STACK is nonzero. That doesn't apply here.
313 If a frame pointer is needed (decided in reload.c ?) then
314 we need assembler of the form
316 movf tos, fn # Restore any saved floating point registers
320 # Restore any saved general purpose registers, restore the stack
321 # pointer from the frame pointer, restore the old frame pointer.
322 exit [<general purpose regs to save>]
324 If a frame pointer is not needed we need assembler of the form
325 # Restore any general purpose registers saved
327 movf tos, fn # Restore any saved floating point registers
331 restore [<general purpose regs to save>]
333 # reclaim space allocated on stack
335 adjspd <-(local stack space + 4)> */
337 #if !defined (MERLIN_TARGET) && !defined (UTEK_ASM)
339 static void
340 ns32k_output_function_epilogue (FILE *file, HOST_WIDE_INT size)
342 register int regno, g_regs_used = 0, f_regs_used = 0;
343 int used_regs_buf[8], *bufp = used_regs_buf;
344 int used_fregs_buf[17], *fbufp = used_fregs_buf;
346 if (flag_pic && current_function_uses_pic_offset_table)
347 fprintf (file, "\tlprd sb,tos\n");
349 *fbufp++ = -2;
350 for (regno = F0_REGNUM; regno < FRAME_POINTER_REGNUM; regno++)
351 if (regs_ever_live[regno] && !call_used_regs[regno])
353 *fbufp++ = regno; f_regs_used++;
355 fbufp--;
357 for (regno = 0; regno < F0_REGNUM; regno++)
358 if (regs_ever_live[regno]
359 && ! call_used_regs[regno])
361 *bufp++ = regno; g_regs_used++;
364 while (fbufp > used_fregs_buf)
366 if ((*fbufp & 1) && fbufp[0] == fbufp[-1] + 1)
368 fprintf (file, "\tmovl tos,%s\n",
369 ns32k_out_reg_names[fbufp[-1]]);
370 fbufp -= 2;
372 else fprintf (file, "\tmovf tos,%s\n", ns32k_out_reg_names[*fbufp--]);
375 if (frame_pointer_needed)
376 fprintf (file, "\texit [");
377 else
379 if (g_regs_used && g_regs_used > 4)
380 fprintf (file, "\trestore [");
381 else
383 while (bufp > used_regs_buf)
384 fprintf (file, "\tmovd tos,r%d\n", *--bufp);
385 g_regs_used = 0;
389 while (bufp > used_regs_buf)
391 fprintf (file, "r%d", *--bufp);
392 if (bufp > used_regs_buf)
393 fputc (',', file);
396 if (g_regs_used || frame_pointer_needed)
397 fprintf (file, "]\n");
399 if (size && !frame_pointer_needed)
400 ADJSP (file, -(size + 4));
402 if (current_function_pops_args)
403 fprintf (file, "\tret %d\n", current_function_pops_args);
404 else
405 fprintf (file, "\tret 0\n");
408 #else /* MERLIN_TARGET || UTEK_ASM */
410 /* This differs from the standard one above in printing a bitmask
411 rather than a register list in the exit or restore instruction. */
413 static void
414 ns32k_output_function_epilogue (file, size)
415 FILE *file;
416 HOST_WIDE_INT size ATTRIBUTE_UNUSED;
418 register int regno, g_regs_used = 0, f_regs_used = 0;
419 int used_regs_buf[8], *bufp = used_regs_buf;
420 int used_fregs_buf[8], *fbufp = used_fregs_buf;
422 *fbufp++ = -2;
423 for (regno = 8; regno < 16; regno++)
424 if (regs_ever_live[regno] && !call_used_regs[regno]) {
425 *fbufp++ = regno; f_regs_used++;
427 fbufp--;
429 for (regno = 0; regno < 8; regno++)
430 if (regs_ever_live[regno]
431 && ! call_used_regs[regno])
433 *bufp++ = regno; g_regs_used++;
436 while (fbufp > used_fregs_buf)
438 if ((*fbufp & 1) && fbufp[0] == fbufp[-1] + 1)
440 fprintf (file, "\tmovl tos,f%d\n", fbufp[-1] - 8);
441 fbufp -= 2;
443 else fprintf (file, "\tmovf tos,f%d\n", *fbufp-- - 8);
446 if (frame_pointer_needed)
447 fprintf (file, "\texit ");
448 else if (g_regs_used)
449 fprintf (file, "\trestore ");
451 if (g_regs_used || frame_pointer_needed)
453 char mask = 0;
455 while (bufp > used_regs_buf)
457 /* Utek assembler takes care of reversing this */
458 mask |= 1 << *--bufp;
460 fprintf (file, "$0x%x\n", (int) mask & 0xff);
463 #ifdef UTEK_ASM
464 if (current_function_pops_args)
465 fprintf (file, "\tret $%d\n", current_function_pops_args);
466 else
467 fprintf (file, "\tret $0\n");
468 #else
469 if (current_function_pops_args)
470 fprintf (file, "\tret %d\n", current_function_pops_args);
471 else
472 fprintf (file, "\tret 0\n");
473 #endif
476 #endif /* MERLIN_TARGET || UTEK_ASM */
478 /* Value is 1 if hard register REGNO can hold a value of machine-mode MODE. */
480 hard_regno_mode_ok (int regno, enum machine_mode mode)
482 int size = GET_MODE_UNIT_SIZE (mode);
484 if (FLOAT_MODE_P (mode))
486 if (size == UNITS_PER_WORD && regno < L1_REGNUM)
487 return 1;
488 if (size == UNITS_PER_WORD * 2
489 && (((regno & 1) == 0 && regno < FRAME_POINTER_REGNUM)))
490 return 1;
491 return 0;
493 if (size == UNITS_PER_WORD * 2
494 && (regno & 1) == 0 && regno < F0_REGNUM)
495 return 1;
496 if (size <= UNITS_PER_WORD
497 && (regno < F0_REGNUM || regno == FRAME_POINTER_REGNUM
498 || regno == STACK_POINTER_REGNUM))
499 return 1;
500 return 0;
503 static bool
504 ns32k_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int *total)
506 switch (code)
508 case CONST_INT:
509 if (INTVAL (x) <= 7 && INTVAL (x) >= -8)
510 *total = 0;
511 else if (INTVAL (x) < 0x2000 && INTVAL (x) >= -0x2000)
512 *total = 1;
513 else
514 *total = 3;
515 return true;
517 case CONST:
518 case LABEL_REF:
519 case SYMBOL_REF:
520 *total = 3;
521 return true;
523 case CONST_DOUBLE:
524 *total = 5;
525 return true;
527 default:
528 return false;
533 register_move_cost (enum reg_class CLASS1, enum reg_class CLASS2)
535 if (CLASS1 == NO_REGS || CLASS2 == NO_REGS)
536 return 2;
537 if ((SUBSET_P (CLASS1, FP_REGS) && !SUBSET_P (CLASS2, FP_REGS))
538 || (!SUBSET_P (CLASS1, FP_REGS) && SUBSET_P (CLASS2, FP_REGS)))
539 return 8;
540 if (((CLASS1) == STACK_POINTER_REG && !SUBSET_P (CLASS2,GENERAL_REGS))
541 || ((CLASS2) == STACK_POINTER_REG && !SUBSET_P (CLASS1,GENERAL_REGS)))
542 return 6;
543 if (((CLASS1) == FRAME_POINTER_REG && !SUBSET_P (CLASS2,GENERAL_REGS))
544 || ((CLASS2) == FRAME_POINTER_REG && !SUBSET_P (CLASS1,GENERAL_REGS)))
545 return 6;
546 return 2;
549 #if 0
550 /* We made the insn definitions copy from floating point to general
551 registers via the stack. */
553 secondary_memory_needed (enum reg_class CLASS1,
554 enum reg_class CLASS2,
555 enum machine_mode M)
557 int ret = ((SUBSET_P (CLASS1, FP_REGS) && !SUBSET_P (CLASS2, FP_REGS))
558 || (!SUBSET_P (CLASS1, FP_REGS) && SUBSET_P (CLASS2, FP_REGS)));
559 return ret;
561 #endif
564 /* TARGET_ADDRESS_COST calls this. This function is not optimal
565 for the 32032 & 32332, but it probably is better than
566 the default. */
568 static int
569 ns32k_address_cost (rtx operand)
571 int cost = 0;
573 switch (GET_CODE (operand))
575 case REG:
576 cost += 1;
577 break;
579 case POST_DEC:
580 case PRE_DEC:
581 break;
583 case CONST_INT:
584 if (INTVAL (operand) <= 7 && INTVAL (operand) >= -8)
585 break;
586 if (INTVAL (operand) < 0x2000 && INTVAL (operand) >= -0x2000)
588 cost +=1;
589 break;
591 case CONST:
592 case LABEL_REF:
593 case SYMBOL_REF:
594 cost +=3;
595 break;
596 case CONST_DOUBLE:
597 cost += 5;
598 break;
600 case MEM:
601 cost += ns32k_address_cost (XEXP (operand, 0)) + 3;
602 break;
604 case MULT:
605 cost += 2;
606 /* FALLTHRU */
607 case PLUS:
608 cost += ns32k_address_cost (XEXP (operand, 0));
609 cost += ns32k_address_cost (XEXP (operand, 1));
610 break;
612 default:
613 break;
616 return cost;
619 /* Return the register class of a scratch register needed to copy IN into
620 or out of a register in CLASS in MODE. If it can be done directly,
621 NO_REGS is returned. */
623 enum reg_class
624 secondary_reload_class (enum reg_class class,
625 enum machine_mode mode ATTRIBUTE_UNUSED,
626 rtx in)
628 int regno = true_regnum (in);
630 if (regno >= FIRST_PSEUDO_REGISTER)
631 regno = -1;
633 if ((class == FRAME_POINTER_REG && regno == STACK_POINTER_REGNUM)
634 || ( class == STACK_POINTER_REG && regno == FRAME_POINTER_REGNUM))
635 return GENERAL_REGS;
636 else
637 return NO_REGS;
640 /* Generate the rtx that comes from an address expression in the md file */
641 /* The expression to be build is BASE[INDEX:SCALE]. To recognize this,
642 scale must be converted from an exponent (from ASHIFT) to a
643 multiplier (for MULT). */
645 static rtx
646 gen_indexed_expr (rtx base, rtx index, rtx scale)
648 rtx addr;
650 /* This generates an invalid addressing mode, if BASE is
651 fp or sp. This is handled by PRINT_OPERAND_ADDRESS. */
652 if (GET_CODE (base) != REG && GET_CODE (base) != CONST_INT)
653 base = gen_rtx_MEM (SImode, base);
654 addr = gen_rtx_MULT (SImode, index,
655 GEN_INT (1 << INTVAL (scale)));
656 addr = gen_rtx_PLUS (SImode, base, addr);
657 return addr;
661 /* Split one or more DImode RTL references into pairs of SImode
662 references. The RTL can be REG, offsettable MEM, integer constant, or
663 CONST_DOUBLE. "operands" is a pointer to an array of DImode RTL to
664 split and "num" is its length. lo_half and hi_half are output arrays
665 that parallel "operands". */
667 void
668 split_di (rtx operands[], int num, rtx lo_half[], rtx hi_half[])
670 while (num--)
672 if (GET_CODE (operands[num]) == REG)
674 lo_half[num] = gen_rtx_REG (SImode, REGNO (operands[num]));
675 hi_half[num] = gen_rtx_REG (SImode, REGNO (operands[num]) + 1);
677 else if (CONSTANT_P (operands[num]))
679 split_double (operands[num], &lo_half[num], &hi_half[num]);
681 else if (offsettable_memref_p (operands[num]))
683 lo_half[num] = operands[num];
684 hi_half[num] = adjust_address (operands[num], SImode, 4);
686 else
687 abort ();
691 /* Return the best assembler insn template
692 for moving operands[1] into operands[0] as a fullword. */
694 static const char *
695 singlemove_string (rtx *operands)
697 if (GET_CODE (operands[1]) == CONST_INT
698 && INTVAL (operands[1]) <= 7
699 && INTVAL (operands[1]) >= -8)
700 return "movqd %1,%0";
701 return "movd %1,%0";
704 const char *
705 output_move_double (rtx *operands)
707 enum anon1 { REGOP, OFFSOP, PUSHOP, CNSTOP, RNDOP } optype0, optype1;
708 rtx latehalf[2];
710 /* First classify both operands. */
712 if (REG_P (operands[0]))
713 optype0 = REGOP;
714 else if (offsettable_memref_p (operands[0]))
715 optype0 = OFFSOP;
716 else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC)
717 optype0 = PUSHOP;
718 else
719 optype0 = RNDOP;
721 if (REG_P (operands[1]))
722 optype1 = REGOP;
723 else if (CONSTANT_P (operands[1])
724 || GET_CODE (operands[1]) == CONST_DOUBLE)
725 optype1 = CNSTOP;
726 else if (offsettable_memref_p (operands[1]))
727 optype1 = OFFSOP;
728 else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC)
729 optype1 = PUSHOP;
730 else
731 optype1 = RNDOP;
733 /* Check for the cases that the operand constraints are not
734 supposed to allow to happen. Abort if we get one,
735 because generating code for these cases is painful. */
737 if (optype0 == RNDOP || optype1 == RNDOP)
738 abort ();
740 /* Ok, we can do one word at a time.
741 Normally we do the low-numbered word first,
742 but if either operand is autodecrementing then we
743 do the high-numbered word first.
745 In either case, set up in LATEHALF the operands to use
746 for the high-numbered word and in some cases alter the
747 operands in OPERANDS to be suitable for the low-numbered word. */
749 if (optype0 == REGOP)
750 latehalf[0] = gen_rtx_REG (SImode, REGNO (operands[0]) + 1);
751 else if (optype0 == OFFSOP)
752 latehalf[0] = adjust_address (operands[0], SImode, 4);
753 else
754 latehalf[0] = operands[0];
756 if (optype1 == REGOP)
757 latehalf[1] = gen_rtx_REG (SImode, REGNO (operands[1]) + 1);
758 else if (optype1 == OFFSOP)
759 latehalf[1] = adjust_address (operands[1], SImode, 4);
760 else if (optype1 == CNSTOP)
761 split_double (operands[1], &operands[1], &latehalf[1]);
762 else
763 latehalf[1] = operands[1];
765 /* If insn is effectively movd N(sp),tos then we will do the
766 high word first. We should use the adjusted operand 1 (which is N+4(sp))
767 for the low word as well, to compensate for the first decrement of sp.
768 Given this, it doesn't matter which half we do "first". */
769 if (optype0 == PUSHOP
770 && REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM
771 && reg_overlap_mentioned_p (stack_pointer_rtx, operands[1]))
772 operands[1] = latehalf[1];
774 /* If one or both operands autodecrementing,
775 do the two words, high-numbered first. */
776 else if (optype0 == PUSHOP || optype1 == PUSHOP)
778 output_asm_insn (singlemove_string (latehalf), latehalf);
779 return singlemove_string (operands);
782 /* If the first move would clobber the source of the second one,
783 do them in the other order. */
785 /* Overlapping registers. */
786 if (optype0 == REGOP && optype1 == REGOP
787 && REGNO (operands[0]) == REGNO (latehalf[1]))
789 /* Do that word. */
790 output_asm_insn (singlemove_string (latehalf), latehalf);
791 /* Do low-numbered word. */
792 return singlemove_string (operands);
794 /* Loading into a register which overlaps a register used in the address. */
795 else if (optype0 == REGOP && optype1 != REGOP
796 && reg_overlap_mentioned_p (operands[0], operands[1]))
798 if (reg_mentioned_p (operands[0], XEXP (operands[1], 0))
799 && reg_mentioned_p (latehalf[0], XEXP (operands[1], 0)))
801 /* If both halves of dest are used in the src memory address,
802 load the destination address into the low reg (operands[0]).
803 Then it works to load latehalf first. */
804 rtx xops[2];
805 xops[0] = XEXP (operands[1], 0);
806 xops[1] = operands[0];
807 output_asm_insn ("addr %a0,%1", xops);
808 operands[1] = gen_rtx_MEM (DImode, operands[0]);
809 latehalf[1] = adjust_address (operands[1], SImode, 4);
810 /* The first half has the overlap, Do the late half first. */
811 output_asm_insn (singlemove_string (latehalf), latehalf);
812 /* Then clobber. */
813 return singlemove_string (operands);
815 if (reg_mentioned_p (operands[0], XEXP (operands[1], 0)))
817 /* The first half has the overlap, Do the late half first. */
818 output_asm_insn (singlemove_string (latehalf), latehalf);
819 /* Then clobber. */
820 return singlemove_string (operands);
824 /* Normal case. Do the two words, low-numbered first. */
826 output_asm_insn (singlemove_string (operands), operands);
828 operands[0] = latehalf[0];
829 operands[1] = latehalf[1];
830 return singlemove_string (operands);
834 #define MAX_UNALIGNED_COPY (32)
835 /* Expand string/block move operations.
837 operands[0] is the pointer to the destination.
838 operands[1] is the pointer to the source.
839 operands[2] is the number of bytes to move.
840 operands[3] is the alignment. */
842 static void
843 move_tail (rtx operands[], int bytes, int offset)
845 if (bytes & 2)
847 emit_move_insn (adjust_address (operands[0], HImode, offset),
848 adjust_address (operands[1], HImode, offset));
849 offset += 2;
851 if (bytes & 1)
852 emit_move_insn (adjust_address (operands[0], QImode, offset),
853 adjust_address (operands[1], QImode, offset));
856 void
857 expand_block_move (rtx operands[])
859 rtx bytes_rtx = operands[2];
860 rtx align_rtx = operands[3];
861 int constp = (GET_CODE (bytes_rtx) == CONST_INT);
862 int bytes = (constp ? INTVAL (bytes_rtx) : 0);
863 int align = INTVAL (align_rtx);
864 rtx src_reg = gen_rtx_REG (Pmode, 1);
865 rtx dest_reg = gen_rtx_REG (Pmode, 2);
866 rtx count_reg = gen_rtx_REG (SImode, 0);
868 if (constp && bytes <= 0)
869 return;
871 if (constp && bytes < 20)
873 int words = bytes >> 2;
875 if (words)
877 if (words < 3)
879 int offset = 0;
881 for (; words; words--, offset += 4)
882 emit_move_insn (adjust_address (operands[0], SImode, offset),
883 adjust_address (operands[1], SImode, offset));
885 else
887 /* Use movmd. It is slower than multiple movd's but more
888 compact. It is also slower than movsd for large copies
889 but causes less registers reloading so is better than movsd
890 for small copies. */
891 rtx src, dest;
892 dest = copy_addr_to_reg (XEXP (operands[0], 0));
893 src = copy_addr_to_reg (XEXP (operands[1], 0));
895 emit_insn (gen_movmemsi2(dest, src, GEN_INT (words)));
898 move_tail (operands, bytes & 3, bytes & ~3);
899 return;
902 if (align > UNITS_PER_WORD)
903 align = UNITS_PER_WORD;
905 /* Move the address into scratch registers. */
906 emit_insn (gen_rtx_CLOBBER (VOIDmode, dest_reg));
907 emit_move_insn (dest_reg, XEXP (operands[0], 0));
908 operands[0] = gen_rtx_MEM (SImode, dest_reg);
909 emit_insn (gen_rtx_CLOBBER (VOIDmode, src_reg));
910 emit_move_insn (src_reg, XEXP (operands[1], 0));
911 operands[1] = gen_rtx_MEM (SImode, src_reg);
912 emit_insn (gen_rtx_CLOBBER (VOIDmode, count_reg));
914 if (constp && (align == UNITS_PER_WORD || bytes < MAX_UNALIGNED_COPY))
916 /* constant no of bytes and aligned or small enough copy to not bother
917 * aligning. Emit insns to copy by words.
919 if (bytes >> 2)
921 emit_move_insn (count_reg, GEN_INT (bytes >> 2));
922 emit_insn (gen_movmemsi1 (GEN_INT (4)));
924 /* insns to copy rest */
925 move_tail (operands, bytes & 3, 0);
927 else if (align == UNITS_PER_WORD)
929 /* insns to copy by words */
930 emit_insn (gen_lshrsi3 (count_reg, bytes_rtx, const2_rtx));
931 emit_insn (gen_movmemsi1 (GEN_INT (4)));
932 if (constp)
934 move_tail (operands, bytes & 3, 0);
936 else
938 /* insns to copy rest */
939 emit_insn (gen_andsi3 (count_reg, bytes_rtx, GEN_INT (3)));
940 emit_insn (gen_movmemsi1 (const1_rtx));
943 else
945 /* Not aligned and we may have a lot to copy so it is worth
946 * aligning.
948 rtx aligned_label = gen_label_rtx ();
949 rtx bytes_reg;
951 bytes_reg = copy_to_mode_reg (SImode, bytes_rtx);
952 if (!constp)
954 /* Emit insns to test and skip over the alignment if it is
955 * not worth it. This doubles as a test to ensure that the alignment
956 * operation can't copy too many bytes
958 emit_insn (gen_cmpsi (bytes_reg, GEN_INT (MAX_UNALIGNED_COPY)));
959 emit_jump_insn (gen_blt (aligned_label));
962 /* Emit insns to do alignment at run time */
963 emit_insn (gen_negsi2 (count_reg, src_reg));
964 emit_insn (gen_andsi3 (count_reg, count_reg, GEN_INT (3)));
965 emit_insn (gen_subsi3 (bytes_reg, bytes_reg, count_reg));
966 emit_insn (gen_movmemsi1 (const1_rtx));
967 if (!constp)
968 emit_label (aligned_label);
970 /* insns to copy by words */
971 emit_insn (gen_lshrsi3 (count_reg, bytes_reg, const2_rtx));
972 emit_insn (gen_movmemsi1 (GEN_INT (4)));
974 /* insns to copy rest */
975 emit_insn (gen_andsi3 (count_reg, bytes_reg, GEN_INT (3)));
976 emit_insn (gen_movmemsi1 (const1_rtx));
981 /* Returns 1 if OP contains a global symbol reference */
984 global_symbolic_reference_mentioned_p (rtx op, int f)
986 register const char *fmt;
987 register int i;
989 if (GET_CODE (op) == SYMBOL_REF)
991 if (! SYMBOL_REF_LOCAL_P (op))
992 return 1;
993 else
994 return 0;
996 else if (f && GET_CODE (op) != CONST)
997 return 0;
999 fmt = GET_RTX_FORMAT (GET_CODE (op));
1000 for (i = GET_RTX_LENGTH (GET_CODE (op)) - 1; i >= 0; i--)
1002 if (fmt[i] == 'E')
1004 register int j;
1006 for (j = XVECLEN (op, i) - 1; j >= 0; j--)
1007 if (global_symbolic_reference_mentioned_p (XVECEXP (op, i, j), 0))
1008 return 1;
1010 else if (fmt[i] == 'e'
1011 && global_symbolic_reference_mentioned_p (XEXP (op, i), 0))
1012 return 1;
1015 return 0;
1019 /* Returns 1 if OP contains a symbol reference */
1022 symbolic_reference_mentioned_p (rtx op)
1024 register const char *fmt;
1025 register int i;
1027 if (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF)
1028 return 1;
1030 fmt = GET_RTX_FORMAT (GET_CODE (op));
1031 for (i = GET_RTX_LENGTH (GET_CODE (op)) - 1; i >= 0; i--)
1033 if (fmt[i] == 'E')
1035 register int j;
1037 for (j = XVECLEN (op, i) - 1; j >= 0; j--)
1038 if (symbolic_reference_mentioned_p (XVECEXP (op, i, j)))
1039 return 1;
1041 else if (fmt[i] == 'e' && symbolic_reference_mentioned_p (XEXP (op, i)))
1042 return 1;
1045 return 0;
1048 /* Table of machine-specific attributes. */
1050 const struct attribute_spec ns32k_attribute_table[] =
1052 /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
1053 /* Stdcall attribute says callee is responsible for popping arguments
1054 if they are not variable. */
1055 { "stdcall", 0, 0, false, true, true, ns32k_handle_fntype_attribute },
1056 /* Cdecl attribute says the callee is a normal C declaration */
1057 { "cdecl", 0, 0, false, true, true, ns32k_handle_fntype_attribute },
1058 { NULL, 0, 0, false, false, false, NULL }
1061 /* Handle an attribute requiring a FUNCTION_TYPE, FIELD_DECL or TYPE_DECL;
1062 arguments as in struct attribute_spec.handler. */
1063 static tree
1064 ns32k_handle_fntype_attribute (tree *node, tree name,
1065 tree args ATTRIBUTE_UNUSED,
1066 int flags ATTRIBUTE_UNUSED,
1067 bool *no_add_attrs)
1069 if (TREE_CODE (*node) != FUNCTION_TYPE
1070 && TREE_CODE (*node) != FIELD_DECL
1071 && TREE_CODE (*node) != TYPE_DECL)
1073 warning ("%qs attribute only applies to functions",
1074 IDENTIFIER_POINTER (name));
1075 *no_add_attrs = true;
1078 return NULL_TREE;
1082 /* Value is the number of bytes of arguments automatically
1083 popped when returning from a subroutine call.
1084 FUNDECL is the declaration node of the function (as a tree),
1085 FUNTYPE is the data type of the function (as a tree),
1086 or for a library call it is an identifier node for the subroutine name.
1087 SIZE is the number of bytes of arguments passed on the stack.
1089 On the ns32k, the RET insn may be used to pop them if the number
1090 of args is fixed, but if the number is variable then the caller
1091 must pop them all. RET can't be used for library calls now
1092 because the library is compiled with the Unix compiler.
1093 Use of RET is a selectable option, since it is incompatible with
1094 standard Unix calling sequences. If the option is not selected,
1095 the caller must always pop the args.
1097 The attribute stdcall is equivalent to RET on a per module basis. */
1100 ns32k_return_pops_args (tree fundecl ATTRIBUTE_UNUSED, tree funtype, int size)
1102 int rtd = TARGET_RTD;
1104 if (TREE_CODE (funtype) == IDENTIFIER_NODE)
1105 return rtd ? size : 0;
1107 /* Cdecl functions override -mrtd, and never pop the stack */
1108 if (lookup_attribute ("cdecl", TYPE_ATTRIBUTES (funtype)))
1109 return 0;
1111 /* Stdcall functions will pop the stack if not variable args */
1112 if (lookup_attribute ("stdcall", TYPE_ATTRIBUTES (funtype)))
1113 rtd = 1;
1115 if (rtd)
1117 if (TYPE_ARG_TYPES (funtype) == NULL_TREE
1118 || (TREE_VALUE (tree_last (TYPE_ARG_TYPES (funtype))) == void_type_node))
1119 return size;
1122 return 0;
1125 /* PRINT_OPERAND is defined to call this function,
1126 which is easier to debug than putting all the code in
1127 a macro definition in ns32k.h. */
1129 /* XXX time 12% of cpu time is in fprintf for non optimizing */
1130 void
1131 print_operand (FILE *file, rtx x, int code)
1133 if (code == '$')
1134 PUT_IMMEDIATE_PREFIX (file);
1135 else if (code == '?')
1136 PUT_EXTERNAL_PREFIX (file);
1137 else if (GET_CODE (x) == REG)
1138 fprintf (file, "%s", ns32k_out_reg_names[REGNO (x)]);
1139 else if (GET_CODE (x) == MEM)
1141 output_address (XEXP (x, 0));
1143 else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) != VOIDmode)
1145 REAL_VALUE_TYPE r;
1147 REAL_VALUE_FROM_CONST_DOUBLE (r, x);
1148 PUT_IMMEDIATE_PREFIX (file);
1149 if (GET_MODE (x) == DFmode)
1151 #ifdef SEQUENT_ASM
1152 /* Sequent likes its floating point constants as integers */
1153 long l[2];
1154 REAL_VALUE_TO_TARGET_DOUBLE (r, l);
1155 fprintf (file, "0Dx%08x%08x",
1156 l[!WORDS_BIG_ENDIAN], l[WORDS_BIG_ENDIAN]);
1157 #else
1158 char s[30];
1159 real_to_decimal (s, &r, sizeof (s), 0, 1);
1160 #ifdef ENCORE_ASM
1161 fprintf (file, "0f%s", s);
1162 #else
1163 fprintf (file, "0d%s", s);
1164 #endif
1165 #endif
1167 else
1169 #ifdef SEQUENT_ASM
1170 long l;
1171 REAL_VALUE_TO_TARGET_SINGLE (r, l);
1172 fprintf (file, "0Fx%08lx", l);
1173 #else
1174 char s[30];
1175 real_to_decimal (s, &r, sizeof (s), 0, 1);
1176 fprintf (file, "0f%s", s);
1177 #endif
1180 else
1182 if (flag_pic
1183 && GET_CODE (x) == CONST
1184 && symbolic_reference_mentioned_p (x))
1186 fprintf (stderr, "illegal constant for pic-mode: \n");
1187 print_rtl (stderr, x);
1188 fprintf (stderr, "\nGET_CODE (x) == %d, CONST == %d, symbolic_reference_mentioned_p (x) == %d\n",
1189 GET_CODE (x), CONST, symbolic_reference_mentioned_p (x));
1190 abort ();
1192 else if (flag_pic
1193 && (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF))
1195 output_addr_const (file, x);
1196 fprintf (file, "(sb)");
1198 else
1200 #ifdef NO_IMMEDIATE_PREFIX_IF_SYMBOLIC
1201 if (GET_CODE (x) == CONST_INT)
1202 #endif
1203 PUT_IMMEDIATE_PREFIX (file);
1204 output_addr_const (file, x);
1209 /* PRINT_OPERAND_ADDRESS is defined to call this function,
1210 which is easier to debug than putting all the code in
1211 a macro definition in ns32k.h . */
1213 /* Completely rewritten to get this to work with Gas for PC532 Mach.
1214 This function didn't work and I just wasn't able (nor very willing) to
1215 figure out how it worked.
1216 90-11-25 Tatu Yl|nen <ylo@cs.hut.fi> */
1218 void
1219 print_operand_address (register FILE *file, register rtx addr)
1221 static const char scales[] = { 'b', 'w', 'd', 0, 'q', };
1222 rtx offset, base, indexexp, tmp;
1223 int scale;
1224 extern int flag_pic;
1226 if (GET_CODE (addr) == PRE_DEC || GET_CODE (addr) == POST_DEC)
1228 fprintf (file, "tos");
1229 return;
1232 offset = NULL;
1233 base = NULL;
1234 indexexp = NULL;
1235 while (addr != NULL)
1237 if (GET_CODE (addr) == PLUS)
1239 if (GET_CODE (XEXP (addr, 0)) == PLUS)
1241 tmp = XEXP (addr, 1);
1242 addr = XEXP (addr, 0);
1244 else
1246 tmp = XEXP (addr,0);
1247 addr = XEXP (addr,1);
1250 else
1252 tmp = addr;
1253 addr = NULL;
1255 switch (GET_CODE (tmp))
1257 case PLUS:
1258 abort ();
1259 case MEM:
1260 if (base)
1262 indexexp = base;
1263 base = tmp;
1265 else
1266 base = tmp;
1267 break;
1268 case REG:
1269 if (REGNO (tmp) < F0_REGNUM)
1270 if (base)
1272 indexexp = tmp;
1274 else
1275 base = tmp;
1276 else
1277 if (base)
1279 indexexp = base;
1280 base = tmp;
1282 else
1283 base = tmp;
1284 break;
1285 case MULT:
1286 indexexp = tmp;
1287 break;
1288 case SYMBOL_REF:
1289 if (flag_pic && ! SYMBOL_REF_LOCAL_P (tmp))
1291 if (base)
1293 if (indexexp)
1294 abort ();
1295 indexexp = base;
1297 base = tmp;
1298 break;
1300 case CONST:
1301 if (flag_pic && GET_CODE (tmp) == CONST)
1303 rtx sym, off, tmp1;
1304 tmp1 = XEXP (tmp,0);
1305 if (GET_CODE (tmp1) != PLUS)
1306 abort ();
1308 sym = XEXP (tmp1,0);
1309 if (GET_CODE (sym) != SYMBOL_REF)
1311 off = sym;
1312 sym = XEXP (tmp1,1);
1314 else
1315 off = XEXP (tmp1,1);
1316 if (GET_CODE (sym) == SYMBOL_REF)
1318 if (GET_CODE (off) != CONST_INT)
1319 abort ();
1321 if (! SYMBOL_REF_LOCAL_P (sym))
1323 if (base)
1325 if (indexexp)
1326 abort ();
1328 indexexp = base;
1331 if (offset != 0)
1332 abort ();
1334 base = sym;
1335 offset = off;
1336 break;
1340 case CONST_INT:
1341 case LABEL_REF:
1342 if (offset)
1343 offset = gen_rtx_PLUS (SImode, tmp, offset);
1344 else
1345 offset = tmp;
1346 break;
1347 default:
1348 abort ();
1351 if (! offset)
1352 offset = const0_rtx;
1354 if (base
1355 #ifndef INDEX_RATHER_THAN_BASE
1356 && (flag_pic || TARGET_HIMEM)
1357 && GET_CODE (base) != SYMBOL_REF
1358 && GET_CODE (offset) != CONST_INT
1359 #else
1360 /* This is a re-implementation of the SEQUENT_ADDRESS_BUG fix. */
1361 #endif
1362 && !indexexp && GET_CODE (base) == REG
1363 && REG_OK_FOR_INDEX_P (base))
1365 indexexp = base;
1366 base = NULL;
1369 /* now, offset, base and indexexp are set */
1370 #ifndef BASE_REG_NEEDED
1371 if (! base)
1373 #if defined (PC_RELATIVE) || defined (NO_ABSOLUTE_PREFIX_IF_SYMBOLIC)
1374 if (GET_CODE (offset) == CONST_INT)
1375 #endif
1376 PUT_ABSOLUTE_PREFIX (file);
1378 #endif
1380 output_addr_const (file, offset);
1381 if (base) /* base can be (REG ...) or (MEM ...) */
1382 switch (GET_CODE (base))
1384 /* now we must output base. Possible alternatives are:
1385 (rN) (REG ...)
1386 (sp) (REG ...)
1387 (fp) (REG ...)
1388 (pc) (REG ...) used for SYMBOL_REF and LABEL_REF, output
1389 (disp(fp)) (MEM ...) just before possible [rX:y]
1390 (disp(sp)) (MEM ...)
1391 (disp(sb)) (MEM ...)
1393 case REG:
1394 fprintf (file, "(%s)", ns32k_out_reg_names[REGNO (base)]);
1395 break;
1396 case SYMBOL_REF:
1397 if (! flag_pic)
1398 abort ();
1400 fprintf (file, "(");
1401 output_addr_const (file, base);
1402 fprintf (file, "(sb))");
1403 break;
1404 case MEM:
1405 addr = XEXP (base,0);
1406 base = NULL;
1407 offset = NULL;
1408 while (addr != NULL)
1410 if (GET_CODE (addr) == PLUS)
1412 if (GET_CODE (XEXP (addr, 0)) == PLUS)
1414 tmp = XEXP (addr, 1);
1415 addr = XEXP (addr, 0);
1417 else
1419 tmp = XEXP (addr, 0);
1420 addr = XEXP (addr, 1);
1423 else
1425 tmp = addr;
1426 addr = NULL;
1428 switch (GET_CODE (tmp))
1430 case REG:
1431 base = tmp;
1432 break;
1433 case CONST:
1434 case CONST_INT:
1435 case SYMBOL_REF:
1436 case LABEL_REF:
1437 if (offset)
1438 offset = gen_rtx_PLUS (SImode, tmp, offset);
1439 else
1440 offset = tmp;
1441 break;
1442 default:
1443 abort ();
1446 if (! offset)
1447 offset = const0_rtx;
1448 fprintf (file, "(");
1449 output_addr_const (file, offset);
1450 if (base)
1451 fprintf (file, "(%s)", ns32k_out_reg_names[REGNO (base)]);
1452 else if (TARGET_SB)
1453 fprintf (file, "(sb)");
1454 else
1455 abort ();
1456 fprintf (file, ")");
1457 break;
1458 default:
1459 abort ();
1461 #ifdef PC_RELATIVE
1462 else if (GET_CODE (offset) != CONST_INT)
1463 fprintf (file, "(pc)");
1464 #ifdef BASE_REG_NEEDED
1465 else if (TARGET_SB)
1466 fprintf (file, "(sb)");
1467 else
1468 abort ();
1469 #endif
1470 #endif /* PC_RELATIVE */
1472 /* now print index if we have one */
1473 if (indexexp)
1475 if (GET_CODE (indexexp) == MULT)
1477 scale = INTVAL (XEXP (indexexp, 1)) >> 1;
1478 indexexp = XEXP (indexexp, 0);
1480 else
1481 scale = 0;
1482 if (GET_CODE (indexexp) != REG || REGNO (indexexp) >= F0_REGNUM)
1483 abort ();
1485 #ifdef UTEK_ASM
1486 fprintf (file, "[%c`%s]",
1487 scales[scale],
1488 ns32k_out_reg_names[REGNO (indexexp)]);
1489 #else
1490 fprintf (file, "[%s:%c]",
1491 ns32k_out_reg_names[REGNO (indexexp)],
1492 scales[scale]);
1493 #endif
1497 /* National 32032 shifting is so bad that we can get
1498 better performance in many common cases by using other
1499 techniques. */
1500 const char *
1501 output_shift_insn (rtx *operands)
1503 if (GET_CODE (operands[2]) == CONST_INT
1504 && INTVAL (operands[2]) > 0
1505 && INTVAL (operands[2]) <= 3)
1507 if (GET_CODE (operands[0]) == REG)
1509 if (GET_CODE (operands[1]) == REG)
1511 if (REGNO (operands[0]) == REGNO (operands[1]))
1513 if (operands[2] == const1_rtx)
1514 return "addd %0,%0";
1515 else if (INTVAL (operands[2]) == 2)
1516 return "addd %0,%0\n\taddd %0,%0";
1518 if (operands[2] == const1_rtx)
1519 return "movd %1,%0\n\taddd %0,%0";
1521 operands[1] = gen_indexed_expr (const0_rtx, operands[1], operands[2]);
1522 return "addr %a1,%0";
1524 if (operands[2] == const1_rtx)
1525 return "movd %1,%0\n\taddd %0,%0";
1527 else if (GET_CODE (operands[1]) == REG)
1529 operands[1] = gen_indexed_expr (const0_rtx, operands[1], operands[2]);
1530 return "addr %a1,%0";
1532 else if (INTVAL (operands[2]) == 1
1533 && GET_CODE (operands[1]) == MEM
1534 && rtx_equal_p (operands [0], operands[1]))
1536 rtx temp = XEXP (operands[1], 0);
1538 if (GET_CODE (temp) == REG
1539 || (GET_CODE (temp) == PLUS
1540 && GET_CODE (XEXP (temp, 0)) == REG
1541 && GET_CODE (XEXP (temp, 1)) == CONST_INT))
1542 return "addd %0,%0";
1544 else return "ashd %2,%0";
1546 return "ashd %2,%0";
1549 const char *
1550 output_move_dconst (int n, const char *s)
1552 static char r[32];
1554 if (n > -9 && n < 8)
1555 strcpy (r, "movqd ");
1556 else if (n > 0 && n < 256)
1557 strcpy (r, "movzbd ");
1558 else if (n > 0 && n < 65536)
1559 strcpy (r, "movzwd ");
1560 else if (n < 0 && n > -129)
1561 strcpy (r, "movxbd ");
1562 else if (n < 0 && n > -32769)
1563 strcpy (r, "movxwd ");
1564 else
1565 strcpy (r, "movd ");
1566 strcat (r, s);
1567 return r;
1570 static rtx
1571 ns32k_struct_value_rtx (tree fntype ATTRIBUTE_UNUSED,
1572 int incoming ATTRIBUTE_UNUSED)
1574 return gen_rtx_REG (Pmode, NS32K_STRUCT_VALUE_REGNUM);
1577 /* Worker function for NOTICE_UPDATE_CC. */
1579 void
1580 ns32k_notice_update_cc (rtx exp, rtx insn ATTRIBUTE_UNUSED)
1582 if (GET_CODE (exp) == SET)
1584 if (GET_CODE (SET_DEST (exp)) == CC0)
1586 cc_status.flags = 0;
1587 cc_status.value1 = SET_DEST (exp);
1588 cc_status.value2 = SET_SRC (exp);
1590 else if (GET_CODE (SET_SRC (exp)) == CALL)
1592 CC_STATUS_INIT;
1594 else if (GET_CODE (SET_DEST (exp)) == REG)
1596 if (cc_status.value1
1597 && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value1))
1598 cc_status.value1 = 0;
1599 if (cc_status.value2
1600 && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value2))
1601 cc_status.value2 = 0;
1603 else if (GET_CODE (SET_DEST (exp)) == MEM)
1605 CC_STATUS_INIT;
1608 else if (GET_CODE (exp) == PARALLEL
1609 && GET_CODE (XVECEXP (exp, 0, 0)) == SET)
1611 if (GET_CODE (SET_DEST (XVECEXP (exp, 0, 0))) == CC0)
1613 cc_status.flags = 0;
1614 cc_status.value1 = SET_DEST (XVECEXP (exp, 0, 0));
1615 cc_status.value2 = SET_SRC (XVECEXP (exp, 0, 0));
1617 else if (GET_CODE (SET_DEST (XVECEXP (exp, 0, 0))) == REG)
1619 if (cc_status.value1
1620 && reg_overlap_mentioned_p (SET_DEST (XVECEXP (exp, 0, 0)),
1621 cc_status.value1))
1622 cc_status.value1 = 0;
1623 if (cc_status.value2
1624 && reg_overlap_mentioned_p (SET_DEST (XVECEXP (exp, 0, 0)),
1625 cc_status.value2))
1626 cc_status.value2 = 0;
1628 else if (GET_CODE (SET_DEST (XVECEXP (exp, 0, 0))) == MEM)
1630 CC_STATUS_INIT;
1633 else if (GET_CODE (exp) == CALL)
1635 /* all bets are off */
1636 CC_STATUS_INIT;
1638 else
1640 /* nothing happens? CC_STATUS_INIT; */
1642 if (cc_status.value1 && GET_CODE (cc_status.value1) == REG
1643 && cc_status.value2
1644 && reg_overlap_mentioned_p (cc_status.value1, cc_status.value2))
1645 abort ();
1648 /* Implement TARGET_ARG_PARTIAL_BYTES. */
1650 static int
1651 ns32k_arg_partial_bytes (CUMULATIVE_ARGS *pcum, enum machine_mode mode,
1652 tree type, bool named ATTRIBUTE_UNUSED)
1654 int cum = *pcum;
1656 if (TARGET_REGPARM && cum < 8)
1658 HOST_WIDE_INT size;
1660 if (mode == BLKmode)
1661 size = int_size_in_bytes (type);
1662 else
1663 size = GET_MODE_SIZE (mode);
1665 if (8 < cum + size)
1666 return 8 - cum;
1669 return 0;