OSDN Git Service

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