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