OSDN Git Service

2008-04-03 Jan Hubicka <jh@suse.cz>
[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   free_after_compilation (cfun);
385
386   /* Clean up the vars set above.  Note that final_end_function resets
387      the global pointer for us.  */
388   reload_completed = 0;
389 }
390
391 /* Copy VALUE to a register and return that register.  If new psuedos
392    are allowed, copy it into a new register, otherwise use DEST.  */
393 static rtx
394 score7_force_temporary (rtx dest, rtx value)
395 {
396   if (can_create_pseudo_p ())
397     return force_reg (Pmode, value);
398   else
399     {
400       emit_move_insn (copy_rtx (dest), value);
401       return dest;
402     }
403 }
404
405 /* Return a LO_SUM expression for ADDR.  TEMP is as for score_force_temporary
406    and is used to load the high part into a register.  */
407 static rtx
408 score7_split_symbol (rtx temp, rtx addr)
409 {
410   rtx high = score7_force_temporary (temp,
411                                      gen_rtx_HIGH (Pmode, copy_rtx (addr)));
412   return gen_rtx_LO_SUM (Pmode, high, addr);
413 }
414
415 /* This function is used to implement LEGITIMIZE_ADDRESS.  If *XLOC can
416    be legitimized in a way that the generic machinery might not expect,
417    put the new address in *XLOC and return true.  */
418 int
419 score7_legitimize_address (rtx *xloc)
420 {
421   enum score_symbol_type symbol_type;
422
423   if (score7_symbolic_constant_p (*xloc, &symbol_type)
424       && symbol_type == SYMBOL_GENERAL)
425     {
426       *xloc = score7_split_symbol (0, *xloc);
427       return 1;
428     }
429
430   if (GET_CODE (*xloc) == PLUS
431       && GET_CODE (XEXP (*xloc, 1)) == CONST_INT)
432     {
433       rtx reg = XEXP (*xloc, 0);
434       if (!score7_valid_base_register_p (reg, 0))
435         reg = copy_to_mode_reg (Pmode, reg);
436       *xloc = score7_add_offset (reg, INTVAL (XEXP (*xloc, 1)));
437       return 1;
438     }
439   return 0;
440 }
441
442 /* Fill INFO with information about a single argument.  CUM is the
443    cumulative state for earlier arguments.  MODE is the mode of this
444    argument and TYPE is its type (if known).  NAMED is true if this
445    is a named (fixed) argument rather than a variable one.  */
446 static void
447 score7_classify_arg (const CUMULATIVE_ARGS *cum, enum machine_mode mode,
448                      tree type, int named, struct score7_arg_info *info)
449 {
450   int even_reg_p;
451   unsigned int num_words, max_regs;
452
453   even_reg_p = 0;
454   if (GET_MODE_CLASS (mode) == MODE_INT
455       || GET_MODE_CLASS (mode) == MODE_FLOAT)
456     even_reg_p = (GET_MODE_SIZE (mode) > UNITS_PER_WORD);
457   else
458     if (type != NULL_TREE && TYPE_ALIGN (type) > BITS_PER_WORD && named)
459       even_reg_p = 1;
460
461   if (TARGET_MUST_PASS_IN_STACK (mode, type))
462     info->reg_offset = ARG_REG_NUM;
463   else
464     {
465       info->reg_offset = cum->num_gprs;
466       if (even_reg_p)
467         info->reg_offset += info->reg_offset & 1;
468     }
469
470   if (mode == BLKmode)
471     info->num_bytes = int_size_in_bytes (type);
472   else
473     info->num_bytes = GET_MODE_SIZE (mode);
474
475   num_words = (info->num_bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
476   max_regs = ARG_REG_NUM - info->reg_offset;
477
478   /* Partition the argument between registers and stack.  */
479   info->reg_words = MIN (num_words, max_regs);
480   info->stack_words = num_words - info->reg_words;
481
482   /* The alignment applied to registers is also applied to stack arguments.  */
483   if (info->stack_words)
484     {
485       info->stack_offset = cum->stack_words;
486       if (even_reg_p)
487         info->stack_offset += info->stack_offset & 1;
488     }
489 }
490
491 /* Set up the stack and frame (if desired) for the function.  */
492 void
493 score7_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
494 {
495   const char *fnname;
496   struct score7_frame_info *f = score7_cached_frame ();
497   HOST_WIDE_INT tsize = f->total_size;
498
499   fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
500   if (!flag_inhibit_size_directive)
501     {
502       fputs ("\t.ent\t", file);
503       assemble_name (file, fnname);
504       fputs ("\n", file);
505     }
506   assemble_name (file, fnname);
507   fputs (":\n", file);
508
509   if (!flag_inhibit_size_directive)
510     {
511       fprintf (file,
512                "\t.frame\t%s," HOST_WIDE_INT_PRINT_DEC ",%s, %d\t\t"
513                "# vars= " HOST_WIDE_INT_PRINT_DEC ", regs= %d"
514                ", args= " HOST_WIDE_INT_PRINT_DEC
515                ", gp= " HOST_WIDE_INT_PRINT_DEC "\n",
516                (reg_names[(frame_pointer_needed)
517                 ? HARD_FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM]),
518                tsize,
519                reg_names[RA_REGNUM],
520                current_function_is_leaf ? 1 : 0,
521                f->var_size,
522                f->num_gp,
523                f->args_size,
524                f->cprestore_size);
525
526       fprintf(file, "\t.mask\t0x%08x," HOST_WIDE_INT_PRINT_DEC "\n",
527               f->mask,
528               (f->gp_sp_offset - f->total_size));
529     }
530 }
531
532 /* Do any necessary cleanup after a function to restore stack, frame,
533    and regs.  */
534 void
535 score7_function_epilogue (FILE *file,
536                           HOST_WIDE_INT size ATTRIBUTE_UNUSED)
537 {
538   if (!flag_inhibit_size_directive)
539     {
540       const char *fnname;
541       fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
542       fputs ("\t.end\t", file);
543       assemble_name (file, fnname);
544       fputs ("\n", file);
545     }
546 }
547
548 /* Returns true if X contains a SYMBOL_REF.  */
549 static bool
550 score7_symbolic_expression_p (rtx x)
551 {
552   if (GET_CODE (x) == SYMBOL_REF)
553     return true;
554
555   if (GET_CODE (x) == CONST)
556     return score7_symbolic_expression_p (XEXP (x, 0));
557
558   if (UNARY_P (x))
559     return score7_symbolic_expression_p (XEXP (x, 0));
560
561   if (ARITHMETIC_P (x))
562     return (score7_symbolic_expression_p (XEXP (x, 0))
563             || score7_symbolic_expression_p (XEXP (x, 1)));
564
565   return false;
566 }
567
568 /* Choose the section to use for the constant rtx expression X that has
569    mode MODE.  */
570 section *
571 score7_select_rtx_section (enum machine_mode mode, rtx x,
572                            unsigned HOST_WIDE_INT align)
573 {
574   if (GET_MODE_SIZE (mode) <= SCORE7_SDATA_MAX)
575     return get_named_section (0, ".sdata", 0);
576   else if (flag_pic && score7_symbolic_expression_p (x))
577     return get_named_section (0, ".data.rel.ro", 3);
578   else
579     return mergeable_constant_section (mode, align, 0);
580 }
581
582 /* Implement TARGET_IN_SMALL_DATA_P.  */
583 bool
584 score7_in_small_data_p (tree decl)
585 {
586   HOST_WIDE_INT size;
587
588   if (TREE_CODE (decl) == STRING_CST
589       || TREE_CODE (decl) == FUNCTION_DECL)
590     return false;
591
592   if (TREE_CODE (decl) == VAR_DECL && DECL_SECTION_NAME (decl) != 0)
593     {
594       const char *name;
595       name = TREE_STRING_POINTER (DECL_SECTION_NAME (decl));
596       if (strcmp (name, ".sdata") != 0
597           && strcmp (name, ".sbss") != 0)
598         return true;
599       if (!DECL_EXTERNAL (decl))
600         return false;
601     }
602   size = int_size_in_bytes (TREE_TYPE (decl));
603   return (size > 0 && size <= SCORE7_SDATA_MAX);
604 }
605
606 /* Implement TARGET_ASM_FILE_START.  */
607 void
608 score7_asm_file_start (void)
609 {
610   default_file_start ();
611   fprintf (asm_out_file, ASM_COMMENT_START
612            "GCC for S+core %s \n", SCORE_GCC_VERSION);
613
614   if (flag_pic)
615     fprintf (asm_out_file, "\t.set pic\n");
616 }
617
618 /* Implement TARGET_ASM_FILE_END.  When using assembler macros, emit
619    .externs for any small-data variables that turned out to be external.  */
620 void
621 score7_asm_file_end (void)
622 {
623   tree name_tree;
624   struct extern_list *p;
625   if (extern_head)
626     {
627       fputs ("\n", asm_out_file);
628       for (p = extern_head; p != 0; p = p->next)
629         {
630           name_tree = get_identifier (p->name);
631           if (!TREE_ASM_WRITTEN (name_tree)
632               && TREE_SYMBOL_REFERENCED (name_tree))
633             {
634               TREE_ASM_WRITTEN (name_tree) = 1;
635               fputs ("\t.extern\t", asm_out_file);
636               assemble_name (asm_out_file, p->name);
637               fprintf (asm_out_file, ", %d\n", p->size);
638             }
639         }
640     }
641 }
642
643 /* Implement OVERRIDE_OPTIONS macro.  */
644 void
645 score7_override_options (void)
646 {
647   flag_pic = false;
648   if (!flag_pic)
649     score7_sdata_max = g_switch_set ? g_switch_value : SCORE7_DEFAULT_SDATA_MAX;
650   else
651     {
652       score7_sdata_max = 0;
653       if (g_switch_set && (g_switch_value != 0))
654         warning (0, "-fPIC and -G are incompatible");
655     }
656
657   score_char_to_class['d'] = G32_REGS;
658   score_char_to_class['e'] = G16_REGS;
659   score_char_to_class['t'] = T32_REGS;
660
661   score_char_to_class['h'] = HI_REG;
662   score_char_to_class['l'] = LO_REG;
663   score_char_to_class['x'] = CE_REGS;
664
665   score_char_to_class['q'] = CN_REG;
666   score_char_to_class['y'] = LC_REG;
667   score_char_to_class['z'] = SC_REG;
668   score_char_to_class['a'] = SP_REGS;
669
670   score_char_to_class['c'] = CR_REGS;
671 }
672
673 /* Implement REGNO_REG_CLASS macro.  */
674 int
675 score7_reg_class (int regno)
676 {
677   int c;
678   gcc_assert (regno >= 0 && regno < FIRST_PSEUDO_REGISTER);
679
680   if (regno == FRAME_POINTER_REGNUM
681       || regno == ARG_POINTER_REGNUM)
682     return ALL_REGS;
683
684   for (c = 0; c < N_REG_CLASSES; c++)
685     if (TEST_HARD_REG_BIT (reg_class_contents[c], regno))
686       return c;
687
688   return NO_REGS;
689 }
690
691 /* Implement PREFERRED_RELOAD_CLASS macro.  */
692 enum reg_class
693 score7_preferred_reload_class (rtx x ATTRIBUTE_UNUSED, enum reg_class class)
694 {
695   if (reg_class_subset_p (G16_REGS, class))
696     return G16_REGS;
697   if (reg_class_subset_p (G32_REGS, class))
698     return G32_REGS;
699   return class;
700 }
701
702 /* Implement SECONDARY_INPUT_RELOAD_CLASS
703    and SECONDARY_OUTPUT_RELOAD_CLASS macro.  */
704 enum reg_class
705 score7_secondary_reload_class (enum reg_class class,
706                                enum machine_mode mode ATTRIBUTE_UNUSED,
707                                rtx x)
708 {
709   int regno = -1;
710   if (GET_CODE (x) == REG || GET_CODE(x) == SUBREG)
711     regno = true_regnum (x);
712
713   if (!GR_REG_CLASS_P (class))
714     return GP_REG_P (regno) ? NO_REGS : G32_REGS;
715   return NO_REGS;
716 }
717
718 /* Implement CONST_OK_FOR_LETTER_P macro.  */
719 /* imm constraints
720    I        imm16 << 16
721    J        uimm5
722    K        uimm16
723    L        simm16
724    M        uimm14
725    N        simm14  */
726 int
727 score7_const_ok_for_letter_p (HOST_WIDE_INT value, char c)
728 {
729   switch (c)
730     {
731     case 'I': return ((value & 0xffff) == 0);
732     case 'J': return IMM_IN_RANGE (value, 5, 0);
733     case 'K': return IMM_IN_RANGE (value, 16, 0);
734     case 'L': return IMM_IN_RANGE (value, 16, 1);
735     case 'M': return IMM_IN_RANGE (value, 14, 0);
736     case 'N': return IMM_IN_RANGE (value, 14, 1);
737     default : return 0;
738     }
739 }
740
741 /* Implement EXTRA_CONSTRAINT macro.  */
742 /* Z        symbol_ref  */
743 int
744 score7_extra_constraint (rtx op, char c)
745 {
746   switch (c)
747     {
748     case 'Z':
749       return GET_CODE (op) == SYMBOL_REF;
750     default:
751       gcc_unreachable ();
752     }
753 }
754
755 /* Return truth value on whether or not a given hard register
756    can support a given mode.  */
757 int
758 score7_hard_regno_mode_ok (unsigned int regno, enum machine_mode mode)
759 {
760   int size = GET_MODE_SIZE (mode);
761   enum mode_class class = GET_MODE_CLASS (mode);
762
763   if (class == MODE_CC)
764     return regno == CC_REGNUM;
765   else if (regno == FRAME_POINTER_REGNUM
766            || regno == ARG_POINTER_REGNUM)
767     return class == MODE_INT;
768   else if (GP_REG_P (regno))
769     /* ((regno <= (GP_REG_LAST- HARD_REGNO_NREGS (dummy, mode)) + 1)  */
770     return !(regno & 1) || (size <= UNITS_PER_WORD);
771   else if (CE_REG_P (regno))
772     return (class == MODE_INT
773             && ((size <= UNITS_PER_WORD)
774                 || (regno == CE_REG_FIRST && size == 2 * UNITS_PER_WORD)));
775   else
776     return (class == MODE_INT) && (size <= UNITS_PER_WORD);
777 }
778
779 /* Implement INITIAL_ELIMINATION_OFFSET.  FROM is either the frame
780    pointer or argument pointer.  TO is either the stack pointer or
781    hard frame pointer.  */
782 HOST_WIDE_INT
783 score7_initial_elimination_offset (int from,
784                                    int to ATTRIBUTE_UNUSED)
785 {
786   struct score7_frame_info *f = score7_compute_frame_size (get_frame_size ());
787   switch (from)
788     {
789     case ARG_POINTER_REGNUM:
790       return f->total_size;
791     case FRAME_POINTER_REGNUM:
792       return 0;
793     default:
794       gcc_unreachable ();
795     }
796 }
797
798 /* Implement FUNCTION_ARG_ADVANCE macro.  */
799 void
800 score7_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
801                              tree type, int named)
802 {
803   struct score7_arg_info info;
804   score7_classify_arg (cum, mode, type, named, &info);
805   cum->num_gprs = info.reg_offset + info.reg_words;
806   if (info.stack_words > 0)
807     cum->stack_words = info.stack_offset + info.stack_words;
808   cum->arg_number++;
809 }
810
811 /* Implement TARGET_ARG_PARTIAL_BYTES macro.  */
812 int
813 score7_arg_partial_bytes (CUMULATIVE_ARGS *cum,
814                           enum machine_mode mode, tree type, bool named)
815 {
816   struct score7_arg_info info;
817   score7_classify_arg (cum, mode, type, named, &info);
818   return info.stack_words > 0 ? info.reg_words * UNITS_PER_WORD : 0;
819 }
820
821 /* Implement FUNCTION_ARG macro.  */
822 rtx
823 score7_function_arg (const CUMULATIVE_ARGS *cum, enum machine_mode mode,
824                      tree type, int named)
825 {
826   struct score7_arg_info info;
827
828   if (mode == VOIDmode || !named)
829     return 0;
830
831   score7_classify_arg (cum, mode, type, named, &info);
832
833   if (info.reg_offset == ARG_REG_NUM)
834     return 0;
835
836   if (!info.stack_words)
837     return gen_rtx_REG (mode, ARG_REG_FIRST + info.reg_offset);
838   else
839     {
840       rtx ret = gen_rtx_PARALLEL (mode, rtvec_alloc (info.reg_words));
841       unsigned int i, part_offset = 0;
842       for (i = 0; i < info.reg_words; i++)
843         {
844           rtx reg;
845           reg = gen_rtx_REG (SImode, ARG_REG_FIRST + info.reg_offset + i);
846           XVECEXP (ret, 0, i) = gen_rtx_EXPR_LIST (SImode, reg,
847                                                    GEN_INT (part_offset));
848           part_offset += UNITS_PER_WORD;
849         }
850       return ret;
851     }
852 }
853
854 /* Implement FUNCTION_VALUE and LIBCALL_VALUE.  For normal calls,
855    VALTYPE is the return type and MODE is VOIDmode.  For libcalls,
856    VALTYPE is null and MODE is the mode of the return value.  */
857 rtx
858 score7_function_value (tree valtype, tree func ATTRIBUTE_UNUSED,
859                        enum machine_mode mode)
860 {
861   if (valtype)
862     {
863       int unsignedp;
864       mode = TYPE_MODE (valtype);
865       unsignedp = TYPE_UNSIGNED (valtype);
866       mode = promote_mode (valtype, mode, &unsignedp, 1);
867     }
868   return gen_rtx_REG (mode, RT_REGNUM);
869 }
870
871 /* Implement INITIALIZE_TRAMPOLINE macro.  */
872 void
873 score7_initialize_trampoline (rtx ADDR, rtx FUNC, rtx CHAIN)
874 {
875 #define FFCACHE          "_flush_cache"
876 #define CODE_SIZE        (TRAMPOLINE_INSNS * UNITS_PER_WORD)
877
878   rtx pfunc, pchain;
879
880   pfunc = plus_constant (ADDR, CODE_SIZE);
881   pchain = plus_constant (ADDR, CODE_SIZE + GET_MODE_SIZE (SImode));
882
883   emit_move_insn (gen_rtx_MEM (SImode, pfunc), FUNC);
884   emit_move_insn (gen_rtx_MEM (SImode, pchain), CHAIN);
885   emit_library_call (gen_rtx_SYMBOL_REF (Pmode, FFCACHE),
886                      0, VOIDmode, 2,
887                      ADDR, Pmode,
888                      GEN_INT (TRAMPOLINE_SIZE), SImode);
889 #undef FFCACHE
890 #undef CODE_SIZE
891 }
892
893 /* This function is used to implement REG_MODE_OK_FOR_BASE_P macro.  */
894 int
895 score7_regno_mode_ok_for_base_p (int regno, int strict)
896 {
897   if (regno >= FIRST_PSEUDO_REGISTER)
898     {
899       if (!strict)
900         return 1;
901       regno = reg_renumber[regno];
902     }
903   if (regno == ARG_POINTER_REGNUM
904       || regno == FRAME_POINTER_REGNUM)
905     return 1;
906   return GP_REG_P (regno);
907 }
908
909 /* Implement GO_IF_LEGITIMATE_ADDRESS macro.  */
910 int
911 score7_address_p (enum machine_mode mode, rtx x, int strict)
912 {
913   struct score7_address_info addr;
914
915   return score7_classify_address (&addr, mode, x, strict);
916 }
917
918 /* Return a number assessing the cost of moving a register in class
919    FROM to class TO. */
920 int
921 score7_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
922                            enum reg_class from, enum reg_class to)
923 {
924   if (GR_REG_CLASS_P (from))
925     {
926       if (GR_REG_CLASS_P (to))
927         return 2;
928       else if (SP_REG_CLASS_P (to))
929         return 4;
930       else if (CP_REG_CLASS_P (to))
931         return 5;
932       else if (CE_REG_CLASS_P (to))
933         return 6;
934     }
935   if (GR_REG_CLASS_P (to))
936     {
937       if (GR_REG_CLASS_P (from))
938         return 2;
939       else if (SP_REG_CLASS_P (from))
940         return 4;
941       else if (CP_REG_CLASS_P (from))
942         return 5;
943       else if (CE_REG_CLASS_P (from))
944         return 6;
945     }
946   return 12;
947 }
948
949 /* Return the number of instructions needed to load a symbol of the
950    given type into a register.  */
951 static int
952 score7_symbol_insns (enum score_symbol_type type)
953 {
954   switch (type)
955     {
956     case SYMBOL_GENERAL:
957       return 2;
958
959     case SYMBOL_SMALL_DATA:
960       return 1;
961     }
962
963   gcc_unreachable ();
964 }
965
966 /* Return the number of instructions needed to load or store a value
967    of mode MODE at X.  Return 0 if X isn't valid for MODE.  */
968 static int
969 score7_address_insns (rtx x, enum machine_mode mode)
970 {
971   struct score7_address_info addr;
972   int factor;
973
974   if (mode == BLKmode)
975     factor = 1;
976   else
977     factor = (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
978
979   if (score7_classify_address (&addr, mode, x, false))
980     switch (addr.type)
981       {
982       case SCORE7_ADD_REG:
983       case SCORE7_ADD_CONST_INT:
984         return factor;
985
986       case SCORE7_ADD_SYMBOLIC:
987         return factor * score7_symbol_insns (addr.symbol_type);
988       }
989   return 0;
990 }
991
992 /* Implement TARGET_RTX_COSTS macro.  */
993 bool
994 score7_rtx_costs (rtx x, int code, int outer_code, int *total)
995 {
996   enum machine_mode mode = GET_MODE (x);
997
998   switch (code)
999     {
1000     case CONST_INT:
1001       if (outer_code == SET)
1002         {
1003           if (CONST_OK_FOR_LETTER_P (INTVAL (x), 'I')
1004               || CONST_OK_FOR_LETTER_P (INTVAL (x), 'L'))
1005             *total = COSTS_N_INSNS (1);
1006           else
1007             *total = COSTS_N_INSNS (2);
1008         }
1009       else if (outer_code == PLUS || outer_code == MINUS)
1010         {
1011           if (CONST_OK_FOR_LETTER_P (INTVAL (x), 'N'))
1012             *total = 0;
1013           else if (CONST_OK_FOR_LETTER_P (INTVAL (x), 'I')
1014                    || CONST_OK_FOR_LETTER_P (INTVAL (x), 'L'))
1015             *total = 1;
1016           else
1017             *total = COSTS_N_INSNS (2);
1018         }
1019       else if (outer_code == AND || outer_code == IOR)
1020         {
1021           if (CONST_OK_FOR_LETTER_P (INTVAL (x), 'M'))
1022             *total = 0;
1023           else if (CONST_OK_FOR_LETTER_P (INTVAL (x), 'I')
1024                    || CONST_OK_FOR_LETTER_P (INTVAL (x), 'K'))
1025             *total = 1;
1026           else
1027             *total = COSTS_N_INSNS (2);
1028         }
1029       else
1030         {
1031           *total = 0;
1032         }
1033       return true;
1034
1035     case CONST:
1036     case SYMBOL_REF:
1037     case LABEL_REF:
1038     case CONST_DOUBLE:
1039       *total = COSTS_N_INSNS (2);
1040       return true;
1041
1042     case MEM:
1043       {
1044         /* If the address is legitimate, return the number of
1045            instructions it needs, otherwise use the default handling.  */
1046         int n = score7_address_insns (XEXP (x, 0), GET_MODE (x));
1047         if (n > 0)
1048           {
1049             *total = COSTS_N_INSNS (n + 1);
1050             return true;
1051           }
1052         return false;
1053       }
1054
1055     case FFS:
1056       *total = COSTS_N_INSNS (6);
1057       return true;
1058
1059     case NOT:
1060       *total = COSTS_N_INSNS (1);
1061       return true;
1062
1063     case AND:
1064     case IOR:
1065     case XOR:
1066       if (mode == DImode)
1067         {
1068           *total = COSTS_N_INSNS (2);
1069           return true;
1070         }
1071       return false;
1072
1073     case ASHIFT:
1074     case ASHIFTRT:
1075     case LSHIFTRT:
1076       if (mode == DImode)
1077         {
1078           *total = COSTS_N_INSNS ((GET_CODE (XEXP (x, 1)) == CONST_INT)
1079                                   ? 4 : 12);
1080           return true;
1081         }
1082       return false;
1083
1084     case ABS:
1085       *total = COSTS_N_INSNS (4);
1086       return true;
1087
1088     case PLUS:
1089     case MINUS:
1090       if (mode == DImode)
1091         {
1092           *total = COSTS_N_INSNS (4);
1093           return true;
1094         }
1095       *total = COSTS_N_INSNS (1);
1096       return true;
1097
1098     case NEG:
1099       if (mode == DImode)
1100         {
1101           *total = COSTS_N_INSNS (4);
1102           return true;
1103         }
1104       return false;
1105
1106     case MULT:
1107       *total = optimize_size ? COSTS_N_INSNS (2) : COSTS_N_INSNS (12);
1108       return true;
1109
1110     case DIV:
1111     case MOD:
1112     case UDIV:
1113     case UMOD:
1114       *total = optimize_size ? COSTS_N_INSNS (2) : COSTS_N_INSNS (33);
1115       return true;
1116
1117     case SIGN_EXTEND:
1118     case ZERO_EXTEND:
1119       switch (GET_MODE (XEXP (x, 0)))
1120         {
1121         case QImode:
1122         case HImode:
1123           if (GET_CODE (XEXP (x, 0)) == MEM)
1124             {
1125               *total = COSTS_N_INSNS (2);
1126
1127               if (!TARGET_LITTLE_ENDIAN &&
1128                   side_effects_p (XEXP (XEXP (x, 0), 0)))
1129                 *total = 100;
1130             }
1131           else
1132             *total = COSTS_N_INSNS (1);
1133           break;
1134
1135         default:
1136           *total = COSTS_N_INSNS (1);
1137           break;
1138         }
1139       return true;
1140
1141     default:
1142       return false;
1143     }
1144 }
1145
1146 /* Implement TARGET_ADDRESS_COST macro.  */
1147 int
1148 score7_address_cost (rtx addr)
1149 {
1150   return score7_address_insns (addr, SImode);
1151 }
1152
1153 /* Implement ASM_OUTPUT_EXTERNAL macro.  */
1154 int
1155 score7_output_external (FILE *file ATTRIBUTE_UNUSED,
1156                         tree decl, const char *name)
1157 {
1158   register struct extern_list *p;
1159
1160   if (score7_in_small_data_p (decl))
1161     {
1162       p = (struct extern_list *) ggc_alloc (sizeof (struct extern_list));
1163       p->next = extern_head;
1164       p->name = name;
1165       p->size = int_size_in_bytes (TREE_TYPE (decl));
1166       extern_head = p;
1167     }
1168   return 0;
1169 }
1170
1171 /* Implement RETURN_ADDR_RTX.  Note, we do not support moving
1172    back to a previous frame.  */
1173 rtx
1174 score7_return_addr (int count, rtx frame ATTRIBUTE_UNUSED)
1175 {
1176   if (count != 0)
1177     return const0_rtx;
1178   return get_hard_reg_initial_val (Pmode, RA_REGNUM);
1179 }
1180
1181 /* Implement PRINT_OPERAND macro.  */
1182 /* Score-specific operand codes:
1183    '['        print .set nor1 directive
1184    ']'        print .set r1 directive
1185    'U'        print hi part of a CONST_INT rtx
1186    'E'        print log2(v)
1187    'F'        print log2(~v)
1188    'D'        print SFmode const double
1189    'S'        selectively print "!" if operand is 15bit instruction accessible
1190    'V'        print "v!" if operand is 15bit instruction accessible, or "lfh!"
1191    'L'        low  part of DImode reg operand
1192    'H'        high part of DImode reg operand
1193    'C'        print part of opcode for a branch condition.  */
1194 void
1195 score7_print_operand (FILE *file, rtx op, int c)
1196 {
1197   enum rtx_code code = -1;
1198   if (!PRINT_OPERAND_PUNCT_VALID_P (c))
1199     code = GET_CODE (op);
1200
1201   if (c == '[')
1202     {
1203       fprintf (file, ".set r1\n");
1204     }
1205   else if (c == ']')
1206     {
1207       fprintf (file, "\n\t.set nor1");
1208     }
1209   else if (c == 'U')
1210     {
1211       gcc_assert (code == CONST_INT);
1212       fprintf (file, HOST_WIDE_INT_PRINT_HEX,
1213                (INTVAL (op) >> 16) & 0xffff);
1214     }
1215   else if (c == 'D')
1216     {
1217       if (GET_CODE (op) == CONST_DOUBLE)
1218         {
1219           rtx temp = gen_lowpart (SImode, op);
1220           gcc_assert (GET_MODE (op) == SFmode);
1221           fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (temp) & 0xffffffff);
1222         }
1223       else
1224         output_addr_const (file, op);
1225     }
1226   else if (c == 'S')
1227     {
1228       gcc_assert (code == REG);
1229       if (G16_REG_P (REGNO (op)))
1230         fprintf (file, "!");
1231     }
1232   else if (c == 'V')
1233     {
1234       gcc_assert (code == REG);
1235       fprintf (file, G16_REG_P (REGNO (op)) ? "v!" : "lfh!");
1236     }
1237   else if (c == 'C')
1238     {
1239       enum machine_mode mode = GET_MODE (XEXP (op, 0));
1240
1241       switch (code)
1242         {
1243         case EQ: fputs ("eq", file); break;
1244         case NE: fputs ("ne", file); break;
1245         case GT: fputs ("gt", file); break;
1246         case GE: fputs (mode != CCmode ? "pl" : "ge", file); break;
1247         case LT: fputs (mode != CCmode ? "mi" : "lt", file); break;
1248         case LE: fputs ("le", file); break;
1249         case GTU: fputs ("gtu", file); break;
1250         case GEU: fputs ("cs", file); break;
1251         case LTU: fputs ("cc", file); break;
1252         case LEU: fputs ("leu", file); break;
1253         default:
1254           output_operand_lossage ("invalid operand for code: '%c'", code);
1255         }
1256     }
1257   else if (c == 'E')
1258     {
1259       unsigned HOST_WIDE_INT i;
1260       unsigned HOST_WIDE_INT pow2mask = 1;
1261       unsigned HOST_WIDE_INT val;
1262
1263       val = INTVAL (op);
1264       for (i = 0; i < 32; i++)
1265         {
1266           if (val == pow2mask)
1267             break;
1268           pow2mask <<= 1;
1269         }
1270       gcc_assert (i < 32);
1271       fprintf (file, HOST_WIDE_INT_PRINT_HEX, i);
1272     }
1273   else if (c == 'F')
1274     {
1275       unsigned HOST_WIDE_INT i;
1276       unsigned HOST_WIDE_INT pow2mask = 1;
1277       unsigned HOST_WIDE_INT val;
1278
1279       val = ~INTVAL (op);
1280       for (i = 0; i < 32; i++)
1281         {
1282           if (val == pow2mask)
1283             break;
1284           pow2mask <<= 1;
1285         }
1286       gcc_assert (i < 32);
1287       fprintf (file, HOST_WIDE_INT_PRINT_HEX, i);
1288     }
1289   else if (code == REG)
1290     {
1291       int regnum = REGNO (op);
1292       if ((c == 'H' && !WORDS_BIG_ENDIAN)
1293           || (c == 'L' && WORDS_BIG_ENDIAN))
1294         regnum ++;
1295       fprintf (file, "%s", reg_names[regnum]);
1296     }
1297   else
1298     {
1299       switch (code)
1300         {
1301         case MEM:
1302           score7_print_operand_address (file, op);
1303           break;
1304         default:
1305           output_addr_const (file, op);
1306         }
1307     }
1308 }
1309
1310 /* Implement PRINT_OPERAND_ADDRESS macro.  */
1311 void
1312 score7_print_operand_address (FILE *file, rtx x)
1313 {
1314   struct score7_address_info addr;
1315   enum rtx_code code = GET_CODE (x);
1316   enum machine_mode mode = GET_MODE (x);
1317
1318   if (code == MEM)
1319     x = XEXP (x, 0);
1320
1321   if (score7_classify_address (&addr, mode, x, true))
1322     {
1323       switch (addr.type)
1324         {
1325         case SCORE7_ADD_REG:
1326           {
1327             switch (addr.code)
1328               {
1329               case PRE_DEC:
1330                 fprintf (file, "[%s,-%ld]+", reg_names[REGNO (addr.reg)],
1331                          INTVAL (addr.offset));
1332                 break;
1333               case POST_DEC:
1334                 fprintf (file, "[%s]+,-%ld", reg_names[REGNO (addr.reg)],
1335                          INTVAL (addr.offset));
1336                 break;
1337               case PRE_INC:
1338                 fprintf (file, "[%s, %ld]+", reg_names[REGNO (addr.reg)],
1339                          INTVAL (addr.offset));
1340                 break;
1341               case POST_INC:
1342                 fprintf (file, "[%s]+, %ld", reg_names[REGNO (addr.reg)],
1343                          INTVAL (addr.offset));
1344                 break;
1345               default:
1346                 if (INTVAL(addr.offset) == 0)
1347                   fprintf(file, "[%s]", reg_names[REGNO (addr.reg)]);
1348                 else
1349                   fprintf(file, "[%s, %ld]", reg_names[REGNO (addr.reg)],
1350                           INTVAL(addr.offset));
1351                 break;
1352               }
1353           }
1354           return;
1355         case SCORE7_ADD_CONST_INT:
1356         case SCORE7_ADD_SYMBOLIC:
1357           output_addr_const (file, x);
1358           return;
1359         }
1360     }
1361   print_rtl (stderr, x);
1362   gcc_unreachable ();
1363 }
1364
1365 /* Implement SELECT_CC_MODE macro.  */
1366 enum machine_mode
1367 score7_select_cc_mode (enum rtx_code op, rtx x, rtx y)
1368 {
1369   if ((op == EQ || op == NE || op == LT || op == GE)
1370       && y == const0_rtx
1371       && GET_MODE (x) == SImode)
1372     {
1373       switch (GET_CODE (x))
1374         {
1375         case PLUS:
1376         case MINUS:
1377         case NEG:
1378         case AND:
1379         case IOR:
1380         case XOR:
1381         case NOT:
1382         case ASHIFT:
1383         case LSHIFTRT:
1384         case ASHIFTRT:
1385           return CC_NZmode;
1386
1387         case SIGN_EXTEND:
1388         case ZERO_EXTEND:
1389         case ROTATE:
1390         case ROTATERT:
1391           return (op == LT || op == GE) ? CC_Nmode : CCmode;
1392
1393         default:
1394           return CCmode;
1395         }
1396     }
1397
1398   if ((op == EQ || op == NE)
1399       && (GET_CODE (y) == NEG)
1400       && register_operand (XEXP (y, 0), SImode)
1401       && register_operand (x, SImode))
1402     {
1403       return CC_NZmode;
1404     }
1405
1406   return CCmode;
1407 }
1408
1409 /* Generate the prologue instructions for entry into a S+core function.  */
1410 void
1411 score7_prologue (void)
1412 {
1413 #define EMIT_PL(_rtx)        RTX_FRAME_RELATED_P (_rtx) = 1
1414
1415   struct score7_frame_info *f = score7_compute_frame_size (get_frame_size ());
1416   HOST_WIDE_INT size;
1417   int regno;
1418
1419   size = f->total_size - f->gp_reg_size;
1420
1421   if (flag_pic)
1422     emit_insn (gen_cpload_score7 ());
1423
1424   for (regno = (int) GP_REG_LAST; regno >= (int) GP_REG_FIRST; regno--)
1425     {
1426       if (BITSET_P (f->mask, regno - GP_REG_FIRST))
1427         {
1428           rtx mem = gen_rtx_MEM (SImode,
1429                                  gen_rtx_PRE_DEC (SImode, stack_pointer_rtx));
1430           rtx reg = gen_rtx_REG (SImode, regno);
1431           if (!current_function_calls_eh_return)
1432             MEM_READONLY_P (mem) = 1;
1433           EMIT_PL (emit_insn (gen_pushsi_score7 (mem, reg)));
1434         }
1435     }
1436
1437   if (size > 0)
1438     {
1439       rtx insn;
1440
1441       if (CONST_OK_FOR_LETTER_P (-size, 'L'))
1442         EMIT_PL (emit_insn (gen_add3_insn (stack_pointer_rtx,
1443                                            stack_pointer_rtx,
1444                                            GEN_INT (-size))));
1445       else
1446         {
1447           EMIT_PL (emit_move_insn (gen_rtx_REG (Pmode, SCORE7_PROLOGUE_TEMP_REGNUM),
1448                                    GEN_INT (size)));
1449           EMIT_PL (emit_insn
1450                    (gen_sub3_insn (stack_pointer_rtx,
1451                                    stack_pointer_rtx,
1452                                    gen_rtx_REG (Pmode,
1453                                                 SCORE7_PROLOGUE_TEMP_REGNUM))));
1454         }
1455       insn = get_last_insn ();
1456       REG_NOTES (insn) =
1457         alloc_EXPR_LIST (REG_FRAME_RELATED_EXPR,
1458                          gen_rtx_SET (VOIDmode, stack_pointer_rtx,
1459                                       plus_constant (stack_pointer_rtx,
1460                                                      -size)),
1461                                       REG_NOTES (insn));
1462     }
1463
1464   if (frame_pointer_needed)
1465     EMIT_PL (emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx));
1466
1467   if (flag_pic && f->cprestore_size)
1468     {
1469       if (frame_pointer_needed)
1470         emit_insn (gen_cprestore_use_fp_score7 (GEN_INT (size - f->cprestore_size)));
1471       else
1472         emit_insn (gen_cprestore_use_sp_score7 (GEN_INT (size - f->cprestore_size)));
1473     }
1474
1475 #undef EMIT_PL
1476 }
1477
1478 /* Generate the epilogue instructions in a S+core function.  */
1479 void
1480 score7_epilogue (int sibcall_p)
1481 {
1482   struct score7_frame_info *f = score7_compute_frame_size (get_frame_size ());
1483   HOST_WIDE_INT size;
1484   int regno;
1485   rtx base;
1486
1487   size = f->total_size - f->gp_reg_size;
1488
1489   if (!frame_pointer_needed)
1490     base = stack_pointer_rtx;
1491   else
1492     base = hard_frame_pointer_rtx;
1493
1494   if (size)
1495     {
1496       if (CONST_OK_FOR_LETTER_P (size, 'L'))
1497         emit_insn (gen_add3_insn (base, base, GEN_INT (size)));
1498       else
1499         {
1500           emit_move_insn (gen_rtx_REG (Pmode, SCORE7_EPILOGUE_TEMP_REGNUM),
1501                           GEN_INT (size));
1502           emit_insn (gen_add3_insn (base, base,
1503                                     gen_rtx_REG (Pmode,
1504                                                  SCORE7_EPILOGUE_TEMP_REGNUM)));
1505         }
1506     }
1507
1508   if (base != stack_pointer_rtx)
1509     emit_move_insn (stack_pointer_rtx, base);
1510
1511   if (current_function_calls_eh_return)
1512     emit_insn (gen_add3_insn (stack_pointer_rtx,
1513                               stack_pointer_rtx,
1514                               EH_RETURN_STACKADJ_RTX));
1515
1516   for (regno = (int) GP_REG_FIRST; regno <= (int) GP_REG_LAST; regno++)
1517     {
1518       if (BITSET_P (f->mask, regno - GP_REG_FIRST))
1519         {
1520           rtx mem = gen_rtx_MEM (SImode,
1521                                  gen_rtx_POST_INC (SImode, stack_pointer_rtx));
1522           rtx reg = gen_rtx_REG (SImode, regno);
1523
1524           if (!current_function_calls_eh_return)
1525             MEM_READONLY_P (mem) = 1;
1526
1527           emit_insn (gen_popsi_score7 (reg, mem));
1528         }
1529     }
1530
1531   if (!sibcall_p)
1532     emit_jump_insn (gen_return_internal_score7 (gen_rtx_REG (Pmode, RA_REGNUM)));
1533 }
1534
1535 void
1536 score7_gen_cmp (enum machine_mode mode)
1537 {
1538   emit_insn (gen_rtx_SET (VOIDmode, gen_rtx_REG (mode, CC_REGNUM),
1539                           gen_rtx_COMPARE (mode, cmp_op0, cmp_op1)));
1540 }
1541
1542 /* Return true if X is a symbolic constant that can be calculated in
1543    the same way as a bare symbol.  If it is, store the type of the
1544    symbol in *SYMBOL_TYPE.  */
1545 int
1546 score7_symbolic_constant_p (rtx x, enum score_symbol_type *symbol_type)
1547 {
1548   HOST_WIDE_INT offset;
1549
1550   score7_split_const (x, &x, &offset);
1551   if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF)
1552     *symbol_type = score7_classify_symbol (x);
1553   else
1554     return 0;
1555
1556   if (offset == 0)
1557     return 1;
1558
1559   /* if offset > 15bit, must reload  */
1560   if (!IMM_IN_RANGE (offset, 15, 1))
1561     return 0;
1562
1563   switch (*symbol_type)
1564     {
1565     case SYMBOL_GENERAL:
1566       return 1;
1567     case SYMBOL_SMALL_DATA:
1568       return score7_offset_within_object_p (x, offset);
1569     }
1570   gcc_unreachable ();
1571 }
1572
1573 void
1574 score7_movsicc (rtx *ops)
1575 {
1576   enum machine_mode mode;
1577
1578   mode = score7_select_cc_mode (GET_CODE (ops[1]), ops[2], ops[3]);
1579   emit_insn (gen_rtx_SET (VOIDmode, gen_rtx_REG (mode, CC_REGNUM),
1580                           gen_rtx_COMPARE (mode, cmp_op0, cmp_op1)));
1581 }
1582
1583 /* Call and sibcall pattern all need call this function.  */
1584 void
1585 score7_call (rtx *ops, bool sib)
1586 {
1587   rtx addr = XEXP (ops[0], 0);
1588   if (!call_insn_operand (addr, VOIDmode))
1589     {
1590       rtx oaddr = addr;
1591       addr = gen_reg_rtx (Pmode);
1592       gen_move_insn (addr, oaddr);
1593     }
1594
1595   if (sib)
1596     emit_call_insn (gen_sibcall_internal_score7 (addr, ops[1]));
1597   else
1598     emit_call_insn (gen_call_internal_score7 (addr, ops[1]));
1599 }
1600
1601 /* Call value and sibcall value pattern all need call this function.  */
1602 void
1603 score7_call_value (rtx *ops, bool sib)
1604 {
1605   rtx result = ops[0];
1606   rtx addr = XEXP (ops[1], 0);
1607   rtx arg = ops[2];
1608
1609   if (!call_insn_operand (addr, VOIDmode))
1610     {
1611       rtx oaddr = addr;
1612       addr = gen_reg_rtx (Pmode);
1613       gen_move_insn (addr, oaddr);
1614     }
1615
1616   if (sib)
1617     emit_call_insn (gen_sibcall_value_internal_score7 (result, addr, arg));
1618   else
1619     emit_call_insn (gen_call_value_internal_score7 (result, addr, arg));
1620 }
1621
1622 /* Machine Split  */
1623 void
1624 score7_movdi (rtx *ops)
1625 {
1626   rtx dst = ops[0];
1627   rtx src = ops[1];
1628   rtx dst0 = score7_subw (dst, 0);
1629   rtx dst1 = score7_subw (dst, 1);
1630   rtx src0 = score7_subw (src, 0);
1631   rtx src1 = score7_subw (src, 1);
1632
1633   if (GET_CODE (dst0) == REG && reg_overlap_mentioned_p (dst0, src))
1634     {
1635       emit_move_insn (dst1, src1);
1636       emit_move_insn (dst0, src0);
1637     }
1638   else
1639     {
1640       emit_move_insn (dst0, src0);
1641       emit_move_insn (dst1, src1);
1642     }
1643 }
1644
1645 void
1646 score7_zero_extract_andi (rtx *ops)
1647 {
1648   if (INTVAL (ops[1]) == 1 && const_uimm5 (ops[2], SImode))
1649     emit_insn (gen_zero_extract_bittst_score7 (ops[0], ops[2]));
1650   else
1651     {
1652       unsigned HOST_WIDE_INT mask;
1653       mask = (0xffffffffU & ((1U << INTVAL (ops[1])) - 1U));
1654       mask = mask << INTVAL (ops[2]);
1655       emit_insn (gen_andsi3_cmp_score7 (ops[3], ops[0],
1656                                  gen_int_mode (mask, SImode)));
1657     }
1658 }
1659
1660 /* Check addr could be present as PRE/POST mode.  */
1661 static bool
1662 score7_pindex_mem (rtx addr)
1663 {
1664   if (GET_CODE (addr) == MEM)
1665     {
1666       switch (GET_CODE (XEXP (addr, 0)))
1667         {
1668         case PRE_DEC:
1669         case POST_DEC:
1670         case PRE_INC:
1671         case POST_INC:
1672           return true;
1673         default:
1674           break;
1675         }
1676     }
1677   return false;
1678 }
1679
1680 /* Output asm code for ld/sw insn.  */
1681 static int
1682 score7_pr_addr_post (rtx *ops, int idata, int iaddr, char *ip, enum score_mem_unit unit)
1683 {
1684   struct score7_address_info ai;
1685
1686   gcc_assert (GET_CODE (ops[idata]) == REG);
1687   gcc_assert (score7_classify_address (&ai, SImode, XEXP (ops[iaddr], 0), true));
1688
1689   if (!score7_pindex_mem (ops[iaddr])
1690       && ai.type == SCORE7_ADD_REG
1691       && GET_CODE (ai.offset) == CONST_INT
1692       && G16_REG_P (REGNO (ops[idata]))
1693       && G16_REG_P (REGNO (ai.reg)))
1694     {
1695       if (INTVAL (ai.offset) == 0)
1696         {
1697           ops[iaddr] = ai.reg;
1698           return snprintf (ip, INS_BUF_SZ,
1699                            "!\t%%%d, [%%%d]", idata, iaddr);
1700         }
1701       if (REGNO (ai.reg) == HARD_FRAME_POINTER_REGNUM)
1702         {
1703           HOST_WIDE_INT offset = INTVAL (ai.offset);
1704           if (SCORE_ALIGN_UNIT (offset, unit)
1705               && CONST_OK_FOR_LETTER_P (offset >> unit, 'J'))
1706             {
1707               ops[iaddr] = ai.offset;
1708               return snprintf (ip, INS_BUF_SZ,
1709                                "p!\t%%%d, %%c%d", idata, iaddr);
1710             }
1711         }
1712     }
1713   return snprintf (ip, INS_BUF_SZ, "\t%%%d, %%a%d", idata, iaddr);
1714 }
1715
1716 /* Output asm insn for load.  */
1717 const char *
1718 score7_linsn (rtx *ops, enum score_mem_unit unit, bool sign)
1719 {
1720   const char *pre_ins[] =
1721     {"lbu", "lhu", "lw", "??", "lb", "lh", "lw", "??"};
1722   char *ip;
1723
1724   strcpy (score7_ins, pre_ins[(sign ? 4 : 0) + unit]);
1725   ip = score7_ins + strlen (score7_ins);
1726
1727   if ((!sign && unit != SCORE_HWORD)
1728       || (sign && unit != SCORE_BYTE))
1729     score7_pr_addr_post (ops, 0, 1, ip, unit);
1730   else
1731     snprintf (ip, INS_BUF_SZ, "\t%%0, %%a1");
1732
1733   return score7_ins;
1734 }
1735
1736 /* Output asm insn for store.  */
1737 const char *
1738 score7_sinsn (rtx *ops, enum score_mem_unit unit)
1739 {
1740   const char *pre_ins[] = {"sb", "sh", "sw"};
1741   char *ip;
1742
1743   strcpy (score7_ins, pre_ins[unit]);
1744   ip = score7_ins + strlen (score7_ins);
1745   score7_pr_addr_post (ops, 1, 0, ip, unit);
1746   return score7_ins;
1747 }
1748
1749 /* Output asm insn for load immediate.  */
1750 const char *
1751 score7_limm (rtx *ops)
1752 {
1753   HOST_WIDE_INT v;
1754
1755   gcc_assert (GET_CODE (ops[0]) == REG);
1756   gcc_assert (GET_CODE (ops[1]) == CONST_INT);
1757
1758   v = INTVAL (ops[1]);
1759   if (G16_REG_P (REGNO (ops[0])) && IMM_IN_RANGE (v, 8, 0))
1760     return "ldiu!\t%0, %c1";
1761   else if (IMM_IN_RANGE (v, 16, 1))
1762     return "ldi\t%0, %c1";
1763   else if ((v & 0xffff) == 0)
1764     return "ldis\t%0, %U1";
1765   else
1766     return "li\t%0, %c1";
1767 }
1768
1769 /* Output asm insn for move.  */
1770 const char *
1771 score7_move (rtx *ops)
1772 {
1773   gcc_assert (GET_CODE (ops[0]) == REG);
1774   gcc_assert (GET_CODE (ops[1]) == REG);
1775
1776   if (G16_REG_P (REGNO (ops[0])))
1777     {
1778       if (G16_REG_P (REGNO (ops[1])))
1779         return "mv!\t%0, %1";
1780       else
1781         return "mlfh!\t%0, %1";
1782     }
1783   else if (G16_REG_P (REGNO (ops[1])))
1784     return "mhfl!\t%0, %1";
1785   else
1786     return "mv\t%0, %1";
1787 }
1788
1789 /* Generate add insn.  */
1790 const char *
1791 score7_select_add_imm (rtx *ops, bool set_cc)
1792 {
1793   HOST_WIDE_INT v = INTVAL (ops[2]);
1794
1795   gcc_assert (GET_CODE (ops[2]) == CONST_INT);
1796   gcc_assert (REGNO (ops[0]) == REGNO (ops[1]));
1797
1798   if (set_cc && G16_REG_P (REGNO (ops[0])))
1799     {
1800       if (v > 0 && IMM_IS_POW_OF_2 ((unsigned HOST_WIDE_INT) v, 0, 15))
1801         {
1802           ops[2] = GEN_INT (ffs (v) - 1);
1803           return "addei!\t%0, %c2";
1804         }
1805
1806       if (v < 0 && IMM_IS_POW_OF_2 ((unsigned HOST_WIDE_INT) (-v), 0, 15))
1807         {
1808           ops[2] = GEN_INT (ffs (-v) - 1);
1809           return "subei!\t%0, %c2";
1810         }
1811     }
1812
1813   if (set_cc)
1814     return "addi.c\t%0, %c2";
1815   else
1816     return "addi\t%0, %c2";
1817 }
1818
1819 /* Output arith insn.  */
1820 const char *
1821 score7_select (rtx *ops, const char *inst_pre,
1822                bool commu, const char *letter, bool set_cc)
1823 {
1824   gcc_assert (GET_CODE (ops[0]) == REG);
1825   gcc_assert (GET_CODE (ops[1]) == REG);
1826
1827   if (set_cc && G16_REG_P (REGNO (ops[0]))
1828       && (GET_CODE (ops[2]) == REG ? G16_REG_P (REGNO (ops[2])) : 1)
1829       && REGNO (ops[0]) == REGNO (ops[1]))
1830     {
1831       snprintf (score7_ins, INS_BUF_SZ, "%s!\t%%0, %%%s2", inst_pre, letter);
1832       return score7_ins;
1833     }
1834
1835   if (commu && set_cc && G16_REG_P (REGNO (ops[0]))
1836       && G16_REG_P (REGNO (ops[1]))
1837       && REGNO (ops[0]) == REGNO (ops[2]))
1838     {
1839       gcc_assert (GET_CODE (ops[2]) == REG);
1840       snprintf (score7_ins, INS_BUF_SZ, "%s!\t%%0, %%%s1", inst_pre, letter);
1841       return score7_ins;
1842     }
1843
1844   if (set_cc)
1845     snprintf (score7_ins, INS_BUF_SZ, "%s.c\t%%0, %%1, %%%s2", inst_pre, letter);
1846   else
1847     snprintf (score7_ins, INS_BUF_SZ, "%s\t%%0, %%1, %%%s2", inst_pre, letter);
1848   return score7_ins;
1849 }
1850