OSDN Git Service

2008-04-03 Richard Guenther <rguenther@suse.de>
[pf3gnuchains/gcc-fork.git] / gcc / config / score / score7.c
1 /* score7.c for Sunplus S+CORE processor
2    Copyright (C) 2005, 2007 Free Software Foundation, Inc.
3    Contributed by Sunnorth
4
5    This file is part of GCC.
6
7    GCC is free software; you can redistribute it and/or modify it
8    under the terms of the GNU General Public License as published
9    by the Free Software Foundation; either version 3, or (at your
10    option) any later version.
11
12    GCC is distributed in the hope that it will be useful, but WITHOUT
13    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
15    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 COPYING3.  If not see
19    <http://www.gnu.org/licenses/>.  */
20
21 #include "config.h"
22 #include "system.h"
23 #include "coretypes.h"
24 #include "tm.h"
25 #include "rtl.h"
26 #include "regs.h"
27 #include "hard-reg-set.h"
28 #include "real.h"
29 #include "insn-config.h"
30 #include "conditions.h"
31 #include "insn-attr.h"
32 #include "recog.h"
33 #include "toplev.h"
34 #include "output.h"
35 #include "tree.h"
36 #include "function.h"
37 #include "expr.h"
38 #include "optabs.h"
39 #include "flags.h"
40 #include "reload.h"
41 #include "tm_p.h"
42 #include "ggc.h"
43 #include "gstab.h"
44 #include "hashtab.h"
45 #include "debug.h"
46 #include "target.h"
47 #include "target-def.h"
48 #include "integrate.h"
49 #include "langhooks.h"
50 #include "cfglayout.h"
51 #include "score7.h"
52
53 #define BITSET_P(VALUE, BIT)      (((VALUE) & (1L << (BIT))) != 0)
54 #define INS_BUF_SZ                128
55
56 /* Define the information needed to generate branch insns.  This is
57    stored from the compare operation.  */
58 extern rtx cmp_op0, cmp_op1;
59 extern enum reg_class score_char_to_class[256];
60
61 static int score7_sdata_max;
62 static char score7_ins[INS_BUF_SZ + 8];
63
64 /* Return true if SYMBOL is a SYMBOL_REF and OFFSET + SYMBOL points
65    to the same object as SYMBOL.  */
66 static int
67 score7_offset_within_object_p (rtx symbol, HOST_WIDE_INT offset)
68 {
69   if (GET_CODE (symbol) != SYMBOL_REF)
70     return 0;
71
72   if (CONSTANT_POOL_ADDRESS_P (symbol)
73       && offset >= 0
74       && offset < (int)GET_MODE_SIZE (get_pool_mode (symbol)))
75     return 1;
76
77   if (SYMBOL_REF_DECL (symbol) != 0
78       && offset >= 0
79       && offset < int_size_in_bytes (TREE_TYPE (SYMBOL_REF_DECL (symbol))))
80     return 1;
81
82   return 0;
83 }
84
85 /* Split X into a base and a constant offset, storing them in *BASE
86    and *OFFSET respectively.  */
87 static void
88 score7_split_const (rtx x, rtx *base, HOST_WIDE_INT *offset)
89 {
90   *offset = 0;
91
92   if (GET_CODE (x) == CONST)
93     x = XEXP (x, 0);
94
95   if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == CONST_INT)
96     {
97       *offset += INTVAL (XEXP (x, 1));
98       x = XEXP (x, 0);
99     }
100
101   *base = x;
102 }
103
104 /* Classify symbol X, which must be a SYMBOL_REF or a LABEL_REF.  */
105 static enum score_symbol_type
106 score7_classify_symbol (rtx x)
107 {
108   if (GET_CODE (x) == LABEL_REF)
109     return SYMBOL_GENERAL;
110
111   gcc_assert (GET_CODE (x) == SYMBOL_REF);
112
113   if (CONSTANT_POOL_ADDRESS_P (x))
114     {
115       if (GET_MODE_SIZE (get_pool_mode (x)) <= SCORE7_SDATA_MAX)
116         return SYMBOL_SMALL_DATA;
117       return SYMBOL_GENERAL;
118     }
119   if (SYMBOL_REF_SMALL_P (x))
120     return SYMBOL_SMALL_DATA;
121   return SYMBOL_GENERAL;
122 }
123
124 /* Return true if the current function must save REGNO.  */
125 static int
126 score7_save_reg_p (unsigned int regno)
127 {
128   /* Check call-saved registers.  */
129   if (df_regs_ever_live_p (regno) && !call_used_regs[regno])
130     return 1;
131
132   /* We need to save the old frame pointer before setting up a new one.  */
133   if (regno == HARD_FRAME_POINTER_REGNUM && frame_pointer_needed)
134     return 1;
135
136   /* We need to save the incoming return address if it is ever clobbered
137      within the function.  */
138   if (regno == RA_REGNUM && df_regs_ever_live_p (regno))
139     return 1;
140
141   return 0;
142 }
143
144 /* Return one word of double-word value OP, taking into account the fixed
145    endianness of certain registers.  HIGH_P is true to select the high part,
146    false to select the low part.  */
147 static rtx
148 score7_subw (rtx op, int high_p)
149 {
150   unsigned int byte;
151   enum machine_mode mode = GET_MODE (op);
152
153   if (mode == VOIDmode)
154     mode = DImode;
155
156   byte = (TARGET_LITTLE_ENDIAN ? high_p : !high_p) ? UNITS_PER_WORD : 0;
157
158   if (GET_CODE (op) == REG && REGNO (op) == HI_REGNUM)
159     return gen_rtx_REG (SImode, high_p ? HI_REGNUM : LO_REGNUM);
160
161   if (GET_CODE (op) == MEM)
162     return adjust_address (op, SImode, byte);
163
164   return simplify_gen_subreg (SImode, op, mode, byte);
165 }
166
167 static struct score7_frame_info *
168 score7_cached_frame (void)
169 {
170   static struct score7_frame_info _frame_info;
171   return &_frame_info;
172 }
173
174 /* Return the bytes needed to compute the frame pointer from the current
175    stack pointer.  SIZE is the size (in bytes) of the local variables.  */
176 static struct score7_frame_info *
177 score7_compute_frame_size (HOST_WIDE_INT size)
178 {
179   unsigned int regno;
180   struct score7_frame_info *f = score7_cached_frame ();
181
182   memset (f, 0, sizeof (struct score7_frame_info));
183   f->gp_reg_size = 0;
184   f->mask = 0;
185   f->var_size = SCORE7_STACK_ALIGN (size);
186   f->args_size = current_function_outgoing_args_size;
187   f->cprestore_size = flag_pic ? UNITS_PER_WORD : 0;
188   if (f->var_size == 0 && current_function_is_leaf)
189     f->args_size = f->cprestore_size = 0;
190
191   if (f->args_size == 0 && current_function_calls_alloca)
192     f->args_size = UNITS_PER_WORD;
193
194   f->total_size = f->var_size + f->args_size + f->cprestore_size;
195   for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
196     {
197       if (score7_save_reg_p (regno))
198         {
199           f->gp_reg_size += GET_MODE_SIZE (SImode);
200           f->mask |= 1 << (regno - GP_REG_FIRST);
201         }
202     }
203
204   if (current_function_calls_eh_return)
205     {
206       unsigned int i;
207       for (i = 0;; ++i)
208         {
209           regno = EH_RETURN_DATA_REGNO (i);
210           if (regno == INVALID_REGNUM)
211             break;
212           f->gp_reg_size += GET_MODE_SIZE (SImode);
213           f->mask |= 1 << (regno - GP_REG_FIRST);
214         }
215     }
216
217   f->total_size += f->gp_reg_size;
218   f->num_gp = f->gp_reg_size / UNITS_PER_WORD;
219
220   if (f->mask)
221     {
222       HOST_WIDE_INT offset;
223       offset = (f->args_size + f->cprestore_size + f->var_size
224                 + f->gp_reg_size - GET_MODE_SIZE (SImode));
225       f->gp_sp_offset = offset;
226     }
227   else
228     f->gp_sp_offset = 0;
229
230   return f;
231 }
232
233 /* Return true if X is a valid base register for the given mode.
234    Allow only hard registers if STRICT.  */
235 static int
236 score7_valid_base_register_p (rtx x, int strict)
237 {
238   if (!strict && GET_CODE (x) == SUBREG)
239     x = SUBREG_REG (x);
240
241   return (GET_CODE (x) == REG
242           && score7_regno_mode_ok_for_base_p (REGNO (x), strict));
243 }
244
245 /* Return true if X is a valid address for machine mode MODE.  If it is,
246    fill in INFO appropriately.  STRICT is true if we should only accept
247    hard base registers.  */
248 static int
249 score7_classify_address (struct score7_address_info *info,
250                          enum machine_mode mode, rtx x, int strict)
251 {
252   info->code = GET_CODE (x);
253
254   switch (info->code)
255     {
256     case REG:
257     case SUBREG:
258       info->type = SCORE7_ADD_REG;
259       info->reg = x;
260       info->offset = const0_rtx;
261       return score7_valid_base_register_p (info->reg, strict);
262     case PLUS:
263       info->type = SCORE7_ADD_REG;
264       info->reg = XEXP (x, 0);
265       info->offset = XEXP (x, 1);
266       return (score7_valid_base_register_p (info->reg, strict)
267               && GET_CODE (info->offset) == CONST_INT
268               && IMM_IN_RANGE (INTVAL (info->offset), 15, 1));
269     case PRE_DEC:
270     case POST_DEC:
271     case PRE_INC:
272     case POST_INC:
273       if (GET_MODE_SIZE (mode) > GET_MODE_SIZE (SImode))
274         return false;
275       info->type = SCORE7_ADD_REG;
276       info->reg = XEXP (x, 0);
277       info->offset = GEN_INT (GET_MODE_SIZE (mode));
278       return score7_valid_base_register_p (info->reg, strict);
279     case CONST_INT:
280       info->type = SCORE7_ADD_CONST_INT;
281       return IMM_IN_RANGE (INTVAL (x), 15, 1);
282     case CONST:
283     case LABEL_REF:
284     case SYMBOL_REF:
285       info->type = SCORE7_ADD_SYMBOLIC;
286       return (score7_symbolic_constant_p (x, &info->symbol_type)
287               && (info->symbol_type == SYMBOL_GENERAL
288                   || info->symbol_type == SYMBOL_SMALL_DATA));
289     default:
290       return 0;
291     }
292 }
293
294 bool
295 score7_return_in_memory (tree type, tree fndecl ATTRIBUTE_UNUSED)
296 {
297     return ((TYPE_MODE (type) == BLKmode)
298             || (int_size_in_bytes (type) > 2 * UNITS_PER_WORD)
299             || (int_size_in_bytes (type) == -1));
300 }
301
302 /* Return a legitimate address for REG + OFFSET.  */
303 static rtx
304 score7_add_offset (rtx reg, HOST_WIDE_INT offset)
305 {
306   if (!IMM_IN_RANGE (offset, 15, 1))
307     {
308       reg = expand_simple_binop (GET_MODE (reg), PLUS,
309                                  gen_int_mode (offset & 0xffffc000,
310                                                GET_MODE (reg)),
311                                  reg, NULL, 0, OPTAB_WIDEN);
312       offset &= 0x3fff;
313     }
314
315   return plus_constant (reg, offset);
316 }
317
318 /* Implement TARGET_ASM_OUTPUT_MI_THUNK.  Generate rtl rather than asm text
319    in order to avoid duplicating too much logic from elsewhere.  */
320 void
321 score7_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
322                         HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
323                         tree function)
324 {
325   rtx this, temp1, insn, fnaddr;
326
327   /* Pretend to be a post-reload pass while generating rtl.  */
328   reload_completed = 1;
329
330   /* Mark the end of the (empty) prologue.  */
331   emit_note (NOTE_INSN_PROLOGUE_END);
332
333   /* We need two temporary registers in some cases.  */
334   temp1 = gen_rtx_REG (Pmode, 8);
335
336   /* Find out which register contains the "this" pointer.  */
337   if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function))
338     this = gen_rtx_REG (Pmode, ARG_REG_FIRST + 1);
339   else
340     this = gen_rtx_REG (Pmode, ARG_REG_FIRST);
341
342   /* Add DELTA to THIS.  */
343   if (delta != 0)
344     {
345       rtx offset = GEN_INT (delta);
346       if (!CONST_OK_FOR_LETTER_P (delta, 'L'))
347         {
348           emit_move_insn (temp1, offset);
349           offset = temp1;
350         }
351       emit_insn (gen_add3_insn (this, this, offset));
352     }
353
354   /* If needed, add *(*THIS + VCALL_OFFSET) to THIS.  */
355   if (vcall_offset != 0)
356     {
357       rtx addr;
358
359       /* Set TEMP1 to *THIS.  */
360       emit_move_insn (temp1, gen_rtx_MEM (Pmode, this));
361
362       /* Set ADDR to a legitimate address for *THIS + VCALL_OFFSET.  */
363       addr = score7_add_offset (temp1, vcall_offset);
364
365       /* Load the offset and add it to THIS.  */
366       emit_move_insn (temp1, gen_rtx_MEM (Pmode, addr));
367       emit_insn (gen_add3_insn (this, this, temp1));
368     }
369
370   /* Jump to the target function.  */
371   fnaddr = XEXP (DECL_RTL (function), 0);
372   insn = emit_call_insn (gen_sibcall_internal_score7 (fnaddr, const0_rtx));
373   SIBLING_CALL_P (insn) = 1;
374
375   /* Run just enough of rest_of_compilation.  This sequence was
376      "borrowed" from alpha.c.  */
377   insn = get_insns ();
378   insn_locators_alloc ();
379   split_all_insns_noflow ();
380   shorten_branches (insn);
381   final_start_function (insn, file, 1);
382   final (insn, file, 1);
383   final_end_function ();
384
385   /* Clean up the vars set above.  Note that final_end_function resets
386      the global pointer for us.  */
387   reload_completed = 0;
388 }
389
390 /* Copy VALUE to a register and return that register.  If new psuedos
391    are allowed, copy it into a new register, otherwise use DEST.  */
392 static rtx
393 score7_force_temporary (rtx dest, rtx value)
394 {
395   if (can_create_pseudo_p ())
396     return force_reg (Pmode, value);
397   else
398     {
399       emit_move_insn (copy_rtx (dest), value);
400       return dest;
401     }
402 }
403
404 /* Return a LO_SUM expression for ADDR.  TEMP is as for score_force_temporary
405    and is used to load the high part into a register.  */
406 static rtx
407 score7_split_symbol (rtx temp, rtx addr)
408 {
409   rtx high = score7_force_temporary (temp,
410                                      gen_rtx_HIGH (Pmode, copy_rtx (addr)));
411   return gen_rtx_LO_SUM (Pmode, high, addr);
412 }
413
414 /* This function is used to implement LEGITIMIZE_ADDRESS.  If *XLOC can
415    be legitimized in a way that the generic machinery might not expect,
416    put the new address in *XLOC and return true.  */
417 int
418 score7_legitimize_address (rtx *xloc)
419 {
420   enum score_symbol_type symbol_type;
421
422   if (score7_symbolic_constant_p (*xloc, &symbol_type)
423       && symbol_type == SYMBOL_GENERAL)
424     {
425       *xloc = score7_split_symbol (0, *xloc);
426       return 1;
427     }
428
429   if (GET_CODE (*xloc) == PLUS
430       && GET_CODE (XEXP (*xloc, 1)) == CONST_INT)
431     {
432       rtx reg = XEXP (*xloc, 0);
433       if (!score7_valid_base_register_p (reg, 0))
434         reg = copy_to_mode_reg (Pmode, reg);
435       *xloc = score7_add_offset (reg, INTVAL (XEXP (*xloc, 1)));
436       return 1;
437     }
438   return 0;
439 }
440
441 /* Fill INFO with information about a single argument.  CUM is the
442    cumulative state for earlier arguments.  MODE is the mode of this
443    argument and TYPE is its type (if known).  NAMED is true if this
444    is a named (fixed) argument rather than a variable one.  */
445 static void
446 score7_classify_arg (const CUMULATIVE_ARGS *cum, enum machine_mode mode,
447                      tree type, int named, struct score7_arg_info *info)
448 {
449   int even_reg_p;
450   unsigned int num_words, max_regs;
451
452   even_reg_p = 0;
453   if (GET_MODE_CLASS (mode) == MODE_INT
454       || GET_MODE_CLASS (mode) == MODE_FLOAT)
455     even_reg_p = (GET_MODE_SIZE (mode) > UNITS_PER_WORD);
456   else
457     if (type != NULL_TREE && TYPE_ALIGN (type) > BITS_PER_WORD && named)
458       even_reg_p = 1;
459
460   if (TARGET_MUST_PASS_IN_STACK (mode, type))
461     info->reg_offset = ARG_REG_NUM;
462   else
463     {
464       info->reg_offset = cum->num_gprs;
465       if (even_reg_p)
466         info->reg_offset += info->reg_offset & 1;
467     }
468
469   if (mode == BLKmode)
470     info->num_bytes = int_size_in_bytes (type);
471   else
472     info->num_bytes = GET_MODE_SIZE (mode);
473
474   num_words = (info->num_bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
475   max_regs = ARG_REG_NUM - info->reg_offset;
476
477   /* Partition the argument between registers and stack.  */
478   info->reg_words = MIN (num_words, max_regs);
479   info->stack_words = num_words - info->reg_words;
480
481   /* The alignment applied to registers is also applied to stack arguments.  */
482   if (info->stack_words)
483     {
484       info->stack_offset = cum->stack_words;
485       if (even_reg_p)
486         info->stack_offset += info->stack_offset & 1;
487     }
488 }
489
490 /* Set up the stack and frame (if desired) for the function.  */
491 void
492 score7_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
493 {
494   const char *fnname;
495   struct score7_frame_info *f = score7_cached_frame ();
496   HOST_WIDE_INT tsize = f->total_size;
497
498   fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
499   if (!flag_inhibit_size_directive)
500     {
501       fputs ("\t.ent\t", file);
502       assemble_name (file, fnname);
503       fputs ("\n", file);
504     }
505   assemble_name (file, fnname);
506   fputs (":\n", file);
507
508   if (!flag_inhibit_size_directive)
509     {
510       fprintf (file,
511                "\t.frame\t%s," HOST_WIDE_INT_PRINT_DEC ",%s, %d\t\t"
512                "# vars= " HOST_WIDE_INT_PRINT_DEC ", regs= %d"
513                ", args= " HOST_WIDE_INT_PRINT_DEC
514                ", gp= " HOST_WIDE_INT_PRINT_DEC "\n",
515                (reg_names[(frame_pointer_needed)
516                 ? HARD_FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM]),
517                tsize,
518                reg_names[RA_REGNUM],
519                current_function_is_leaf ? 1 : 0,
520                f->var_size,
521                f->num_gp,
522                f->args_size,
523                f->cprestore_size);
524
525       fprintf(file, "\t.mask\t0x%08x," HOST_WIDE_INT_PRINT_DEC "\n",
526               f->mask,
527               (f->gp_sp_offset - f->total_size));
528     }
529 }
530
531 /* Do any necessary cleanup after a function to restore stack, frame,
532    and regs.  */
533 void
534 score7_function_epilogue (FILE *file,
535                           HOST_WIDE_INT size ATTRIBUTE_UNUSED)
536 {
537   if (!flag_inhibit_size_directive)
538     {
539       const char *fnname;
540       fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
541       fputs ("\t.end\t", file);
542       assemble_name (file, fnname);
543       fputs ("\n", file);
544     }
545 }
546
547 /* Returns true if X contains a SYMBOL_REF.  */
548 static bool
549 score7_symbolic_expression_p (rtx x)
550 {
551   if (GET_CODE (x) == SYMBOL_REF)
552     return true;
553
554   if (GET_CODE (x) == CONST)
555     return score7_symbolic_expression_p (XEXP (x, 0));
556
557   if (UNARY_P (x))
558     return score7_symbolic_expression_p (XEXP (x, 0));
559
560   if (ARITHMETIC_P (x))
561     return (score7_symbolic_expression_p (XEXP (x, 0))
562             || score7_symbolic_expression_p (XEXP (x, 1)));
563
564   return false;
565 }
566
567 /* Choose the section to use for the constant rtx expression X that has
568    mode MODE.  */
569 section *
570 score7_select_rtx_section (enum machine_mode mode, rtx x,
571                            unsigned HOST_WIDE_INT align)
572 {
573   if (GET_MODE_SIZE (mode) <= SCORE7_SDATA_MAX)
574     return get_named_section (0, ".sdata", 0);
575   else if (flag_pic && score7_symbolic_expression_p (x))
576     return get_named_section (0, ".data.rel.ro", 3);
577   else
578     return mergeable_constant_section (mode, align, 0);
579 }
580
581 /* Implement TARGET_IN_SMALL_DATA_P.  */
582 bool
583 score7_in_small_data_p (tree decl)
584 {
585   HOST_WIDE_INT size;
586
587   if (TREE_CODE (decl) == STRING_CST
588       || TREE_CODE (decl) == FUNCTION_DECL)
589     return false;
590
591   if (TREE_CODE (decl) == VAR_DECL && DECL_SECTION_NAME (decl) != 0)
592     {
593       const char *name;
594       name = TREE_STRING_POINTER (DECL_SECTION_NAME (decl));
595       if (strcmp (name, ".sdata") != 0
596           && strcmp (name, ".sbss") != 0)
597         return true;
598       if (!DECL_EXTERNAL (decl))
599         return false;
600     }
601   size = int_size_in_bytes (TREE_TYPE (decl));
602   return (size > 0 && size <= SCORE7_SDATA_MAX);
603 }
604
605 /* Implement TARGET_ASM_FILE_START.  */
606 void
607 score7_asm_file_start (void)
608 {
609   default_file_start ();
610   fprintf (asm_out_file, ASM_COMMENT_START
611            "GCC for S+core %s \n", SCORE_GCC_VERSION);
612
613   if (flag_pic)
614     fprintf (asm_out_file, "\t.set pic\n");
615 }
616
617 /* Implement TARGET_ASM_FILE_END.  When using assembler macros, emit
618    .externs for any small-data variables that turned out to be external.  */
619 void
620 score7_asm_file_end (void)
621 {
622   tree name_tree;
623   struct extern_list *p;
624   if (extern_head)
625     {
626       fputs ("\n", asm_out_file);
627       for (p = extern_head; p != 0; p = p->next)
628         {
629           name_tree = get_identifier (p->name);
630           if (!TREE_ASM_WRITTEN (name_tree)
631               && TREE_SYMBOL_REFERENCED (name_tree))
632             {
633               TREE_ASM_WRITTEN (name_tree) = 1;
634               fputs ("\t.extern\t", asm_out_file);
635               assemble_name (asm_out_file, p->name);
636               fprintf (asm_out_file, ", %d\n", p->size);
637             }
638         }
639     }
640 }
641
642 /* Implement OVERRIDE_OPTIONS macro.  */
643 void
644 score7_override_options (void)
645 {
646   flag_pic = false;
647   if (!flag_pic)
648     score7_sdata_max = g_switch_set ? g_switch_value : SCORE7_DEFAULT_SDATA_MAX;
649   else
650     {
651       score7_sdata_max = 0;
652       if (g_switch_set && (g_switch_value != 0))
653         warning (0, "-fPIC and -G are incompatible");
654     }
655
656   score_char_to_class['d'] = G32_REGS;
657   score_char_to_class['e'] = G16_REGS;
658   score_char_to_class['t'] = T32_REGS;
659
660   score_char_to_class['h'] = HI_REG;
661   score_char_to_class['l'] = LO_REG;
662   score_char_to_class['x'] = CE_REGS;
663
664   score_char_to_class['q'] = CN_REG;
665   score_char_to_class['y'] = LC_REG;
666   score_char_to_class['z'] = SC_REG;
667   score_char_to_class['a'] = SP_REGS;
668
669   score_char_to_class['c'] = CR_REGS;
670 }
671
672 /* Implement REGNO_REG_CLASS macro.  */
673 int
674 score7_reg_class (int regno)
675 {
676   int c;
677   gcc_assert (regno >= 0 && regno < FIRST_PSEUDO_REGISTER);
678
679   if (regno == FRAME_POINTER_REGNUM
680       || regno == ARG_POINTER_REGNUM)
681     return ALL_REGS;
682
683   for (c = 0; c < N_REG_CLASSES; c++)
684     if (TEST_HARD_REG_BIT (reg_class_contents[c], regno))
685       return c;
686
687   return NO_REGS;
688 }
689
690 /* Implement PREFERRED_RELOAD_CLASS macro.  */
691 enum reg_class
692 score7_preferred_reload_class (rtx x ATTRIBUTE_UNUSED, enum reg_class class)
693 {
694   if (reg_class_subset_p (G16_REGS, class))
695     return G16_REGS;
696   if (reg_class_subset_p (G32_REGS, class))
697     return G32_REGS;
698   return class;
699 }
700
701 /* Implement SECONDARY_INPUT_RELOAD_CLASS
702    and SECONDARY_OUTPUT_RELOAD_CLASS macro.  */
703 enum reg_class
704 score7_secondary_reload_class (enum reg_class class,
705                                enum machine_mode mode ATTRIBUTE_UNUSED,
706                                rtx x)
707 {
708   int regno = -1;
709   if (GET_CODE (x) == REG || GET_CODE(x) == SUBREG)
710     regno = true_regnum (x);
711
712   if (!GR_REG_CLASS_P (class))
713     return GP_REG_P (regno) ? NO_REGS : G32_REGS;
714   return NO_REGS;
715 }
716
717 /* Implement CONST_OK_FOR_LETTER_P macro.  */
718 /* imm constraints
719    I        imm16 << 16
720    J        uimm5
721    K        uimm16
722    L        simm16
723    M        uimm14
724    N        simm14  */
725 int
726 score7_const_ok_for_letter_p (HOST_WIDE_INT value, char c)
727 {
728   switch (c)
729     {
730     case 'I': return ((value & 0xffff) == 0);
731     case 'J': return IMM_IN_RANGE (value, 5, 0);
732     case 'K': return IMM_IN_RANGE (value, 16, 0);
733     case 'L': return IMM_IN_RANGE (value, 16, 1);
734     case 'M': return IMM_IN_RANGE (value, 14, 0);
735     case 'N': return IMM_IN_RANGE (value, 14, 1);
736     default : return 0;
737     }
738 }
739
740 /* Implement EXTRA_CONSTRAINT macro.  */
741 /* Z        symbol_ref  */
742 int
743 score7_extra_constraint (rtx op, char c)
744 {
745   switch (c)
746     {
747     case 'Z':
748       return GET_CODE (op) == SYMBOL_REF;
749     default:
750       gcc_unreachable ();
751     }
752 }
753
754 /* Return truth value on whether or not a given hard register
755    can support a given mode.  */
756 int
757 score7_hard_regno_mode_ok (unsigned int regno, enum machine_mode mode)
758 {
759   int size = GET_MODE_SIZE (mode);
760   enum mode_class class = GET_MODE_CLASS (mode);
761
762   if (class == MODE_CC)
763     return regno == CC_REGNUM;
764   else if (regno == FRAME_POINTER_REGNUM
765            || regno == ARG_POINTER_REGNUM)
766     return class == MODE_INT;
767   else if (GP_REG_P (regno))
768     /* ((regno <= (GP_REG_LAST- HARD_REGNO_NREGS (dummy, mode)) + 1)  */
769     return !(regno & 1) || (size <= UNITS_PER_WORD);
770   else if (CE_REG_P (regno))
771     return (class == MODE_INT
772             && ((size <= UNITS_PER_WORD)
773                 || (regno == CE_REG_FIRST && size == 2 * UNITS_PER_WORD)));
774   else
775     return (class == MODE_INT) && (size <= UNITS_PER_WORD);
776 }
777
778 /* Implement INITIAL_ELIMINATION_OFFSET.  FROM is either the frame
779    pointer or argument pointer.  TO is either the stack pointer or
780    hard frame pointer.  */
781 HOST_WIDE_INT
782 score7_initial_elimination_offset (int from,
783                                    int to ATTRIBUTE_UNUSED)
784 {
785   struct score7_frame_info *f = score7_compute_frame_size (get_frame_size ());
786   switch (from)
787     {
788     case ARG_POINTER_REGNUM:
789       return f->total_size;
790     case FRAME_POINTER_REGNUM:
791       return 0;
792     default:
793       gcc_unreachable ();
794     }
795 }
796
797 /* Implement FUNCTION_ARG_ADVANCE macro.  */
798 void
799 score7_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
800                              tree type, int named)
801 {
802   struct score7_arg_info info;
803   score7_classify_arg (cum, mode, type, named, &info);
804   cum->num_gprs = info.reg_offset + info.reg_words;
805   if (info.stack_words > 0)
806     cum->stack_words = info.stack_offset + info.stack_words;
807   cum->arg_number++;
808 }
809
810 /* Implement TARGET_ARG_PARTIAL_BYTES macro.  */
811 int
812 score7_arg_partial_bytes (CUMULATIVE_ARGS *cum,
813                           enum machine_mode mode, tree type, bool named)
814 {
815   struct score7_arg_info info;
816   score7_classify_arg (cum, mode, type, named, &info);
817   return info.stack_words > 0 ? info.reg_words * UNITS_PER_WORD : 0;
818 }
819
820 /* Implement FUNCTION_ARG macro.  */
821 rtx
822 score7_function_arg (const CUMULATIVE_ARGS *cum, enum machine_mode mode,
823                      tree type, int named)
824 {
825   struct score7_arg_info info;
826
827   if (mode == VOIDmode || !named)
828     return 0;
829
830   score7_classify_arg (cum, mode, type, named, &info);
831
832   if (info.reg_offset == ARG_REG_NUM)
833     return 0;
834
835   if (!info.stack_words)
836     return gen_rtx_REG (mode, ARG_REG_FIRST + info.reg_offset);
837   else
838     {
839       rtx ret = gen_rtx_PARALLEL (mode, rtvec_alloc (info.reg_words));
840       unsigned int i, part_offset = 0;
841       for (i = 0; i < info.reg_words; i++)
842         {
843           rtx reg;
844           reg = gen_rtx_REG (SImode, ARG_REG_FIRST + info.reg_offset + i);
845           XVECEXP (ret, 0, i) = gen_rtx_EXPR_LIST (SImode, reg,
846                                                    GEN_INT (part_offset));
847           part_offset += UNITS_PER_WORD;
848         }
849       return ret;
850     }
851 }
852
853 /* Implement FUNCTION_VALUE and LIBCALL_VALUE.  For normal calls,
854    VALTYPE is the return type and MODE is VOIDmode.  For libcalls,
855    VALTYPE is null and MODE is the mode of the return value.  */
856 rtx
857 score7_function_value (tree valtype, tree func ATTRIBUTE_UNUSED,
858                        enum machine_mode mode)
859 {
860   if (valtype)
861     {
862       int unsignedp;
863       mode = TYPE_MODE (valtype);
864       unsignedp = TYPE_UNSIGNED (valtype);
865       mode = promote_mode (valtype, mode, &unsignedp, 1);
866     }
867   return gen_rtx_REG (mode, RT_REGNUM);
868 }
869
870 /* Implement INITIALIZE_TRAMPOLINE macro.  */
871 void
872 score7_initialize_trampoline (rtx ADDR, rtx FUNC, rtx CHAIN)
873 {
874 #define FFCACHE          "_flush_cache"
875 #define CODE_SIZE        (TRAMPOLINE_INSNS * UNITS_PER_WORD)
876
877   rtx pfunc, pchain;
878
879   pfunc = plus_constant (ADDR, CODE_SIZE);
880   pchain = plus_constant (ADDR, CODE_SIZE + GET_MODE_SIZE (SImode));
881
882   emit_move_insn (gen_rtx_MEM (SImode, pfunc), FUNC);
883   emit_move_insn (gen_rtx_MEM (SImode, pchain), CHAIN);
884   emit_library_call (gen_rtx_SYMBOL_REF (Pmode, FFCACHE),
885                      0, VOIDmode, 2,
886                      ADDR, Pmode,
887                      GEN_INT (TRAMPOLINE_SIZE), SImode);
888 #undef FFCACHE
889 #undef CODE_SIZE
890 }
891
892 /* This function is used to implement REG_MODE_OK_FOR_BASE_P macro.  */
893 int
894 score7_regno_mode_ok_for_base_p (int regno, int strict)
895 {
896   if (regno >= FIRST_PSEUDO_REGISTER)
897     {
898       if (!strict)
899         return 1;
900       regno = reg_renumber[regno];
901     }
902   if (regno == ARG_POINTER_REGNUM
903       || regno == FRAME_POINTER_REGNUM)
904     return 1;
905   return GP_REG_P (regno);
906 }
907
908 /* Implement GO_IF_LEGITIMATE_ADDRESS macro.  */
909 int
910 score7_address_p (enum machine_mode mode, rtx x, int strict)
911 {
912   struct score7_address_info addr;
913
914   return score7_classify_address (&addr, mode, x, strict);
915 }
916
917 /* Return a number assessing the cost of moving a register in class
918    FROM to class TO. */
919 int
920 score7_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
921                            enum reg_class from, enum reg_class to)
922 {
923   if (GR_REG_CLASS_P (from))
924     {
925       if (GR_REG_CLASS_P (to))
926         return 2;
927       else if (SP_REG_CLASS_P (to))
928         return 4;
929       else if (CP_REG_CLASS_P (to))
930         return 5;
931       else if (CE_REG_CLASS_P (to))
932         return 6;
933     }
934   if (GR_REG_CLASS_P (to))
935     {
936       if (GR_REG_CLASS_P (from))
937         return 2;
938       else if (SP_REG_CLASS_P (from))
939         return 4;
940       else if (CP_REG_CLASS_P (from))
941         return 5;
942       else if (CE_REG_CLASS_P (from))
943         return 6;
944     }
945   return 12;
946 }
947
948 /* Return the number of instructions needed to load a symbol of the
949    given type into a register.  */
950 static int
951 score7_symbol_insns (enum score_symbol_type type)
952 {
953   switch (type)
954     {
955     case SYMBOL_GENERAL:
956       return 2;
957
958     case SYMBOL_SMALL_DATA:
959       return 1;
960     }
961
962   gcc_unreachable ();
963 }
964
965 /* Return the number of instructions needed to load or store a value
966    of mode MODE at X.  Return 0 if X isn't valid for MODE.  */
967 static int
968 score7_address_insns (rtx x, enum machine_mode mode)
969 {
970   struct score7_address_info addr;
971   int factor;
972
973   if (mode == BLKmode)
974     factor = 1;
975   else
976     factor = (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
977
978   if (score7_classify_address (&addr, mode, x, false))
979     switch (addr.type)
980       {
981       case SCORE7_ADD_REG:
982       case SCORE7_ADD_CONST_INT:
983         return factor;
984
985       case SCORE7_ADD_SYMBOLIC:
986         return factor * score7_symbol_insns (addr.symbol_type);
987       }
988   return 0;
989 }
990
991 /* Implement TARGET_RTX_COSTS macro.  */
992 bool
993 score7_rtx_costs (rtx x, int code, int outer_code, int *total)
994 {
995   enum machine_mode mode = GET_MODE (x);
996
997   switch (code)
998     {
999     case CONST_INT:
1000       if (outer_code == SET)
1001         {
1002           if (CONST_OK_FOR_LETTER_P (INTVAL (x), 'I')
1003               || CONST_OK_FOR_LETTER_P (INTVAL (x), 'L'))
1004             *total = COSTS_N_INSNS (1);
1005           else
1006             *total = COSTS_N_INSNS (2);
1007         }
1008       else if (outer_code == PLUS || outer_code == MINUS)
1009         {
1010           if (CONST_OK_FOR_LETTER_P (INTVAL (x), 'N'))
1011             *total = 0;
1012           else if (CONST_OK_FOR_LETTER_P (INTVAL (x), 'I')
1013                    || CONST_OK_FOR_LETTER_P (INTVAL (x), 'L'))
1014             *total = 1;
1015           else
1016             *total = COSTS_N_INSNS (2);
1017         }
1018       else if (outer_code == AND || outer_code == IOR)
1019         {
1020           if (CONST_OK_FOR_LETTER_P (INTVAL (x), 'M'))
1021             *total = 0;
1022           else if (CONST_OK_FOR_LETTER_P (INTVAL (x), 'I')
1023                    || CONST_OK_FOR_LETTER_P (INTVAL (x), 'K'))
1024             *total = 1;
1025           else
1026             *total = COSTS_N_INSNS (2);
1027         }
1028       else
1029         {
1030           *total = 0;
1031         }
1032       return true;
1033
1034     case CONST:
1035     case SYMBOL_REF:
1036     case LABEL_REF:
1037     case CONST_DOUBLE:
1038       *total = COSTS_N_INSNS (2);
1039       return true;
1040
1041     case MEM:
1042       {
1043         /* If the address is legitimate, return the number of
1044            instructions it needs, otherwise use the default handling.  */
1045         int n = score7_address_insns (XEXP (x, 0), GET_MODE (x));
1046         if (n > 0)
1047           {
1048             *total = COSTS_N_INSNS (n + 1);
1049             return true;
1050           }
1051         return false;
1052       }
1053
1054     case FFS:
1055       *total = COSTS_N_INSNS (6);
1056       return true;
1057
1058     case NOT:
1059       *total = COSTS_N_INSNS (1);
1060       return true;
1061
1062     case AND:
1063     case IOR:
1064     case XOR:
1065       if (mode == DImode)
1066         {
1067           *total = COSTS_N_INSNS (2);
1068           return true;
1069         }
1070       return false;
1071
1072     case ASHIFT:
1073     case ASHIFTRT:
1074     case LSHIFTRT:
1075       if (mode == DImode)
1076         {
1077           *total = COSTS_N_INSNS ((GET_CODE (XEXP (x, 1)) == CONST_INT)
1078                                   ? 4 : 12);
1079           return true;
1080         }
1081       return false;
1082
1083     case ABS:
1084       *total = COSTS_N_INSNS (4);
1085       return true;
1086
1087     case PLUS:
1088     case MINUS:
1089       if (mode == DImode)
1090         {
1091           *total = COSTS_N_INSNS (4);
1092           return true;
1093         }
1094       *total = COSTS_N_INSNS (1);
1095       return true;
1096
1097     case NEG:
1098       if (mode == DImode)
1099         {
1100           *total = COSTS_N_INSNS (4);
1101           return true;
1102         }
1103       return false;
1104
1105     case MULT:
1106       *total = optimize_size ? COSTS_N_INSNS (2) : COSTS_N_INSNS (12);
1107       return true;
1108
1109     case DIV:
1110     case MOD:
1111     case UDIV:
1112     case UMOD:
1113       *total = optimize_size ? COSTS_N_INSNS (2) : COSTS_N_INSNS (33);
1114       return true;
1115
1116     case SIGN_EXTEND:
1117     case ZERO_EXTEND:
1118       switch (GET_MODE (XEXP (x, 0)))
1119         {
1120         case QImode:
1121         case HImode:
1122           if (GET_CODE (XEXP (x, 0)) == MEM)
1123             {
1124               *total = COSTS_N_INSNS (2);
1125
1126               if (!TARGET_LITTLE_ENDIAN &&
1127                   side_effects_p (XEXP (XEXP (x, 0), 0)))
1128                 *total = 100;
1129             }
1130           else
1131             *total = COSTS_N_INSNS (1);
1132           break;
1133
1134         default:
1135           *total = COSTS_N_INSNS (1);
1136           break;
1137         }
1138       return true;
1139
1140     default:
1141       return false;
1142     }
1143 }
1144
1145 /* Implement TARGET_ADDRESS_COST macro.  */
1146 int
1147 score7_address_cost (rtx addr)
1148 {
1149   return score7_address_insns (addr, SImode);
1150 }
1151
1152 /* Implement ASM_OUTPUT_EXTERNAL macro.  */
1153 int
1154 score7_output_external (FILE *file ATTRIBUTE_UNUSED,
1155                         tree decl, const char *name)
1156 {
1157   register struct extern_list *p;
1158
1159   if (score7_in_small_data_p (decl))
1160     {
1161       p = (struct extern_list *) ggc_alloc (sizeof (struct extern_list));
1162       p->next = extern_head;
1163       p->name = name;
1164       p->size = int_size_in_bytes (TREE_TYPE (decl));
1165       extern_head = p;
1166     }
1167   return 0;
1168 }
1169
1170 /* Implement RETURN_ADDR_RTX.  Note, we do not support moving
1171    back to a previous frame.  */
1172 rtx
1173 score7_return_addr (int count, rtx frame ATTRIBUTE_UNUSED)
1174 {
1175   if (count != 0)
1176     return const0_rtx;
1177   return get_hard_reg_initial_val (Pmode, RA_REGNUM);
1178 }
1179
1180 /* Implement PRINT_OPERAND macro.  */
1181 /* Score-specific operand codes:
1182    '['        print .set nor1 directive
1183    ']'        print .set r1 directive
1184    'U'        print hi part of a CONST_INT rtx
1185    'E'        print log2(v)
1186    'F'        print log2(~v)
1187    'D'        print SFmode const double
1188    'S'        selectively print "!" if operand is 15bit instruction accessible
1189    'V'        print "v!" if operand is 15bit instruction accessible, or "lfh!"
1190    'L'        low  part of DImode reg operand
1191    'H'        high part of DImode reg operand
1192    'C'        print part of opcode for a branch condition.  */
1193 void
1194 score7_print_operand (FILE *file, rtx op, int c)
1195 {
1196   enum rtx_code code = -1;
1197   if (!PRINT_OPERAND_PUNCT_VALID_P (c))
1198     code = GET_CODE (op);
1199
1200   if (c == '[')
1201     {
1202       fprintf (file, ".set r1\n");
1203     }
1204   else if (c == ']')
1205     {
1206       fprintf (file, "\n\t.set nor1");
1207     }
1208   else if (c == 'U')
1209     {
1210       gcc_assert (code == CONST_INT);
1211       fprintf (file, HOST_WIDE_INT_PRINT_HEX,
1212                (INTVAL (op) >> 16) & 0xffff);
1213     }
1214   else if (c == 'D')
1215     {
1216       if (GET_CODE (op) == CONST_DOUBLE)
1217         {
1218           rtx temp = gen_lowpart (SImode, op);
1219           gcc_assert (GET_MODE (op) == SFmode);
1220           fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (temp) & 0xffffffff);
1221         }
1222       else
1223         output_addr_const (file, op);
1224     }
1225   else if (c == 'S')
1226     {
1227       gcc_assert (code == REG);
1228       if (G16_REG_P (REGNO (op)))
1229         fprintf (file, "!");
1230     }
1231   else if (c == 'V')
1232     {
1233       gcc_assert (code == REG);
1234       fprintf (file, G16_REG_P (REGNO (op)) ? "v!" : "lfh!");
1235     }
1236   else if (c == 'C')
1237     {
1238       enum machine_mode mode = GET_MODE (XEXP (op, 0));
1239
1240       switch (code)
1241         {
1242         case EQ: fputs ("eq", file); break;
1243         case NE: fputs ("ne", file); break;
1244         case GT: fputs ("gt", file); break;
1245         case GE: fputs (mode != CCmode ? "pl" : "ge", file); break;
1246         case LT: fputs (mode != CCmode ? "mi" : "lt", file); break;
1247         case LE: fputs ("le", file); break;
1248         case GTU: fputs ("gtu", file); break;
1249         case GEU: fputs ("cs", file); break;
1250         case LTU: fputs ("cc", file); break;
1251         case LEU: fputs ("leu", file); break;
1252         default:
1253           output_operand_lossage ("invalid operand for code: '%c'", code);
1254         }
1255     }
1256   else if (c == 'E')
1257     {
1258       unsigned HOST_WIDE_INT i;
1259       unsigned HOST_WIDE_INT pow2mask = 1;
1260       unsigned HOST_WIDE_INT val;
1261
1262       val = INTVAL (op);
1263       for (i = 0; i < 32; i++)
1264         {
1265           if (val == pow2mask)
1266             break;
1267           pow2mask <<= 1;
1268         }
1269       gcc_assert (i < 32);
1270       fprintf (file, HOST_WIDE_INT_PRINT_HEX, i);
1271     }
1272   else if (c == 'F')
1273     {
1274       unsigned HOST_WIDE_INT i;
1275       unsigned HOST_WIDE_INT pow2mask = 1;
1276       unsigned HOST_WIDE_INT val;
1277
1278       val = ~INTVAL (op);
1279       for (i = 0; i < 32; i++)
1280         {
1281           if (val == pow2mask)
1282             break;
1283           pow2mask <<= 1;
1284         }
1285       gcc_assert (i < 32);
1286       fprintf (file, HOST_WIDE_INT_PRINT_HEX, i);
1287     }
1288   else if (code == REG)
1289     {
1290       int regnum = REGNO (op);
1291       if ((c == 'H' && !WORDS_BIG_ENDIAN)
1292           || (c == 'L' && WORDS_BIG_ENDIAN))
1293         regnum ++;
1294       fprintf (file, "%s", reg_names[regnum]);
1295     }
1296   else
1297     {
1298       switch (code)
1299         {
1300         case MEM:
1301           score7_print_operand_address (file, op);
1302           break;
1303         default:
1304           output_addr_const (file, op);
1305         }
1306     }
1307 }
1308
1309 /* Implement PRINT_OPERAND_ADDRESS macro.  */
1310 void
1311 score7_print_operand_address (FILE *file, rtx x)
1312 {
1313   struct score7_address_info addr;
1314   enum rtx_code code = GET_CODE (x);
1315   enum machine_mode mode = GET_MODE (x);
1316
1317   if (code == MEM)
1318     x = XEXP (x, 0);
1319
1320   if (score7_classify_address (&addr, mode, x, true))
1321     {
1322       switch (addr.type)
1323         {
1324         case SCORE7_ADD_REG:
1325           {
1326             switch (addr.code)
1327               {
1328               case PRE_DEC:
1329                 fprintf (file, "[%s,-%ld]+", reg_names[REGNO (addr.reg)],
1330                          INTVAL (addr.offset));
1331                 break;
1332               case POST_DEC:
1333                 fprintf (file, "[%s]+,-%ld", reg_names[REGNO (addr.reg)],
1334                          INTVAL (addr.offset));
1335                 break;
1336               case PRE_INC:
1337                 fprintf (file, "[%s, %ld]+", reg_names[REGNO (addr.reg)],
1338                          INTVAL (addr.offset));
1339                 break;
1340               case POST_INC:
1341                 fprintf (file, "[%s]+, %ld", reg_names[REGNO (addr.reg)],
1342                          INTVAL (addr.offset));
1343                 break;
1344               default:
1345                 if (INTVAL(addr.offset) == 0)
1346                   fprintf(file, "[%s]", reg_names[REGNO (addr.reg)]);
1347                 else
1348                   fprintf(file, "[%s, %ld]", reg_names[REGNO (addr.reg)],
1349                           INTVAL(addr.offset));
1350                 break;
1351               }
1352           }
1353           return;
1354         case SCORE7_ADD_CONST_INT:
1355         case SCORE7_ADD_SYMBOLIC:
1356           output_addr_const (file, x);
1357           return;
1358         }
1359     }
1360   print_rtl (stderr, x);
1361   gcc_unreachable ();
1362 }
1363
1364 /* Implement SELECT_CC_MODE macro.  */
1365 enum machine_mode
1366 score7_select_cc_mode (enum rtx_code op, rtx x, rtx y)
1367 {
1368   if ((op == EQ || op == NE || op == LT || op == GE)
1369       && y == const0_rtx
1370       && GET_MODE (x) == SImode)
1371     {
1372       switch (GET_CODE (x))
1373         {
1374         case PLUS:
1375         case MINUS:
1376         case NEG:
1377         case AND:
1378         case IOR:
1379         case XOR:
1380         case NOT:
1381         case ASHIFT:
1382         case LSHIFTRT:
1383         case ASHIFTRT:
1384           return CC_NZmode;
1385
1386         case SIGN_EXTEND:
1387         case ZERO_EXTEND:
1388         case ROTATE:
1389         case ROTATERT:
1390           return (op == LT || op == GE) ? CC_Nmode : CCmode;
1391
1392         default:
1393           return CCmode;
1394         }
1395     }
1396
1397   if ((op == EQ || op == NE)
1398       && (GET_CODE (y) == NEG)
1399       && register_operand (XEXP (y, 0), SImode)
1400       && register_operand (x, SImode))
1401     {
1402       return CC_NZmode;
1403     }
1404
1405   return CCmode;
1406 }
1407
1408 /* Generate the prologue instructions for entry into a S+core function.  */
1409 void
1410 score7_prologue (void)
1411 {
1412 #define EMIT_PL(_rtx)        RTX_FRAME_RELATED_P (_rtx) = 1
1413
1414   struct score7_frame_info *f = score7_compute_frame_size (get_frame_size ());
1415   HOST_WIDE_INT size;
1416   int regno;
1417
1418   size = f->total_size - f->gp_reg_size;
1419
1420   if (flag_pic)
1421     emit_insn (gen_cpload_score7 ());
1422
1423   for (regno = (int) GP_REG_LAST; regno >= (int) GP_REG_FIRST; regno--)
1424     {
1425       if (BITSET_P (f->mask, regno - GP_REG_FIRST))
1426         {
1427           rtx mem = gen_rtx_MEM (SImode,
1428                                  gen_rtx_PRE_DEC (SImode, stack_pointer_rtx));
1429           rtx reg = gen_rtx_REG (SImode, regno);
1430           if (!current_function_calls_eh_return)
1431             MEM_READONLY_P (mem) = 1;
1432           EMIT_PL (emit_insn (gen_pushsi_score7 (mem, reg)));
1433         }
1434     }
1435
1436   if (size > 0)
1437     {
1438       rtx insn;
1439
1440       if (CONST_OK_FOR_LETTER_P (-size, 'L'))
1441         EMIT_PL (emit_insn (gen_add3_insn (stack_pointer_rtx,
1442                                            stack_pointer_rtx,
1443                                            GEN_INT (-size))));
1444       else
1445         {
1446           EMIT_PL (emit_move_insn (gen_rtx_REG (Pmode, SCORE7_PROLOGUE_TEMP_REGNUM),
1447                                    GEN_INT (size)));
1448           EMIT_PL (emit_insn
1449                    (gen_sub3_insn (stack_pointer_rtx,
1450                                    stack_pointer_rtx,
1451                                    gen_rtx_REG (Pmode,
1452                                                 SCORE7_PROLOGUE_TEMP_REGNUM))));
1453         }
1454       insn = get_last_insn ();
1455       REG_NOTES (insn) =
1456         alloc_EXPR_LIST (REG_FRAME_RELATED_EXPR,
1457                          gen_rtx_SET (VOIDmode, stack_pointer_rtx,
1458                                       plus_constant (stack_pointer_rtx,
1459                                                      -size)),
1460                                       REG_NOTES (insn));
1461     }
1462
1463   if (frame_pointer_needed)
1464     EMIT_PL (emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx));
1465
1466   if (flag_pic && f->cprestore_size)
1467     {
1468       if (frame_pointer_needed)
1469         emit_insn (gen_cprestore_use_fp_score7 (GEN_INT (size - f->cprestore_size)));
1470       else
1471         emit_insn (gen_cprestore_use_sp_score7 (GEN_INT (size - f->cprestore_size)));
1472     }
1473
1474 #undef EMIT_PL
1475 }
1476
1477 /* Generate the epilogue instructions in a S+core function.  */
1478 void
1479 score7_epilogue (int sibcall_p)
1480 {
1481   struct score7_frame_info *f = score7_compute_frame_size (get_frame_size ());
1482   HOST_WIDE_INT size;
1483   int regno;
1484   rtx base;
1485
1486   size = f->total_size - f->gp_reg_size;
1487
1488   if (!frame_pointer_needed)
1489     base = stack_pointer_rtx;
1490   else
1491     base = hard_frame_pointer_rtx;
1492
1493   if (size)
1494     {
1495       if (CONST_OK_FOR_LETTER_P (size, 'L'))
1496         emit_insn (gen_add3_insn (base, base, GEN_INT (size)));
1497       else
1498         {
1499           emit_move_insn (gen_rtx_REG (Pmode, SCORE7_EPILOGUE_TEMP_REGNUM),
1500                           GEN_INT (size));
1501           emit_insn (gen_add3_insn (base, base,
1502                                     gen_rtx_REG (Pmode,
1503                                                  SCORE7_EPILOGUE_TEMP_REGNUM)));
1504         }
1505     }
1506
1507   if (base != stack_pointer_rtx)
1508     emit_move_insn (stack_pointer_rtx, base);
1509
1510   if (current_function_calls_eh_return)
1511     emit_insn (gen_add3_insn (stack_pointer_rtx,
1512                               stack_pointer_rtx,
1513                               EH_RETURN_STACKADJ_RTX));
1514
1515   for (regno = (int) GP_REG_FIRST; regno <= (int) GP_REG_LAST; regno++)
1516     {
1517       if (BITSET_P (f->mask, regno - GP_REG_FIRST))
1518         {
1519           rtx mem = gen_rtx_MEM (SImode,
1520                                  gen_rtx_POST_INC (SImode, stack_pointer_rtx));
1521           rtx reg = gen_rtx_REG (SImode, regno);
1522
1523           if (!current_function_calls_eh_return)
1524             MEM_READONLY_P (mem) = 1;
1525
1526           emit_insn (gen_popsi_score7 (reg, mem));
1527         }
1528     }
1529
1530   if (!sibcall_p)
1531     emit_jump_insn (gen_return_internal_score7 (gen_rtx_REG (Pmode, RA_REGNUM)));
1532 }
1533
1534 void
1535 score7_gen_cmp (enum machine_mode mode)
1536 {
1537   emit_insn (gen_rtx_SET (VOIDmode, gen_rtx_REG (mode, CC_REGNUM),
1538                           gen_rtx_COMPARE (mode, cmp_op0, cmp_op1)));
1539 }
1540
1541 /* Return true if X is a symbolic constant that can be calculated in
1542    the same way as a bare symbol.  If it is, store the type of the
1543    symbol in *SYMBOL_TYPE.  */
1544 int
1545 score7_symbolic_constant_p (rtx x, enum score_symbol_type *symbol_type)
1546 {
1547   HOST_WIDE_INT offset;
1548
1549   score7_split_const (x, &x, &offset);
1550   if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF)
1551     *symbol_type = score7_classify_symbol (x);
1552   else
1553     return 0;
1554
1555   if (offset == 0)
1556     return 1;
1557
1558   /* if offset > 15bit, must reload  */
1559   if (!IMM_IN_RANGE (offset, 15, 1))
1560     return 0;
1561
1562   switch (*symbol_type)
1563     {
1564     case SYMBOL_GENERAL:
1565       return 1;
1566     case SYMBOL_SMALL_DATA:
1567       return score7_offset_within_object_p (x, offset);
1568     }
1569   gcc_unreachable ();
1570 }
1571
1572 void
1573 score7_movsicc (rtx *ops)
1574 {
1575   enum machine_mode mode;
1576
1577   mode = score7_select_cc_mode (GET_CODE (ops[1]), ops[2], ops[3]);
1578   emit_insn (gen_rtx_SET (VOIDmode, gen_rtx_REG (mode, CC_REGNUM),
1579                           gen_rtx_COMPARE (mode, cmp_op0, cmp_op1)));
1580 }
1581
1582 /* Call and sibcall pattern all need call this function.  */
1583 void
1584 score7_call (rtx *ops, bool sib)
1585 {
1586   rtx addr = XEXP (ops[0], 0);
1587   if (!call_insn_operand (addr, VOIDmode))
1588     {
1589       rtx oaddr = addr;
1590       addr = gen_reg_rtx (Pmode);
1591       gen_move_insn (addr, oaddr);
1592     }
1593
1594   if (sib)
1595     emit_call_insn (gen_sibcall_internal_score7 (addr, ops[1]));
1596   else
1597     emit_call_insn (gen_call_internal_score7 (addr, ops[1]));
1598 }
1599
1600 /* Call value and sibcall value pattern all need call this function.  */
1601 void
1602 score7_call_value (rtx *ops, bool sib)
1603 {
1604   rtx result = ops[0];
1605   rtx addr = XEXP (ops[1], 0);
1606   rtx arg = ops[2];
1607
1608   if (!call_insn_operand (addr, VOIDmode))
1609     {
1610       rtx oaddr = addr;
1611       addr = gen_reg_rtx (Pmode);
1612       gen_move_insn (addr, oaddr);
1613     }
1614
1615   if (sib)
1616     emit_call_insn (gen_sibcall_value_internal_score7 (result, addr, arg));
1617   else
1618     emit_call_insn (gen_call_value_internal_score7 (result, addr, arg));
1619 }
1620
1621 /* Machine Split  */
1622 void
1623 score7_movdi (rtx *ops)
1624 {
1625   rtx dst = ops[0];
1626   rtx src = ops[1];
1627   rtx dst0 = score7_subw (dst, 0);
1628   rtx dst1 = score7_subw (dst, 1);
1629   rtx src0 = score7_subw (src, 0);
1630   rtx src1 = score7_subw (src, 1);
1631
1632   if (GET_CODE (dst0) == REG && reg_overlap_mentioned_p (dst0, src))
1633     {
1634       emit_move_insn (dst1, src1);
1635       emit_move_insn (dst0, src0);
1636     }
1637   else
1638     {
1639       emit_move_insn (dst0, src0);
1640       emit_move_insn (dst1, src1);
1641     }
1642 }
1643
1644 void
1645 score7_zero_extract_andi (rtx *ops)
1646 {
1647   if (INTVAL (ops[1]) == 1 && const_uimm5 (ops[2], SImode))
1648     emit_insn (gen_zero_extract_bittst_score7 (ops[0], ops[2]));
1649   else
1650     {
1651       unsigned HOST_WIDE_INT mask;
1652       mask = (0xffffffffU & ((1U << INTVAL (ops[1])) - 1U));
1653       mask = mask << INTVAL (ops[2]);
1654       emit_insn (gen_andsi3_cmp_score7 (ops[3], ops[0],
1655                                  gen_int_mode (mask, SImode)));
1656     }
1657 }
1658
1659 /* Check addr could be present as PRE/POST mode.  */
1660 static bool
1661 score7_pindex_mem (rtx addr)
1662 {
1663   if (GET_CODE (addr) == MEM)
1664     {
1665       switch (GET_CODE (XEXP (addr, 0)))
1666         {
1667         case PRE_DEC:
1668         case POST_DEC:
1669         case PRE_INC:
1670         case POST_INC:
1671           return true;
1672         default:
1673           break;
1674         }
1675     }
1676   return false;
1677 }
1678
1679 /* Output asm code for ld/sw insn.  */
1680 static int
1681 score7_pr_addr_post (rtx *ops, int idata, int iaddr, char *ip, enum score_mem_unit unit)
1682 {
1683   struct score7_address_info ai;
1684
1685   gcc_assert (GET_CODE (ops[idata]) == REG);
1686   gcc_assert (score7_classify_address (&ai, SImode, XEXP (ops[iaddr], 0), true));
1687
1688   if (!score7_pindex_mem (ops[iaddr])
1689       && ai.type == SCORE7_ADD_REG
1690       && GET_CODE (ai.offset) == CONST_INT
1691       && G16_REG_P (REGNO (ops[idata]))
1692       && G16_REG_P (REGNO (ai.reg)))
1693     {
1694       if (INTVAL (ai.offset) == 0)
1695         {
1696           ops[iaddr] = ai.reg;
1697           return snprintf (ip, INS_BUF_SZ,
1698                            "!\t%%%d, [%%%d]", idata, iaddr);
1699         }
1700       if (REGNO (ai.reg) == HARD_FRAME_POINTER_REGNUM)
1701         {
1702           HOST_WIDE_INT offset = INTVAL (ai.offset);
1703           if (SCORE_ALIGN_UNIT (offset, unit)
1704               && CONST_OK_FOR_LETTER_P (offset >> unit, 'J'))
1705             {
1706               ops[iaddr] = ai.offset;
1707               return snprintf (ip, INS_BUF_SZ,
1708                                "p!\t%%%d, %%c%d", idata, iaddr);
1709             }
1710         }
1711     }
1712   return snprintf (ip, INS_BUF_SZ, "\t%%%d, %%a%d", idata, iaddr);
1713 }
1714
1715 /* Output asm insn for load.  */
1716 const char *
1717 score7_linsn (rtx *ops, enum score_mem_unit unit, bool sign)
1718 {
1719   const char *pre_ins[] =
1720     {"lbu", "lhu", "lw", "??", "lb", "lh", "lw", "??"};
1721   char *ip;
1722
1723   strcpy (score7_ins, pre_ins[(sign ? 4 : 0) + unit]);
1724   ip = score7_ins + strlen (score7_ins);
1725
1726   if ((!sign && unit != SCORE_HWORD)
1727       || (sign && unit != SCORE_BYTE))
1728     score7_pr_addr_post (ops, 0, 1, ip, unit);
1729   else
1730     snprintf (ip, INS_BUF_SZ, "\t%%0, %%a1");
1731
1732   return score7_ins;
1733 }
1734
1735 /* Output asm insn for store.  */
1736 const char *
1737 score7_sinsn (rtx *ops, enum score_mem_unit unit)
1738 {
1739   const char *pre_ins[] = {"sb", "sh", "sw"};
1740   char *ip;
1741
1742   strcpy (score7_ins, pre_ins[unit]);
1743   ip = score7_ins + strlen (score7_ins);
1744   score7_pr_addr_post (ops, 1, 0, ip, unit);
1745   return score7_ins;
1746 }
1747
1748 /* Output asm insn for load immediate.  */
1749 const char *
1750 score7_limm (rtx *ops)
1751 {
1752   HOST_WIDE_INT v;
1753
1754   gcc_assert (GET_CODE (ops[0]) == REG);
1755   gcc_assert (GET_CODE (ops[1]) == CONST_INT);
1756
1757   v = INTVAL (ops[1]);
1758   if (G16_REG_P (REGNO (ops[0])) && IMM_IN_RANGE (v, 8, 0))
1759     return "ldiu!\t%0, %c1";
1760   else if (IMM_IN_RANGE (v, 16, 1))
1761     return "ldi\t%0, %c1";
1762   else if ((v & 0xffff) == 0)
1763     return "ldis\t%0, %U1";
1764   else
1765     return "li\t%0, %c1";
1766 }
1767
1768 /* Output asm insn for move.  */
1769 const char *
1770 score7_move (rtx *ops)
1771 {
1772   gcc_assert (GET_CODE (ops[0]) == REG);
1773   gcc_assert (GET_CODE (ops[1]) == REG);
1774
1775   if (G16_REG_P (REGNO (ops[0])))
1776     {
1777       if (G16_REG_P (REGNO (ops[1])))
1778         return "mv!\t%0, %1";
1779       else
1780         return "mlfh!\t%0, %1";
1781     }
1782   else if (G16_REG_P (REGNO (ops[1])))
1783     return "mhfl!\t%0, %1";
1784   else
1785     return "mv\t%0, %1";
1786 }
1787
1788 /* Generate add insn.  */
1789 const char *
1790 score7_select_add_imm (rtx *ops, bool set_cc)
1791 {
1792   HOST_WIDE_INT v = INTVAL (ops[2]);
1793
1794   gcc_assert (GET_CODE (ops[2]) == CONST_INT);
1795   gcc_assert (REGNO (ops[0]) == REGNO (ops[1]));
1796
1797   if (set_cc && G16_REG_P (REGNO (ops[0])))
1798     {
1799       if (v > 0 && IMM_IS_POW_OF_2 ((unsigned HOST_WIDE_INT) v, 0, 15))
1800         {
1801           ops[2] = GEN_INT (ffs (v) - 1);
1802           return "addei!\t%0, %c2";
1803         }
1804
1805       if (v < 0 && IMM_IS_POW_OF_2 ((unsigned HOST_WIDE_INT) (-v), 0, 15))
1806         {
1807           ops[2] = GEN_INT (ffs (-v) - 1);
1808           return "subei!\t%0, %c2";
1809         }
1810     }
1811
1812   if (set_cc)
1813     return "addi.c\t%0, %c2";
1814   else
1815     return "addi\t%0, %c2";
1816 }
1817
1818 /* Output arith insn.  */
1819 const char *
1820 score7_select (rtx *ops, const char *inst_pre,
1821                bool commu, const char *letter, bool set_cc)
1822 {
1823   gcc_assert (GET_CODE (ops[0]) == REG);
1824   gcc_assert (GET_CODE (ops[1]) == REG);
1825
1826   if (set_cc && G16_REG_P (REGNO (ops[0]))
1827       && (GET_CODE (ops[2]) == REG ? G16_REG_P (REGNO (ops[2])) : 1)
1828       && REGNO (ops[0]) == REGNO (ops[1]))
1829     {
1830       snprintf (score7_ins, INS_BUF_SZ, "%s!\t%%0, %%%s2", inst_pre, letter);
1831       return score7_ins;
1832     }
1833
1834   if (commu && set_cc && G16_REG_P (REGNO (ops[0]))
1835       && G16_REG_P (REGNO (ops[1]))
1836       && REGNO (ops[0]) == REGNO (ops[2]))
1837     {
1838       gcc_assert (GET_CODE (ops[2]) == REG);
1839       snprintf (score7_ins, INS_BUF_SZ, "%s!\t%%0, %%%s1", inst_pre, letter);
1840       return score7_ins;
1841     }
1842
1843   if (set_cc)
1844     snprintf (score7_ins, INS_BUF_SZ, "%s.c\t%%0, %%1, %%%s2", inst_pre, letter);
1845   else
1846     snprintf (score7_ins, INS_BUF_SZ, "%s\t%%0, %%1, %%%s2", inst_pre, letter);
1847   return score7_ins;
1848 }
1849