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