]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/config/ns32k/ns32k.c
builtins.c: Rename movstr*, except for movstrict*, to movmem* and clrstr* to clrmem*.
[thirdparty/gcc.git] / gcc / config / ns32k / ns32k.c
CommitLineData
2861fe6d 1/* Subroutines for assembler code output on the NS32000.
2001a5cf 2 Copyright (C) 1988, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2004
4592bdcb 3 Free Software Foundation, Inc.
2861fe6d 4
7ec022b2 5This file is part of GCC.
2861fe6d 6
7ec022b2 7GCC is free software; you can redistribute it and/or modify
2861fe6d
RS
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 2, or (at your option)
10any later version.
11
7ec022b2 12GCC is distributed in the hope that it will be useful,
2861fe6d
RS
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
7ec022b2 18along with GCC; see the file COPYING. If not, write to
0e29e3c9
RK
19the Free Software Foundation, 59 Temple Place - Suite 330,
20Boston, MA 02111-1307, USA. */
2861fe6d 21
2861fe6d 22#include "config.h"
83575957 23#include "system.h"
4977bab6
ZW
24#include "coretypes.h"
25#include "tm.h"
2861fe6d
RS
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"
2861fe6d
RS
32#include "output.h"
33#include "insn-attr.h"
83575957 34#include "tree.h"
bf6bb899 35#include "function.h"
83575957
ID
36#include "expr.h"
37#include "flags.h"
fabf04b6
KG
38#include "recog.h"
39#include "tm_p.h"
672a6f42
NB
40#include "target.h"
41#include "target-def.h"
91d231cb 42#include "toplev.h"
2861fe6d
RS
43
44#ifdef OSF_OS
45int ns32k_num_files = 0;
46#endif
47
fabf04b6 48/* This duplicates reg_class_contents in reg_class.c, but maybe that isn't
83575957
ID
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 */
51
0139adca 52const unsigned int ns32k_reg_class_contents[N_REG_CLASSES][1] = REG_CLASS_CONTENTS;
83575957 53
0139adca 54const enum reg_class regclass_map[FIRST_PSEUDO_REGISTER] =
83575957
ID
55{
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,
8056c5f2
ID
60 LONG_REGS, LONG_REGS, LONG_REGS, LONG_REGS,
61 LONG_REGS, LONG_REGS, LONG_REGS, LONG_REGS,
83575957
ID
62 FRAME_POINTER_REG, STACK_POINTER_REG
63};
64
83182544 65static const char *const ns32k_out_reg_names[] = OUTPUT_REGISTER_NAMES;
2861fe6d 66
7f682641
SB
67static rtx gen_indexed_expr (rtx, rtx, rtx);
68static const char *singlemove_string (rtx *);
69static void move_tail (rtx[], int, int);
70static tree ns32k_handle_fntype_attribute (tree *, tree, tree, int, bool *);
91d231cb 71const struct attribute_spec ns32k_attribute_table[];
7f682641
SB
72static void ns32k_output_function_prologue (FILE *, HOST_WIDE_INT);
73static void ns32k_output_function_epilogue (FILE *, HOST_WIDE_INT);
74static bool ns32k_rtx_costs (rtx, int, int, int *);
75static int ns32k_address_cost (rtx);
2001a5cf 76static rtx ns32k_struct_value_rtx (tree, int);
672a6f42
NB
77\f
78/* Initialize the GCC target structure. */
91d231cb
JM
79#undef TARGET_ATTRIBUTE_TABLE
80#define TARGET_ATTRIBUTE_TABLE ns32k_attribute_table
2861fe6d 81
301d03af
RS
82#undef TARGET_ASM_ALIGNED_HI_OP
83#define TARGET_ASM_ALIGNED_HI_OP "\t.word\t"
84
85#ifdef ENCORE_ASM
86#undef TARGET_ASM_ALIGNED_SI_OP
87#define TARGET_ASM_ALIGNED_SI_OP "\t.double\t"
88#endif
89
08c148a8
NB
90#undef TARGET_ASM_FUNCTION_PROLOGUE
91#define TARGET_ASM_FUNCTION_PROLOGUE ns32k_output_function_prologue
92#undef TARGET_ASM_FUNCTION_EPILOGUE
93#define TARGET_ASM_FUNCTION_EPILOGUE ns32k_output_function_epilogue
94
3c50106f
RH
95#undef TARGET_RTX_COSTS
96#define TARGET_RTX_COSTS ns32k_rtx_costs
dcefdf67
RH
97#undef TARGET_ADDRESS_COST
98#define TARGET_ADDRESS_COST ns32k_address_cost
3c50106f 99
2001a5cf
KH
100#undef TARGET_STRUCT_VALUE_RTX
101#define TARGET_STRUCT_VALUE_RTX ns32k_struct_value_rtx
102
1bc7c5b6
ZW
103#undef TARGET_ASM_FILE_START_APP_OFF
104#define TARGET_ASM_FILE_START_APP_OFF true
105
f6897b10 106struct gcc_target targetm = TARGET_INITIALIZER;
672a6f42 107\f
08c148a8
NB
108/* Generate the assembly code for function entry. FILE is a stdio
109 stream to output the code to. SIZE is an int: how many units of
110 temporary storage to allocate.
111
112 Refer to the array `regs_ever_live' to determine which registers to
113 save; `regs_ever_live[I]' is nonzero if register number I is ever
114 used in the function. This function is responsible for knowing
115 which registers should not be saved even if used. */
116
117/*
118 * The function prologue for the ns32k is fairly simple.
119 * If a frame pointer is needed (decided in reload.c ?) then
120 * we need assembler of the form
121 *
122 * # Save the oldframe pointer, set the new frame pointer, make space
123 * # on the stack and save any general purpose registers necessary
124 *
125 * enter [<general purpose regs to save>], <local stack space>
126 *
127 * movf fn, tos # Save any floating point registers necessary
128 * .
129 * .
130 *
131 * If a frame pointer is not needed we need assembler of the form
132 *
133 * # Make space on the stack
134 *
135 * adjspd <local stack space + 4>
136 *
137 * # Save any general purpose registers necessary
138 *
139 * save [<general purpose regs to save>]
140 *
141 * movf fn, tos # Save any floating point registers necessary
142 * .
143 * .
144 */
145
146#if !defined (MERLIN_TARGET) && !defined (UTEK_ASM)
147
148#if defined(IMMEDIATE_PREFIX) && IMMEDIATE_PREFIX
149#define ADJSP(FILE, N) \
8745765a 150 fprintf (FILE, "\tadjspd %c" HOST_WIDE_INT_PRINT_DEC "\n", IMMEDIATE_PREFIX, (N))
08c148a8
NB
151#else
152#define ADJSP(FILE, N) \
8745765a 153 fprintf (FILE, "\tadjspd " HOST_WIDE_INT_PRINT_DEC "\n", (N))
08c148a8
NB
154#endif
155
156static void
bfb26edd 157ns32k_output_function_prologue (FILE *file, HOST_WIDE_INT size)
08c148a8
NB
158{
159 register int regno, g_regs_used = 0;
160 int used_regs_buf[8], *bufp = used_regs_buf;
161 int used_fregs_buf[17], *fbufp = used_fregs_buf;
08c148a8
NB
162
163 for (regno = R0_REGNUM; regno < F0_REGNUM; regno++)
164 if (regs_ever_live[regno]
165 && ! call_used_regs[regno])
166 {
167 *bufp++ = regno; g_regs_used++;
168 }
169 *bufp = -1;
170
171 for (; regno < FRAME_POINTER_REGNUM; regno++)
172 if (regs_ever_live[regno] && !call_used_regs[regno])
173 {
174 *fbufp++ = regno;
175 }
176 *fbufp = -1;
177
178 bufp = used_regs_buf;
179 if (frame_pointer_needed)
180 fprintf (file, "\tenter [");
181 else
182 {
183 if (size)
184 ADJSP (file, size + 4);
185 if (g_regs_used && g_regs_used > 4)
186 fprintf (file, "\tsave [");
187 else
188 {
189 while (*bufp >= 0)
190 fprintf (file, "\tmovd r%d,tos\n", *bufp++);
191 g_regs_used = 0;
192 }
193 }
194
195 while (*bufp >= 0)
196 {
197 fprintf (file, "r%d", *bufp++);
198 if (*bufp >= 0)
199 fputc (',', file);
200 }
201
202 if (frame_pointer_needed)
8745765a 203 fprintf (file, "]," HOST_WIDE_INT_PRINT_DEC "\n", size);
08c148a8
NB
204 else if (g_regs_used)
205 fprintf (file, "]\n");
206
207 fbufp = used_fregs_buf;
208 while (*fbufp >= 0)
209 {
210 if ((*fbufp & 1) || (fbufp[0] != fbufp[1] - 1))
211 fprintf (file, "\tmovf %s,tos\n", ns32k_out_reg_names[*fbufp++]);
212 else
213 {
214 fprintf (file, "\tmovl %s,tos\n",
215 ns32k_out_reg_names[fbufp[0]]);
216 fbufp += 2;
217 }
218 }
219
220 if (flag_pic && current_function_uses_pic_offset_table)
221 {
222 fprintf (file, "\tsprd sb,tos\n");
223 if (TARGET_REGPARM)
224 {
225 fprintf (file, "\taddr __GLOBAL_OFFSET_TABLE_(pc),tos\n");
226 fprintf (file, "\tlprd sb,tos\n");
227 }
228 else
229 {
230 fprintf (file, "\taddr __GLOBAL_OFFSET_TABLE_(pc),r0\n");
231 fprintf (file, "\tlprd sb,r0\n");
232 }
233 }
234}
235
236#else /* MERLIN_TARGET || UTEK_ASM */
237
238/* This differs from the standard one above in printing a bitmask
239 rather than a register list in the enter or save instruction. */
240
241static void
242ns32k_output_function_prologue (file, size)
243 FILE *file;
244 HOST_WIDE_INT size;
245{
246 register int regno, g_regs_used = 0;
247 int used_regs_buf[8], *bufp = used_regs_buf;
248 int used_fregs_buf[8], *fbufp = used_fregs_buf;
08c148a8
NB
249
250 for (regno = 0; regno < 8; regno++)
251 if (regs_ever_live[regno]
252 && ! call_used_regs[regno])
253 {
254 *bufp++ = regno; g_regs_used++;
255 }
256 *bufp = -1;
257
258 for (; regno < 16; regno++)
259 if (regs_ever_live[regno] && !call_used_regs[regno]) {
260 *fbufp++ = regno;
261 }
262 *fbufp = -1;
263
264 bufp = used_regs_buf;
265 if (frame_pointer_needed)
266 fprintf (file, "\tenter ");
267 else if (g_regs_used)
268 fprintf (file, "\tsave ");
269
270 if (frame_pointer_needed || g_regs_used)
271 {
272 char mask = 0;
273 while (*bufp >= 0)
274 mask |= 1 << *bufp++;
275 fprintf (file, "$0x%x", (int) mask & 0xff);
276 }
277
278 if (frame_pointer_needed)
279#ifdef UTEK_ASM
280 fprintf (file, ",$%d\n", size);
281#else
282 fprintf (file, ",%d\n", size);
283#endif
284 else if (g_regs_used)
285 fprintf (file, "\n");
286
287 fbufp = used_fregs_buf;
288 while (*fbufp >= 0)
289 {
290 if ((*fbufp & 1) || (fbufp[0] != fbufp[1] - 1))
291 fprintf (file, "\tmovf f%d,tos\n", *fbufp++ - 8);
292 else
293 {
294 fprintf (file, "\tmovl f%d,tos\n", fbufp[0] - 8);
295 fbufp += 2;
296 }
297 }
298}
299
300#endif /* MERLIN_TARGET || UTEK_ASM */
301
302/* This function generates the assembly code for function exit,
303 on machines that need it.
304
305 The function epilogue should not depend on the current stack pointer,
306 if EXIT_IGNORE_STACK is nonzero. That doesn't apply here.
307
308 If a frame pointer is needed (decided in reload.c ?) then
309 we need assembler of the form
310
311 movf tos, fn # Restore any saved floating point registers
312 .
313 .
314
315 # Restore any saved general purpose registers, restore the stack
316 # pointer from the frame pointer, restore the old frame pointer.
317 exit [<general purpose regs to save>]
318
319 If a frame pointer is not needed we need assembler of the form
320 # Restore any general purpose registers saved
321
322 movf tos, fn # Restore any saved floating point registers
323 .
324 .
325 .
326 restore [<general purpose regs to save>]
327
328 # reclaim space allocated on stack
329
330 adjspd <-(local stack space + 4)> */
331
332#if !defined (MERLIN_TARGET) && !defined (UTEK_ASM)
333
334static void
bfb26edd 335ns32k_output_function_epilogue (FILE *file, HOST_WIDE_INT size)
08c148a8
NB
336{
337 register int regno, g_regs_used = 0, f_regs_used = 0;
338 int used_regs_buf[8], *bufp = used_regs_buf;
339 int used_fregs_buf[17], *fbufp = used_fregs_buf;
08c148a8
NB
340
341 if (flag_pic && current_function_uses_pic_offset_table)
342 fprintf (file, "\tlprd sb,tos\n");
343
344 *fbufp++ = -2;
345 for (regno = F0_REGNUM; regno < FRAME_POINTER_REGNUM; regno++)
346 if (regs_ever_live[regno] && !call_used_regs[regno])
347 {
348 *fbufp++ = regno; f_regs_used++;
349 }
350 fbufp--;
351
352 for (regno = 0; regno < F0_REGNUM; regno++)
353 if (regs_ever_live[regno]
354 && ! call_used_regs[regno])
355 {
356 *bufp++ = regno; g_regs_used++;
357 }
358
359 while (fbufp > used_fregs_buf)
360 {
361 if ((*fbufp & 1) && fbufp[0] == fbufp[-1] + 1)
362 {
363 fprintf (file, "\tmovl tos,%s\n",
364 ns32k_out_reg_names[fbufp[-1]]);
365 fbufp -= 2;
366 }
367 else fprintf (file, "\tmovf tos,%s\n", ns32k_out_reg_names[*fbufp--]);
368 }
369
370 if (frame_pointer_needed)
371 fprintf (file, "\texit [");
372 else
373 {
374 if (g_regs_used && g_regs_used > 4)
375 fprintf (file, "\trestore [");
376 else
377 {
378 while (bufp > used_regs_buf)
379 fprintf (file, "\tmovd tos,r%d\n", *--bufp);
380 g_regs_used = 0;
381 }
382 }
383
384 while (bufp > used_regs_buf)
385 {
386 fprintf (file, "r%d", *--bufp);
387 if (bufp > used_regs_buf)
388 fputc (',', file);
389 }
390
391 if (g_regs_used || frame_pointer_needed)
392 fprintf (file, "]\n");
393
394 if (size && !frame_pointer_needed)
395 ADJSP (file, -(size + 4));
396
397 if (current_function_pops_args)
398 fprintf (file, "\tret %d\n", current_function_pops_args);
399 else
400 fprintf (file, "\tret 0\n");
401}
402
403#else /* MERLIN_TARGET || UTEK_ASM */
404
405/* This differs from the standard one above in printing a bitmask
406 rather than a register list in the exit or restore instruction. */
407
408static void
409ns32k_output_function_epilogue (file, size)
410 FILE *file;
411 HOST_WIDE_INT size ATTRIBUTE_UNUSED;
412{
413 register int regno, g_regs_used = 0, f_regs_used = 0;
414 int used_regs_buf[8], *bufp = used_regs_buf;
415 int used_fregs_buf[8], *fbufp = used_fregs_buf;
08c148a8
NB
416
417 *fbufp++ = -2;
418 for (regno = 8; regno < 16; regno++)
419 if (regs_ever_live[regno] && !call_used_regs[regno]) {
420 *fbufp++ = regno; f_regs_used++;
421 }
422 fbufp--;
423
424 for (regno = 0; regno < 8; regno++)
425 if (regs_ever_live[regno]
426 && ! call_used_regs[regno])
427 {
428 *bufp++ = regno; g_regs_used++;
429 }
430
431 while (fbufp > used_fregs_buf)
432 {
433 if ((*fbufp & 1) && fbufp[0] == fbufp[-1] + 1)
434 {
435 fprintf (file, "\tmovl tos,f%d\n", fbufp[-1] - 8);
436 fbufp -= 2;
437 }
438 else fprintf (file, "\tmovf tos,f%d\n", *fbufp-- - 8);
439 }
440
441 if (frame_pointer_needed)
442 fprintf (file, "\texit ");
443 else if (g_regs_used)
444 fprintf (file, "\trestore ");
445
446 if (g_regs_used || frame_pointer_needed)
447 {
448 char mask = 0;
449
450 while (bufp > used_regs_buf)
451 {
452 /* Utek assembler takes care of reversing this */
453 mask |= 1 << *--bufp;
454 }
455 fprintf (file, "$0x%x\n", (int) mask & 0xff);
456 }
457
458#ifdef UTEK_ASM
459 if (current_function_pops_args)
460 fprintf (file, "\tret $%d\n", current_function_pops_args);
461 else
462 fprintf (file, "\tret $0\n");
463#else
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#endif
469}
470
471#endif /* MERLIN_TARGET || UTEK_ASM */
472
83575957 473/* Value is 1 if hard register REGNO can hold a value of machine-mode MODE. */
2861fe6d 474int
bfb26edd 475hard_regno_mode_ok (int regno, enum machine_mode mode)
2861fe6d 476{
4c54e4e4 477 int size = GET_MODE_UNIT_SIZE (mode);
83575957 478
4c54e4e4 479 if (FLOAT_MODE_P (mode))
eb7528db 480 {
83575957 481 if (size == UNITS_PER_WORD && regno < L1_REGNUM)
eb7528db 482 return 1;
83575957
ID
483 if (size == UNITS_PER_WORD * 2
484 && (((regno & 1) == 0 && regno < FRAME_POINTER_REGNUM)))
eb7528db 485 return 1;
83575957 486 return 0;
eb7528db 487 }
83575957
ID
488 if (size == UNITS_PER_WORD * 2
489 && (regno & 1) == 0 && regno < F0_REGNUM)
490 return 1;
491 if (size <= UNITS_PER_WORD
492 && (regno < F0_REGNUM || regno == FRAME_POINTER_REGNUM
493 || regno == STACK_POINTER_REGNUM))
494 return 1;
a9a55844 495 return 0;
2861fe6d
RS
496}
497
3c50106f 498static bool
bfb26edd 499ns32k_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int *total)
3c50106f
RH
500{
501 switch (code)
502 {
503 case CONST_INT:
504 if (INTVAL (x) <= 7 && INTVAL (x) >= -8)
505 *total = 0;
506 else if (INTVAL (x) < 0x2000 && INTVAL (x) >= -0x2000)
507 *total = 1;
508 else
509 *total = 3;
510 return true;
511
512 case CONST:
513 case LABEL_REF:
514 case SYMBOL_REF:
515 *total = 3;
516 return true;
517
518 case CONST_DOUBLE:
519 *total = 5;
520 return true;
521
522 default:
523 return false;
524 }
525}
526
bfb26edd
KH
527int
528register_move_cost (enum reg_class CLASS1, enum reg_class CLASS2)
83575957
ID
529{
530 if (CLASS1 == NO_REGS || CLASS2 == NO_REGS)
531 return 2;
4c54e4e4
ID
532 if ((SUBSET_P (CLASS1, FP_REGS) && !SUBSET_P (CLASS2, FP_REGS))
533 || (!SUBSET_P (CLASS1, FP_REGS) && SUBSET_P (CLASS2, FP_REGS)))
83575957 534 return 8;
4c54e4e4
ID
535 if (((CLASS1) == STACK_POINTER_REG && !SUBSET_P (CLASS2,GENERAL_REGS))
536 || ((CLASS2) == STACK_POINTER_REG && !SUBSET_P (CLASS1,GENERAL_REGS)))
83575957 537 return 6;
4c54e4e4
ID
538 if (((CLASS1) == FRAME_POINTER_REG && !SUBSET_P (CLASS2,GENERAL_REGS))
539 || ((CLASS2) == FRAME_POINTER_REG && !SUBSET_P (CLASS1,GENERAL_REGS)))
83575957
ID
540 return 6;
541 return 2;
542}
543
544#if 0
545/* We made the insn definitions copy from floating point to general
ff482c8d 546 registers via the stack. */
bfb26edd
KH
547int
548secondary_memory_needed (enum reg_class CLASS1,
549 enum reg_class CLASS2,
550 enum machine_mode M)
83575957 551{
4c54e4e4
ID
552 int ret = ((SUBSET_P (CLASS1, FP_REGS) && !SUBSET_P (CLASS2, FP_REGS))
553 || (!SUBSET_P (CLASS1, FP_REGS) && SUBSET_P (CLASS2, FP_REGS)));
83575957
ID
554 return ret;
555}
556#endif
557
558
dcefdf67 559/* TARGET_ADDRESS_COST calls this. This function is not optimal
2861fe6d 560 for the 32032 & 32332, but it probably is better than
ff482c8d 561 the default. */
2861fe6d 562
dcefdf67 563static int
bfb26edd 564ns32k_address_cost (rtx operand)
2861fe6d 565{
2861fe6d 566 int cost = 0;
dcefdf67 567
2861fe6d
RS
568 switch (GET_CODE (operand))
569 {
570 case REG:
4c54e4e4
ID
571 cost += 1;
572 break;
dcefdf67 573
2861fe6d
RS
574 case POST_DEC:
575 case PRE_DEC:
576 break;
dcefdf67 577
4c54e4e4
ID
578 case CONST_INT:
579 if (INTVAL (operand) <= 7 && INTVAL (operand) >= -8)
580 break;
581 if (INTVAL (operand) < 0x2000 && INTVAL (operand) >= -0x2000)
582 {
583 cost +=1;
584 break;
585 }
586 case CONST:
587 case LABEL_REF:
588 case SYMBOL_REF:
589 cost +=3;
590 break;
591 case CONST_DOUBLE:
592 cost += 5;
593 break;
dcefdf67 594
2861fe6d 595 case MEM:
dcefdf67 596 cost += ns32k_address_cost (XEXP (operand, 0)) + 3;
83575957 597 break;
dcefdf67 598
83575957 599 case MULT:
dcefdf67 600 cost += 2;
5efb1046 601 /* FALLTHRU */
2861fe6d 602 case PLUS:
dcefdf67
RH
603 cost += ns32k_address_cost (XEXP (operand, 0));
604 cost += ns32k_address_cost (XEXP (operand, 1));
605 break;
606
2861fe6d
RS
607 default:
608 break;
609 }
dcefdf67 610
2861fe6d
RS
611 return cost;
612}
613
614/* Return the register class of a scratch register needed to copy IN into
615 or out of a register in CLASS in MODE. If it can be done directly,
616 NO_REGS is returned. */
617
618enum reg_class
bfb26edd
KH
619secondary_reload_class (enum reg_class class,
620 enum machine_mode mode ATTRIBUTE_UNUSED,
621 rtx in)
2861fe6d
RS
622{
623 int regno = true_regnum (in);
624
625 if (regno >= FIRST_PSEUDO_REGISTER)
626 regno = -1;
627
83575957
ID
628 if ((class == FRAME_POINTER_REG && regno == STACK_POINTER_REGNUM)
629 || ( class == STACK_POINTER_REG && regno == FRAME_POINTER_REGNUM))
630 return GENERAL_REGS;
631 else
eb7528db 632 return NO_REGS;
2861fe6d 633}
83575957 634
2861fe6d
RS
635/* Generate the rtx that comes from an address expression in the md file */
636/* The expression to be build is BASE[INDEX:SCALE]. To recognize this,
637 scale must be converted from an exponent (from ASHIFT) to a
6d750308 638 multiplier (for MULT). */
4c54e4e4 639
83575957 640static rtx
bfb26edd 641gen_indexed_expr (rtx base, rtx index, rtx scale)
2861fe6d
RS
642{
643 rtx addr;
644
3826a3da 645 /* This generates an invalid addressing mode, if BASE is
2861fe6d
RS
646 fp or sp. This is handled by PRINT_OPERAND_ADDRESS. */
647 if (GET_CODE (base) != REG && GET_CODE (base) != CONST_INT)
c5c76735
JL
648 base = gen_rtx_MEM (SImode, base);
649 addr = gen_rtx_MULT (SImode, index,
650 GEN_INT (1 << INTVAL (scale)));
651 addr = gen_rtx_PLUS (SImode, base, addr);
2861fe6d
RS
652 return addr;
653}
654
2861fe6d 655\f
8bca2922
RK
656/* Split one or more DImode RTL references into pairs of SImode
657 references. The RTL can be REG, offsettable MEM, integer constant, or
658 CONST_DOUBLE. "operands" is a pointer to an array of DImode RTL to
659 split and "num" is its length. lo_half and hi_half are output arrays
660 that parallel "operands". */
661
662void
bfb26edd 663split_di (rtx operands[], int num, rtx lo_half[], rtx hi_half[])
8bca2922
RK
664{
665 while (num--)
666 {
667 if (GET_CODE (operands[num]) == REG)
668 {
c5c76735
JL
669 lo_half[num] = gen_rtx_REG (SImode, REGNO (operands[num]));
670 hi_half[num] = gen_rtx_REG (SImode, REGNO (operands[num]) + 1);
8bca2922
RK
671 }
672 else if (CONSTANT_P (operands[num]))
673 {
674 split_double (operands[num], &lo_half[num], &hi_half[num]);
675 }
676 else if (offsettable_memref_p (operands[num]))
677 {
678 lo_half[num] = operands[num];
b72f00af 679 hi_half[num] = adjust_address (operands[num], SImode, 4);
8bca2922
RK
680 }
681 else
4c54e4e4 682 abort ();
8bca2922
RK
683 }
684}
685\f
2861fe6d
RS
686/* Return the best assembler insn template
687 for moving operands[1] into operands[0] as a fullword. */
688
fabf04b6 689static const char *
bfb26edd 690singlemove_string (rtx *operands)
2861fe6d
RS
691{
692 if (GET_CODE (operands[1]) == CONST_INT
693 && INTVAL (operands[1]) <= 7
694 && INTVAL (operands[1]) >= -8)
695 return "movqd %1,%0";
696 return "movd %1,%0";
697}
698
fabf04b6 699const char *
bfb26edd 700output_move_double (rtx *operands)
2861fe6d 701{
33724a9f 702 enum anon1 { REGOP, OFFSOP, PUSHOP, CNSTOP, RNDOP } optype0, optype1;
2861fe6d
RS
703 rtx latehalf[2];
704
705 /* First classify both operands. */
706
707 if (REG_P (operands[0]))
708 optype0 = REGOP;
709 else if (offsettable_memref_p (operands[0]))
710 optype0 = OFFSOP;
711 else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC)
33724a9f 712 optype0 = PUSHOP;
2861fe6d
RS
713 else
714 optype0 = RNDOP;
715
716 if (REG_P (operands[1]))
717 optype1 = REGOP;
4fa6668b 718 else if (CONSTANT_P (operands[1])
2861fe6d
RS
719 || GET_CODE (operands[1]) == CONST_DOUBLE)
720 optype1 = CNSTOP;
721 else if (offsettable_memref_p (operands[1]))
722 optype1 = OFFSOP;
723 else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC)
33724a9f 724 optype1 = PUSHOP;
2861fe6d
RS
725 else
726 optype1 = RNDOP;
727
728 /* Check for the cases that the operand constraints are not
729 supposed to allow to happen. Abort if we get one,
730 because generating code for these cases is painful. */
731
732 if (optype0 == RNDOP || optype1 == RNDOP)
733 abort ();
734
735 /* Ok, we can do one word at a time.
736 Normally we do the low-numbered word first,
737 but if either operand is autodecrementing then we
738 do the high-numbered word first.
739
740 In either case, set up in LATEHALF the operands to use
741 for the high-numbered word and in some cases alter the
742 operands in OPERANDS to be suitable for the low-numbered word. */
743
744 if (optype0 == REGOP)
c5c76735 745 latehalf[0] = gen_rtx_REG (SImode, REGNO (operands[0]) + 1);
2861fe6d 746 else if (optype0 == OFFSOP)
b72f00af 747 latehalf[0] = adjust_address (operands[0], SImode, 4);
2861fe6d
RS
748 else
749 latehalf[0] = operands[0];
750
751 if (optype1 == REGOP)
c5c76735 752 latehalf[1] = gen_rtx_REG (SImode, REGNO (operands[1]) + 1);
2861fe6d 753 else if (optype1 == OFFSOP)
b72f00af 754 latehalf[1] = adjust_address (operands[1], SImode, 4);
2861fe6d 755 else if (optype1 == CNSTOP)
09dcff63 756 split_double (operands[1], &operands[1], &latehalf[1]);
2861fe6d
RS
757 else
758 latehalf[1] = operands[1];
759
33724a9f
RS
760 /* If insn is effectively movd N(sp),tos then we will do the
761 high word first. We should use the adjusted operand 1 (which is N+4(sp))
762 for the low word as well, to compensate for the first decrement of sp.
763 Given this, it doesn't matter which half we do "first". */
764 if (optype0 == PUSHOP
765 && REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM
766 && reg_overlap_mentioned_p (stack_pointer_rtx, operands[1]))
767 operands[1] = latehalf[1];
768
2861fe6d
RS
769 /* If one or both operands autodecrementing,
770 do the two words, high-numbered first. */
33724a9f 771 else if (optype0 == PUSHOP || optype1 == PUSHOP)
2861fe6d
RS
772 {
773 output_asm_insn (singlemove_string (latehalf), latehalf);
774 return singlemove_string (operands);
775 }
776
5a167d9c
RS
777 /* If the first move would clobber the source of the second one,
778 do them in the other order. */
779
780 /* Overlapping registers. */
781 if (optype0 == REGOP && optype1 == REGOP
df7b8573 782 && REGNO (operands[0]) == REGNO (latehalf[1]))
5a167d9c
RS
783 {
784 /* Do that word. */
785 output_asm_insn (singlemove_string (latehalf), latehalf);
786 /* Do low-numbered word. */
787 return singlemove_string (operands);
788 }
789 /* Loading into a register which overlaps a register used in the address. */
790 else if (optype0 == REGOP && optype1 != REGOP
df7b8573 791 && reg_overlap_mentioned_p (operands[0], operands[1]))
5a167d9c 792 {
df7b8573
RS
793 if (reg_mentioned_p (operands[0], XEXP (operands[1], 0))
794 && reg_mentioned_p (latehalf[0], XEXP (operands[1], 0)))
5a167d9c
RS
795 {
796 /* If both halves of dest are used in the src memory address,
d8e5c946 797 load the destination address into the low reg (operands[0]).
5a167d9c
RS
798 Then it works to load latehalf first. */
799 rtx xops[2];
d8e5c946 800 xops[0] = XEXP (operands[1], 0);
df7b8573 801 xops[1] = operands[0];
d8e5c946 802 output_asm_insn ("addr %a0,%1", xops);
c5c76735 803 operands[1] = gen_rtx_MEM (DImode, operands[0]);
b72f00af 804 latehalf[1] = adjust_address (operands[1], SImode, 4);
f0a5c7b7
RS
805 /* The first half has the overlap, Do the late half first. */
806 output_asm_insn (singlemove_string (latehalf), latehalf);
807 /* Then clobber. */
808 return singlemove_string (operands);
5a167d9c 809 }
df7b8573 810 if (reg_mentioned_p (operands[0], XEXP (operands[1], 0)))
cb410709
RS
811 {
812 /* The first half has the overlap, Do the late half first. */
813 output_asm_insn (singlemove_string (latehalf), latehalf);
814 /* Then clobber. */
815 return singlemove_string (operands);
816 }
5a167d9c
RS
817 }
818
819 /* Normal case. Do the two words, low-numbered first. */
2861fe6d
RS
820
821 output_asm_insn (singlemove_string (operands), operands);
822
823 operands[0] = latehalf[0];
824 operands[1] = latehalf[1];
825 return singlemove_string (operands);
826}
827
83575957
ID
828\f
829#define MAX_UNALIGNED_COPY (32)
830/* Expand string/block move operations.
831
832 operands[0] is the pointer to the destination.
833 operands[1] is the pointer to the source.
834 operands[2] is the number of bytes to move.
835 operands[3] is the alignment. */
836
837static void
bfb26edd 838move_tail (rtx operands[], int bytes, int offset)
2861fe6d 839{
83575957
ID
840 if (bytes & 2)
841 {
f4ef873c
RK
842 emit_move_insn (adjust_address (operands[0], HImode, offset),
843 adjust_address (operands[1], HImode, offset));
83575957
ID
844 offset += 2;
845 }
846 if (bytes & 1)
f4ef873c
RK
847 emit_move_insn (adjust_address (operands[0], QImode, offset),
848 adjust_address (operands[1], QImode, offset));
83575957 849}
2861fe6d 850
83575957 851void
bfb26edd 852expand_block_move (rtx operands[])
83575957
ID
853{
854 rtx bytes_rtx = operands[2];
855 rtx align_rtx = operands[3];
856 int constp = (GET_CODE (bytes_rtx) == CONST_INT);
857 int bytes = (constp ? INTVAL (bytes_rtx) : 0);
858 int align = INTVAL (align_rtx);
4c54e4e4
ID
859 rtx src_reg = gen_rtx_REG (Pmode, 1);
860 rtx dest_reg = gen_rtx_REG (Pmode, 2);
861 rtx count_reg = gen_rtx_REG (SImode, 0);
83575957
ID
862
863 if (constp && bytes <= 0)
864 return;
865
866 if (constp && bytes < 20)
2861fe6d 867 {
83575957 868 int words = bytes >> 2;
f4ef873c 869
83575957 870 if (words)
4c54e4e4 871 {
e95ef187 872 if (words < 3)
4c54e4e4
ID
873 {
874 int offset = 0;
f4ef873c 875
4c54e4e4 876 for (; words; words--, offset += 4)
f4ef873c
RK
877 emit_move_insn (adjust_address (operands[0], SImode, offset),
878 adjust_address (operands[1], SImode, offset));
4c54e4e4
ID
879 }
880 else
881 {
882 /* Use movmd. It is slower than multiple movd's but more
883 compact. It is also slower than movsd for large copies
884 but causes less registers reloading so is better than movsd
ff482c8d 885 for small copies. */
4c54e4e4
ID
886 rtx src, dest;
887 dest = copy_addr_to_reg (XEXP (operands[0], 0));
888 src = copy_addr_to_reg (XEXP (operands[1], 0));
83575957 889
70128ad9 890 emit_insn (gen_movmemsi2(dest, src, GEN_INT (words)));
4c54e4e4
ID
891 }
892 }
893 move_tail (operands, bytes & 3, bytes & ~3);
83575957
ID
894 return;
895 }
896
897 if (align > UNITS_PER_WORD)
898 align = UNITS_PER_WORD;
899
900 /* Move the address into scratch registers. */
4c54e4e4
ID
901 emit_insn (gen_rtx_CLOBBER (VOIDmode, dest_reg));
902 emit_move_insn (dest_reg, XEXP (operands[0], 0));
903 operands[0] = gen_rtx_MEM (SImode, dest_reg);
904 emit_insn (gen_rtx_CLOBBER (VOIDmode, src_reg));
905 emit_move_insn (src_reg, XEXP (operands[1], 0));
906 operands[1] = gen_rtx_MEM (SImode, src_reg);
907 emit_insn (gen_rtx_CLOBBER (VOIDmode, count_reg));
83575957
ID
908
909 if (constp && (align == UNITS_PER_WORD || bytes < MAX_UNALIGNED_COPY))
910 {
83575957
ID
911 /* constant no of bytes and aligned or small enough copy to not bother
912 * aligning. Emit insns to copy by words.
913 */
914 if (bytes >> 2)
915 {
4c54e4e4 916 emit_move_insn (count_reg, GEN_INT (bytes >> 2));
70128ad9 917 emit_insn (gen_movmemsi1 (GEN_INT (4)));
83575957
ID
918 }
919 /* insns to copy rest */
4c54e4e4 920 move_tail (operands, bytes & 3, 0);
83575957
ID
921 }
922 else if (align == UNITS_PER_WORD)
923 {
924 /* insns to copy by words */
a556fd39 925 emit_insn (gen_lshrsi3 (count_reg, bytes_rtx, const2_rtx));
70128ad9 926 emit_insn (gen_movmemsi1 (GEN_INT (4)));
4c54e4e4
ID
927 if (constp)
928 {
929 move_tail (operands, bytes & 3, 0);
930 }
931 else
932 {
933 /* insns to copy rest */
934 emit_insn (gen_andsi3 (count_reg, bytes_rtx, GEN_INT (3)));
70128ad9 935 emit_insn (gen_movmemsi1 (const1_rtx));
4c54e4e4 936 }
83575957
ID
937 }
938 else
939 {
940 /* Not aligned and we may have a lot to copy so it is worth
941 * aligning.
942 */
943 rtx aligned_label = gen_label_rtx ();
944 rtx bytes_reg;
945
4c54e4e4 946 bytes_reg = copy_to_mode_reg (SImode, bytes_rtx);
83575957
ID
947 if (!constp)
948 {
949 /* Emit insns to test and skip over the alignment if it is
950 * not worth it. This doubles as a test to ensure that the alignment
951 * operation can't copy too many bytes
952 */
4c54e4e4 953 emit_insn (gen_cmpsi (bytes_reg, GEN_INT (MAX_UNALIGNED_COPY)));
83575957
ID
954 emit_jump_insn (gen_blt (aligned_label));
955 }
956
957 /* Emit insns to do alignment at run time */
4c54e4e4
ID
958 emit_insn (gen_negsi2 (count_reg, src_reg));
959 emit_insn (gen_andsi3 (count_reg, count_reg, GEN_INT (3)));
960 emit_insn (gen_subsi3 (bytes_reg, bytes_reg, count_reg));
70128ad9 961 emit_insn (gen_movmemsi1 (const1_rtx));
83575957
ID
962 if (!constp)
963 emit_label (aligned_label);
964
965 /* insns to copy by words */
a556fd39 966 emit_insn (gen_lshrsi3 (count_reg, bytes_reg, const2_rtx));
70128ad9 967 emit_insn (gen_movmemsi1 (GEN_INT (4)));
83575957
ID
968
969 /* insns to copy rest */
4c54e4e4 970 emit_insn (gen_andsi3 (count_reg, bytes_reg, GEN_INT (3)));
70128ad9 971 emit_insn (gen_movmemsi1 (const1_rtx));
2861fe6d 972 }
2861fe6d 973}
83575957 974\f
bb4efbc8
RK
975
976/* Returns 1 if OP contains a global symbol reference */
977
978int
bfb26edd 979global_symbolic_reference_mentioned_p (rtx op, int f)
bb4efbc8 980{
6f7d635c 981 register const char *fmt;
bb4efbc8
RK
982 register int i;
983
bc69bfac
RK
984 if (GET_CODE (op) == SYMBOL_REF)
985 {
e95ef187 986 if (! SYMBOL_REF_LOCAL_P (op))
bc69bfac
RK
987 return 1;
988 else
4fa6668b 989 return 0;
bc69bfac 990 }
4fa6668b 991 else if (f && GET_CODE (op) != CONST)
bc69bfac 992 return 0;
bb4efbc8
RK
993
994 fmt = GET_RTX_FORMAT (GET_CODE (op));
995 for (i = GET_RTX_LENGTH (GET_CODE (op)) - 1; i >= 0; i--)
996 {
997 if (fmt[i] == 'E')
998 {
999 register int j;
1000
1001 for (j = XVECLEN (op, i) - 1; j >= 0; j--)
4fa6668b 1002 if (global_symbolic_reference_mentioned_p (XVECEXP (op, i, j), 0))
bb4efbc8
RK
1003 return 1;
1004 }
1005 else if (fmt[i] == 'e'
4fa6668b 1006 && global_symbolic_reference_mentioned_p (XEXP (op, i), 0))
bb4efbc8
RK
1007 return 1;
1008 }
1009
1010 return 0;
1011}
1012
83575957
ID
1013\f
1014/* Returns 1 if OP contains a symbol reference */
1015
1016int
bfb26edd 1017symbolic_reference_mentioned_p (rtx op)
83575957 1018{
6f7d635c 1019 register const char *fmt;
83575957
ID
1020 register int i;
1021
1022 if (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF)
1023 return 1;
1024
1025 fmt = GET_RTX_FORMAT (GET_CODE (op));
1026 for (i = GET_RTX_LENGTH (GET_CODE (op)) - 1; i >= 0; i--)
1027 {
1028 if (fmt[i] == 'E')
1029 {
1030 register int j;
1031
1032 for (j = XVECLEN (op, i) - 1; j >= 0; j--)
1033 if (symbolic_reference_mentioned_p (XVECEXP (op, i, j)))
1034 return 1;
1035 }
1036 else if (fmt[i] == 'e' && symbolic_reference_mentioned_p (XEXP (op, i)))
1037 return 1;
1038 }
1039
1040 return 0;
1041}
1042\f
91d231cb 1043/* Table of machine-specific attributes. */
83575957 1044
91d231cb
JM
1045const struct attribute_spec ns32k_attribute_table[] =
1046{
1047 /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
83575957
ID
1048 /* Stdcall attribute says callee is responsible for popping arguments
1049 if they are not variable. */
91d231cb 1050 { "stdcall", 0, 0, false, true, true, ns32k_handle_fntype_attribute },
83575957 1051 /* Cdecl attribute says the callee is a normal C declaration */
91d231cb
JM
1052 { "cdecl", 0, 0, false, true, true, ns32k_handle_fntype_attribute },
1053 { NULL, 0, 0, false, false, false, NULL }
1054};
83575957 1055
91d231cb
JM
1056/* Handle an attribute requiring a FUNCTION_TYPE, FIELD_DECL or TYPE_DECL;
1057 arguments as in struct attribute_spec.handler. */
1058static tree
bfb26edd
KH
1059ns32k_handle_fntype_attribute (tree *node, tree name,
1060 tree args ATTRIBUTE_UNUSED,
1061 int flags ATTRIBUTE_UNUSED,
1062 bool *no_add_attrs)
91d231cb
JM
1063{
1064 if (TREE_CODE (*node) != FUNCTION_TYPE
1065 && TREE_CODE (*node) != FIELD_DECL
1066 && TREE_CODE (*node) != TYPE_DECL)
1067 {
1068 warning ("`%s' attribute only applies to functions",
1069 IDENTIFIER_POINTER (name));
1070 *no_add_attrs = true;
1071 }
1072
1073 return NULL_TREE;
83575957
ID
1074}
1075
83575957
ID
1076\f
1077/* Value is the number of bytes of arguments automatically
1078 popped when returning from a subroutine call.
1079 FUNDECL is the declaration node of the function (as a tree),
1080 FUNTYPE is the data type of the function (as a tree),
1081 or for a library call it is an identifier node for the subroutine name.
1082 SIZE is the number of bytes of arguments passed on the stack.
1083
1084 On the ns32k, the RET insn may be used to pop them if the number
1085 of args is fixed, but if the number is variable then the caller
1086 must pop them all. RET can't be used for library calls now
1087 because the library is compiled with the Unix compiler.
1088 Use of RET is a selectable option, since it is incompatible with
1089 standard Unix calling sequences. If the option is not selected,
1090 the caller must always pop the args.
1091
1092 The attribute stdcall is equivalent to RET on a per module basis. */
1093
1094int
bfb26edd 1095ns32k_return_pops_args (tree fundecl ATTRIBUTE_UNUSED, tree funtype, int size)
83575957
ID
1096{
1097 int rtd = TARGET_RTD;
1098
1099 if (TREE_CODE (funtype) == IDENTIFIER_NODE)
1100 return rtd ? size : 0;
1101
1102 /* Cdecl functions override -mrtd, and never pop the stack */
1103 if (lookup_attribute ("cdecl", TYPE_ATTRIBUTES (funtype)))
1104 return 0;
1105
1106 /* Stdcall functions will pop the stack if not variable args */
1107 if (lookup_attribute ("stdcall", TYPE_ATTRIBUTES (funtype)))
1108 rtd = 1;
1109
1110 if (rtd)
1111 {
1112 if (TYPE_ARG_TYPES (funtype) == NULL_TREE
1113 || (TREE_VALUE (tree_last (TYPE_ARG_TYPES (funtype))) == void_type_node))
1114 return size;
1115 }
1116
1117 return 0;
1118}
2861fe6d
RS
1119\f
1120/* PRINT_OPERAND is defined to call this function,
1121 which is easier to debug than putting all the code in
1122 a macro definition in ns32k.h. */
1123
83575957 1124/* XXX time 12% of cpu time is in fprintf for non optimizing */
2861fe6d 1125void
bfb26edd 1126print_operand (FILE *file, rtx x, int code)
2861fe6d
RS
1127{
1128 if (code == '$')
c8470713 1129 PUT_IMMEDIATE_PREFIX (file);
2861fe6d 1130 else if (code == '?')
c8470713 1131 PUT_EXTERNAL_PREFIX (file);
2861fe6d 1132 else if (GET_CODE (x) == REG)
83575957 1133 fprintf (file, "%s", ns32k_out_reg_names[REGNO (x)]);
2861fe6d 1134 else if (GET_CODE (x) == MEM)
6d750308 1135 {
989b3e72 1136 output_address (XEXP (x, 0));
6d750308 1137 }
311b7cd7 1138 else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) != VOIDmode)
6d750308 1139 {
b216cd4a
ZW
1140 REAL_VALUE_TYPE r;
1141
1142 REAL_VALUE_FROM_CONST_DOUBLE (r, x);
1143 PUT_IMMEDIATE_PREFIX (file);
6d750308
RS
1144 if (GET_MODE (x) == DFmode)
1145 {
6d750308 1146#ifdef SEQUENT_ASM
9ec36da5 1147 /* Sequent likes its floating point constants as integers */
da6eec72
RH
1148 long l[2];
1149 REAL_VALUE_TO_TARGET_DOUBLE (r, l);
b216cd4a 1150 fprintf (file, "0Dx%08x%08x",
da6eec72 1151 l[!WORDS_BIG_ENDIAN], l[WORDS_BIG_ENDIAN]);
6d750308 1152#else
b216cd4a 1153 char s[30];
da6eec72 1154 real_to_decimal (s, &r, sizeof (s), 0, 1);
6d750308 1155#ifdef ENCORE_ASM
b216cd4a 1156 fprintf (file, "0f%s", s);
6d750308 1157#else
b216cd4a 1158 fprintf (file, "0d%s", s);
6d750308
RS
1159#endif
1160#endif
1161 }
1162 else
b216cd4a 1163 {
6d750308 1164#ifdef SEQUENT_ASM
b216cd4a
ZW
1165 long l;
1166 REAL_VALUE_TO_TARGET_SINGLE (r, l);
1167 fprintf (file, "0Fx%08lx", l);
6d750308 1168#else
b216cd4a 1169 char s[30];
da6eec72 1170 real_to_decimal (s, &r, sizeof (s), 0, 1);
b216cd4a 1171 fprintf (file, "0f%s", s);
6d750308
RS
1172#endif
1173 }
1174 }
2861fe6d
RS
1175 else
1176 {
83575957
ID
1177 if (flag_pic
1178 && GET_CODE (x) == CONST
1179 && symbolic_reference_mentioned_p (x))
1180 {
4c54e4e4
ID
1181 fprintf (stderr, "illegal constant for pic-mode: \n");
1182 print_rtl (stderr, x);
1183 fprintf (stderr, "\nGET_CODE (x) == %d, CONST == %d, symbolic_reference_mentioned_p (x) == %d\n",
1184 GET_CODE (x), CONST, symbolic_reference_mentioned_p (x));
83575957
ID
1185 abort ();
1186 }
1187 else if (flag_pic
1188 && (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF))
1189 {
1190 output_addr_const (file, x);
1191 fprintf (file, "(sb)");
1192 }
1193 else
1194 {
1512dbbe 1195#ifdef NO_IMMEDIATE_PREFIX_IF_SYMBOLIC
83575957 1196 if (GET_CODE (x) == CONST_INT)
c8470713 1197#endif
83575957
ID
1198 PUT_IMMEDIATE_PREFIX (file);
1199 output_addr_const (file, x);
1200 }
2861fe6d
RS
1201 }
1202}
1203\f
1204/* PRINT_OPERAND_ADDRESS is defined to call this function,
1205 which is easier to debug than putting all the code in
1206 a macro definition in ns32k.h . */
1207
1208/* Completely rewritten to get this to work with Gas for PC532 Mach.
1209 This function didn't work and I just wasn't able (nor very willing) to
1210 figure out how it worked.
1211 90-11-25 Tatu Yl|nen <ylo@cs.hut.fi> */
1212
83575957 1213void
bfb26edd 1214print_operand_address (register FILE *file, register rtx addr)
2861fe6d 1215{
8b60264b 1216 static const char scales[] = { 'b', 'w', 'd', 0, 'q', };
2861fe6d
RS
1217 rtx offset, base, indexexp, tmp;
1218 int scale;
bb4efbc8 1219 extern int flag_pic;
2861fe6d
RS
1220
1221 if (GET_CODE (addr) == PRE_DEC || GET_CODE (addr) == POST_DEC)
1222 {
1223 fprintf (file, "tos");
1224 return;
1225 }
1226
1227 offset = NULL;
1228 base = NULL;
1229 indexexp = NULL;
1230 while (addr != NULL)
1231 {
1232 if (GET_CODE (addr) == PLUS)
1233 {
1234 if (GET_CODE (XEXP (addr, 0)) == PLUS)
1235 {
1236 tmp = XEXP (addr, 1);
1237 addr = XEXP (addr, 0);
1238 }
1239 else
1240 {
1241 tmp = XEXP (addr,0);
1242 addr = XEXP (addr,1);
1243 }
1244 }
1245 else
1246 {
1247 tmp = addr;
1248 addr = NULL;
1249 }
1250 switch (GET_CODE (tmp))
1251 {
1252 case PLUS:
1253 abort ();
1254 case MEM:
1255 if (base)
1256 {
1257 indexexp = base;
1258 base = tmp;
1259 }
1260 else
1261 base = tmp;
1262 break;
1263 case REG:
83575957 1264 if (REGNO (tmp) < F0_REGNUM)
2861fe6d
RS
1265 if (base)
1266 {
1267 indexexp = tmp;
1268 }
1269 else
1270 base = tmp;
1271 else
1272 if (base)
1273 {
1274 indexexp = base;
1275 base = tmp;
1276 }
1277 else
1278 base = tmp;
1279 break;
1280 case MULT:
1281 indexexp = tmp;
1282 break;
bb4efbc8 1283 case SYMBOL_REF:
e95ef187 1284 if (flag_pic && ! SYMBOL_REF_LOCAL_P (tmp))
bb4efbc8
RK
1285 {
1286 if (base)
1287 {
1288 if (indexexp)
1289 abort ();
1290 indexexp = base;
1291 }
1292 base = tmp;
1293 break;
1294 }
2861fe6d 1295 case CONST:
bb4efbc8
RK
1296 if (flag_pic && GET_CODE (tmp) == CONST)
1297 {
1298 rtx sym, off, tmp1;
1299 tmp1 = XEXP (tmp,0);
1300 if (GET_CODE (tmp1) != PLUS)
989b3e72 1301 abort ();
bb4efbc8
RK
1302
1303 sym = XEXP (tmp1,0);
1304 if (GET_CODE (sym) != SYMBOL_REF)
1305 {
1306 off = sym;
1307 sym = XEXP (tmp1,1);
1308 }
1309 else
1310 off = XEXP (tmp1,1);
1311 if (GET_CODE (sym) == SYMBOL_REF)
1312 {
8c4ff866 1313 if (GET_CODE (off) != CONST_INT)
bb4efbc8
RK
1314 abort ();
1315
e95ef187 1316 if (! SYMBOL_REF_LOCAL_P (sym))
bb4efbc8
RK
1317 {
1318 if (base)
1319 {
1320 if (indexexp)
1321 abort ();
1322
1323 indexexp = base;
1324 }
1325
1326 if (offset != 0)
1327 abort ();
1328
1329 base = sym;
1330 offset = off;
1331 break;
1332 }
1333 }
1334 }
2861fe6d 1335 case CONST_INT:
2861fe6d
RS
1336 case LABEL_REF:
1337 if (offset)
c5c76735 1338 offset = gen_rtx_PLUS (SImode, tmp, offset);
2861fe6d
RS
1339 else
1340 offset = tmp;
1341 break;
1342 default:
1343 abort ();
1344 }
1345 }
1346 if (! offset)
1347 offset = const0_rtx;
79aa5346 1348
4fa6668b
RK
1349 if (base
1350#ifndef INDEX_RATHER_THAN_BASE
989b3e72 1351 && (flag_pic || TARGET_HIMEM)
4fa6668b
RK
1352 && GET_CODE (base) != SYMBOL_REF
1353 && GET_CODE (offset) != CONST_INT
1354#else
79aa5346 1355 /* This is a re-implementation of the SEQUENT_ADDRESS_BUG fix. */
4fa6668b
RK
1356#endif
1357 && !indexexp && GET_CODE (base) == REG
2bb649fe 1358 && REG_OK_FOR_INDEX_P (base))
79aa5346
RS
1359 {
1360 indexexp = base;
4fa6668b 1361 base = NULL;
79aa5346 1362 }
79aa5346 1363
2861fe6d 1364 /* now, offset, base and indexexp are set */
989b3e72 1365#ifndef BASE_REG_NEEDED
2861fe6d 1366 if (! base)
2861fe6d 1367 {
6a8c88c8 1368#if defined (PC_RELATIVE) || defined (NO_ABSOLUTE_PREFIX_IF_SYMBOLIC)
79aa5346 1369 if (GET_CODE (offset) == CONST_INT)
2861fe6d
RS
1370#endif
1371 PUT_ABSOLUTE_PREFIX (file);
2861fe6d 1372 }
989b3e72 1373#endif
2861fe6d 1374
6a8c88c8 1375 output_addr_const (file, offset);
2861fe6d
RS
1376 if (base) /* base can be (REG ...) or (MEM ...) */
1377 switch (GET_CODE (base))
1378 {
1379 /* now we must output base. Possible alternatives are:
1380 (rN) (REG ...)
79aa5346 1381 (sp) (REG ...)
2861fe6d
RS
1382 (fp) (REG ...)
1383 (pc) (REG ...) used for SYMBOL_REF and LABEL_REF, output
1384 (disp(fp)) (MEM ...) just before possible [rX:y]
1385 (disp(sp)) (MEM ...)
1386 (disp(sb)) (MEM ...)
1387 */
1388 case REG:
83575957 1389 fprintf (file, "(%s)", ns32k_out_reg_names[REGNO (base)]);
2861fe6d 1390 break;
bb4efbc8 1391 case SYMBOL_REF:
989b3e72
RK
1392 if (! flag_pic)
1393 abort ();
bb4efbc8
RK
1394
1395 fprintf (file, "(");
1396 output_addr_const (file, base);
1397 fprintf (file, "(sb))");
1398 break;
2861fe6d 1399 case MEM:
4c54e4e4 1400 addr = XEXP (base,0);
2861fe6d
RS
1401 base = NULL;
1402 offset = NULL;
1403 while (addr != NULL)
1404 {
1405 if (GET_CODE (addr) == PLUS)
1406 {
1407 if (GET_CODE (XEXP (addr, 0)) == PLUS)
1408 {
1409 tmp = XEXP (addr, 1);
1410 addr = XEXP (addr, 0);
1411 }
1412 else
1413 {
1414 tmp = XEXP (addr, 0);
1415 addr = XEXP (addr, 1);
1416 }
1417 }
1418 else
1419 {
1420 tmp = addr;
1421 addr = NULL;
1422 }
1423 switch (GET_CODE (tmp))
1424 {
1425 case REG:
1426 base = tmp;
1427 break;
1428 case CONST:
1429 case CONST_INT:
1430 case SYMBOL_REF:
1431 case LABEL_REF:
1432 if (offset)
c5c76735 1433 offset = gen_rtx_PLUS (SImode, tmp, offset);
2861fe6d
RS
1434 else
1435 offset = tmp;
1436 break;
1437 default:
1438 abort ();
1439 }
1440 }
1441 if (! offset)
1442 offset = const0_rtx;
1443 fprintf (file, "(");
1444 output_addr_const (file, offset);
1445 if (base)
83575957 1446 fprintf (file, "(%s)", ns32k_out_reg_names[REGNO (base)]);
6ce47c07
RK
1447 else if (TARGET_SB)
1448 fprintf (file, "(sb)");
1449 else
1450 abort ();
6ce47c07
RK
1451 fprintf (file, ")");
1452 break;
6ce47c07
RK
1453 default:
1454 abort ();
1455 }
1456#ifdef PC_RELATIVE
989b3e72 1457 else if (GET_CODE (offset) != CONST_INT)
6ce47c07 1458 fprintf (file, "(pc)");
6a8c88c8 1459#ifdef BASE_REG_NEEDED
989b3e72
RK
1460 else if (TARGET_SB)
1461 fprintf (file, "(sb)");
1462 else
1463 abort ();
2861fe6d 1464#endif
989b3e72
RK
1465#endif /* PC_RELATIVE */
1466
2861fe6d
RS
1467 /* now print index if we have one */
1468 if (indexexp)
1469 {
1470 if (GET_CODE (indexexp) == MULT)
1471 {
1472 scale = INTVAL (XEXP (indexexp, 1)) >> 1;
1473 indexexp = XEXP (indexexp, 0);
1474 }
1475 else
1476 scale = 0;
83575957 1477 if (GET_CODE (indexexp) != REG || REGNO (indexexp) >= F0_REGNUM)
2861fe6d
RS
1478 abort ();
1479
c8470713
RS
1480#ifdef UTEK_ASM
1481 fprintf (file, "[%c`%s]",
1482 scales[scale],
83575957 1483 ns32k_out_reg_names[REGNO (indexexp)]);
c8470713 1484#else
2861fe6d 1485 fprintf (file, "[%s:%c]",
83575957 1486 ns32k_out_reg_names[REGNO (indexexp)],
2861fe6d 1487 scales[scale]);
c8470713 1488#endif
2861fe6d
RS
1489 }
1490}
1491\f
1492/* National 32032 shifting is so bad that we can get
1493 better performance in many common cases by using other
1494 techniques. */
fabf04b6 1495const char *
bfb26edd 1496output_shift_insn (rtx *operands)
2861fe6d
RS
1497{
1498 if (GET_CODE (operands[2]) == CONST_INT
1499 && INTVAL (operands[2]) > 0
1500 && INTVAL (operands[2]) <= 3)
4c54e4e4
ID
1501 {
1502 if (GET_CODE (operands[0]) == REG)
1503 {
1504 if (GET_CODE (operands[1]) == REG)
1505 {
1506 if (REGNO (operands[0]) == REGNO (operands[1]))
1507 {
1508 if (operands[2] == const1_rtx)
1509 return "addd %0,%0";
1510 else if (INTVAL (operands[2]) == 2)
1511 return "addd %0,%0\n\taddd %0,%0";
1512 }
1513 if (operands[2] == const1_rtx)
1514 return "movd %1,%0\n\taddd %0,%0";
2861fe6d 1515
4c54e4e4
ID
1516 operands[1] = gen_indexed_expr (const0_rtx, operands[1], operands[2]);
1517 return "addr %a1,%0";
1518 }
1519 if (operands[2] == const1_rtx)
1520 return "movd %1,%0\n\taddd %0,%0";
1521 }
1522 else if (GET_CODE (operands[1]) == REG)
1523 {
1524 operands[1] = gen_indexed_expr (const0_rtx, operands[1], operands[2]);
1525 return "addr %a1,%0";
1526 }
1527 else if (INTVAL (operands[2]) == 1
1528 && GET_CODE (operands[1]) == MEM
1529 && rtx_equal_p (operands [0], operands[1]))
1530 {
1531 rtx temp = XEXP (operands[1], 0);
2861fe6d 1532
4c54e4e4
ID
1533 if (GET_CODE (temp) == REG
1534 || (GET_CODE (temp) == PLUS
1535 && GET_CODE (XEXP (temp, 0)) == REG
1536 && GET_CODE (XEXP (temp, 1)) == CONST_INT))
1537 return "addd %0,%0";
1538 }
1539 else return "ashd %2,%0";
1540 }
2861fe6d
RS
1541 return "ashd %2,%0";
1542}
bb4efbc8 1543
fabf04b6 1544const char *
bfb26edd 1545output_move_dconst (int n, const char *s)
bb4efbc8
RK
1546{
1547 static char r[32];
1548
1549 if (n > -9 && n < 8)
1550 strcpy (r, "movqd ");
1551 else if (n > 0 && n < 256)
1552 strcpy (r, "movzbd ");
1553 else if (n > 0 && n < 65536)
1554 strcpy (r, "movzwd ");
51a52b63 1555 else if (n < 0 && n > -129)
bb4efbc8 1556 strcpy (r, "movxbd ");
51a52b63 1557 else if (n < 0 && n > -32769)
bb4efbc8
RK
1558 strcpy (r, "movxwd ");
1559 else
1560 strcpy (r, "movd ");
1561 strcat (r, s);
1562 return r;
1563}
2001a5cf
KH
1564
1565static rtx
1566ns32k_struct_value_rtx (tree fntype ATTRIBUTE_UNUSED,
1567 int incoming ATTRIBUTE_UNUSED)
1568{
1569 return gen_rtx_REG (Pmode, NS32K_STRUCT_VALUE_REGNUM);
1570}
eff3c926
KH
1571
1572/* Worker function for NOTICE_UPDATE_CC. */
1573
1574void
1575ns32k_notice_update_cc (rtx exp, rtx insn ATTRIBUTE_UNUSED)
1576{
1577 if (GET_CODE (exp) == SET)
1578 {
1579 if (GET_CODE (SET_DEST (exp)) == CC0)
1580 {
1581 cc_status.flags = 0;
1582 cc_status.value1 = SET_DEST (exp);
1583 cc_status.value2 = SET_SRC (exp);
1584 }
1585 else if (GET_CODE (SET_SRC (exp)) == CALL)
1586 {
1587 CC_STATUS_INIT;
1588 }
1589 else if (GET_CODE (SET_DEST (exp)) == REG)
1590 {
1591 if (cc_status.value1
1592 && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value1))
1593 cc_status.value1 = 0;
1594 if (cc_status.value2
1595 && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value2))
1596 cc_status.value2 = 0;
1597 }
1598 else if (GET_CODE (SET_DEST (exp)) == MEM)
1599 {
1600 CC_STATUS_INIT;
1601 }
1602 }
1603 else if (GET_CODE (exp) == PARALLEL
1604 && GET_CODE (XVECEXP (exp, 0, 0)) == SET)
1605 {
1606 if (GET_CODE (SET_DEST (XVECEXP (exp, 0, 0))) == CC0)
1607 {
1608 cc_status.flags = 0;
1609 cc_status.value1 = SET_DEST (XVECEXP (exp, 0, 0));
1610 cc_status.value2 = SET_SRC (XVECEXP (exp, 0, 0));
1611 }
1612 else if (GET_CODE (SET_DEST (XVECEXP (exp, 0, 0))) == REG)
1613 {
1614 if (cc_status.value1
1615 && reg_overlap_mentioned_p (SET_DEST (XVECEXP (exp, 0, 0)),
1616 cc_status.value1))
1617 cc_status.value1 = 0;
1618 if (cc_status.value2
1619 && reg_overlap_mentioned_p (SET_DEST (XVECEXP (exp, 0, 0)),
1620 cc_status.value2))
1621 cc_status.value2 = 0;
1622 }
1623 else if (GET_CODE (SET_DEST (XVECEXP (exp, 0, 0))) == MEM)
1624 {
1625 CC_STATUS_INIT;
1626 }
1627 }
1628 else if (GET_CODE (exp) == CALL)
1629 {
1630 /* all bets are off */
1631 CC_STATUS_INIT;
1632 }
1633 else
1634 {
1635 /* nothing happens? CC_STATUS_INIT; */
1636 }
1637 if (cc_status.value1 && GET_CODE (cc_status.value1) == REG
1638 && cc_status.value2
1639 && reg_overlap_mentioned_p (cc_status.value1, cc_status.value2))
1640 abort ();
1641}